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

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

このコミットは、Goコンパイラ(cmd/gc、特に6g5g8gといったアーキテクチャ固有のコンパイラ)において、mapslicestringlen(長さ)およびcap(容量)の型がint32ではなくintとして扱われるように準備するための変更です。これは、将来的にGoのint型が64ビット整数として扱われるようになるための基盤を築くことを目的としています。

コミット

commit 650160e36a2f8d205c1a9ecfcf8f5611c1af3de3
Author: Russ Cox <rsc@golang.org>
Date:   Mon Sep 24 14:59:44 2012 -0400

    cmd/gc: prepare for 64-bit ints
    
    This CL makes the compiler understand that the type of
    the len or cap of a map, slice, or string is 'int', not 'int32'.
    It does not change the meaning of int, but it should make
    the eventual change of the meaning of int in 6g a bit smoother.
    
    Update #2188.
    
    R=ken, dave, remyoudompheng
    CC=golang-dev
    https://golang.org/cl/6542059

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

https://github.com/golang/go/commit/650160e36a2f8d205c1a9ecfcf8f5611c1af3de3

元コミット内容

cmd/gc: prepare for 64-bit ints

This CL makes the compiler understand that the type of
the len or cap of a map, slice, or string is 'int', not 'int32'.
It does not change the meaning of int, but it should make
the eventual change of the meaning of int in 6g a bit smoother.

Update #2188.

R=ken, dave, remyoudompheng
CC=golang-dev
https://golang.org/cl/6542059

変更の背景

この変更の主な背景は、Go言語のint型が将来的に64ビットアーキテクチャ上で64ビット幅を持つようになることへの準備です。Go 1のリリース当初、int型はプラットフォームに依存するサイズ(32ビットまたは64ビット)を持っていましたが、lencapといった組み込み関数の戻り値は、内部的には32ビット整数として扱われることがありました。

コミットメッセージにあるUpdate #2188は、Goのintのサイズと最大配列サイズ(2GB要素)に関する議論を指している可能性が高いです。これは、intが32ビット幅の場合、2^31-1(約20億)を超える要素を持つ配列やスライスを扱うことができないという制約につながります。64ビットシステムでintが64ビット幅になることで、この制約が緩和され、より大きなデータ構造を効率的に扱えるようになります。

このコミットは、intの実際のサイズを変更するものではありませんが、コンパイラがlencapの戻り値をint32ではなく、より汎用的なint型として認識するようにすることで、将来の64ビットintへの移行をスムーズにするための前段階の作業です。これにより、コンパイラ内部の型推論やコード生成ロジックが、intのサイズ変更に柔軟に対応できるようになります。

前提知識の解説

  • Goのint: Go言語のint型は、その実行環境のCPUアーキテクチャに依存する符号付き整数型です。32ビットシステムでは32ビット幅、64ビットシステムでは64ビット幅を持ちます。これは、C言語のint型と同様の特性です。
  • int32: int32は、Go言語において常に32ビット幅を持つ符号付き整数型です。int型とは異なり、プラットフォームに依存しません。
  • lencap:
    • len(): スライス、配列、文字列、マップ、チャネルの長さを返します。
    • cap(): スライス、配列、チャネルの容量を返します。 これらの組み込み関数は、Goのデータ構造のサイズや容量を動的に取得するために不可欠です。
  • widthptr: ポインタの幅(サイズ)を示す定数です。32ビットシステムでは4バイト、64ビットシステムでは8バイトになります。
  • widthint: int型の幅(サイズ)を示す定数です。このコミットで導入または調整され、intの実際のサイズをコンパイラが認識するために使用されます。
  • Goコンパイラ (gc, 5g, 6g, 8g):
    • gc: Goコンパイラの総称です。
    • 5g: ARMアーキテクチャ向けのGoコンパイラ。
    • 6g: AMD64 (x86-64) アーキテクチャ向けのGoコンパイラ。
    • 8g: x86 (32ビット) アーキテクチャ向けのGoコンパイラ。 これらのコンパイラは、Goのソースコードを各アーキテクチャの機械語に変換する役割を担っています。このコミットでは、特に6g(64ビットアーキテクチャ)でのintの将来的な変更に焦点を当てています。

技術的詳細

このコミットの技術的な核心は、Goコンパイラの内部でlencapの戻り値の型をTINT32(32ビット整数)からsimtype[TINT]int型のシミュレートされた型)へと変更することにあります。

具体的には、以下の変更が行われています。

  1. widthintの導入と利用:

    • src/cmd/gc/go.hEXTERN int widthint;が追加され、widthintというグローバル変数が宣言されました。これはint型のサイズを保持します。
    • src/cmd/5g/galign.csrc/cmd/6g/galign.csrc/cmd/8g/galign.cbetyeinit関数内で、widthint = 4;が設定されています。これは、現時点ではintが32ビット幅であることを示していますが、将来的に64ビットシステムではwidthint = 8;に変更されることを想定しています。
    • src/cmd/gc/align.cでは、Array_nelArray_capsizeof_Arraysizeof_Stringといった配列や文字列の内部構造のサイズ計算において、types[TUINT32]->widthの代わりにwidthintが使用されるようになりました。これにより、intのサイズが変更されても、これらの構造体のオフセット計算が自動的に調整されるようになります。
  2. TINT32からsimtype[TINT]への型変更:

    • src/cmd/6g/cgen.csrc/cmd/6g/gobj.csrc/cmd/6g/gsubr.csrc/cmd/gc/gen.csrc/cmd/gc/obj.csrc/cmd/gc/reflect.csrc/cmd/gc/sinit.csrc/cmd/gc/walk.cなど、多くのファイルでTINT32またはTUINT32と明示的に指定されていた型が、simtype[TINT]またはsimtype[TUINT]に変更されています。
    • simtypeは、Goコンパイラ内部で型を抽象的に扱うためのメカニズムです。simtype[TINT]を使用することで、コンパイラはintの実際のサイズ(32ビットか64ビットか)を意識せずに、より汎用的なコードを生成できるようになります。
    • 特に、OLENlen演算子)やOCAPcap演算子)のコード生成部分で、戻り値の型がtypes[TINT32]からtypes[simtype[TINT]]に変更されています。これにより、lencapが返す値が、プラットフォームのintのサイズに適切に対応できるようになります。
  3. Addr構造体のwidthフィールドの型変更:

    • src/cmd/6g/gg.hにおいて、struct Addr内のwidthフィールドの型がintからint64に変更されました。これは、アドレスの幅が64ビットになる可能性を考慮したものです。

これらの変更は、コンパイラがintのサイズをハードコードするのではなく、widthintsimtype[TINT]といった抽象的なメカニズムを通じて動的に決定できるようにすることで、将来のGo言語の進化に対応するための柔軟性を提供します。

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

このコミットで特に重要な変更箇所は以下の通りです。

  1. src/cmd/6g/cgen.c: OLENおよびOCAPのコード生成部分で、mapslicestringchanの長さや容量を扱う際の型がtypes[TINT32]からtypes[simtype[TINT]]またはtypes[simtype[TUINT]]に変更されています。また、OCAPにおけるオフセット計算で4widthintに置き換えられています。

    --- a/src/cmd/6g/cgen.c
    +++ b/src/cmd/6g/cgen.c
    @@ -309,7 +309,7 @@ cgen(Node *n, Node *res)
     
     	case OLEN:
     		if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
    -			// map and chan have len in the first 32-bit word.
    +			// map and chan have len in the first int-sized word.
     			// a zero pointer means zero length
     			regalloc(&n1, types[tptr], res);
     			cgen(nl, &n1);
    @@ -320,7 +320,7 @@ cgen(Node *n, Node *res)
     
     			n2 = n1;
     			n2.op = OINDREG;
    -			n2.type = types[TINT32];
    +			n2.type = types[simtype[TINT]];
     			gmove(&n2, &n1);
     
     			patch(p1, pc);
    @@ -333,7 +333,7 @@ cgen(Node *n, Node *res)
     
     			// both slice and string have len one pointer into the struct.
     			// a zero pointer means zero length
     			igen(nl, &n1, res);
    -			n1.type = types[TUINT32];
    +			n1.type = types[simtype[TUINT]];
     			n1.xoffset += Array_nel;
     			gmove(&n1, res);
     			regfree(&n1);
    @@ -344,7 +344,7 @@ cgen(Node *n, Node *res)
     
     	case OCAP:
     		if(istype(nl->type, TCHAN)) {
    -			// chan has cap in the second 32-bit word.
    +			// chan has cap in the second int-sized word.
     			// a zero pointer means zero length
     			regalloc(&n1, types[tptr], res);
     			cgen(nl, &n1);
    @@ -355,8 +355,8 @@ cgen(Node *n, Node *res)
     
     			n2 = n1;
     			n2.op = OINDREG;
    -			n2.xoffset = 4;
    -			n2.type = types[TINT32];
    +			n2.xoffset = widthint;
    +			n2.type = types[simtype[TINT]];
     			gmove(&n2, &n1);
     
     			patch(p1, pc);
    @@ -367,7 +367,7 @@ cgen(Node *n, Node *res)
     		}
     		if(isslice(nl->type)) {
     			igen(nl, &n1, res);
    -			n1.type = types[TUINT32];
    +			n1.type = types[simtype[TUINT]];
     			n1.xoffset += Array_cap;
     			gmove(&n1, res);
     			regfree(&n1);
    
  2. src/cmd/gc/align.c: Array_nel, Array_cap, sizeof_Array, sizeof_Stringの計算でtypes[TUINT32]->widthwidthintに置き換えられています。

    --- a/src/cmd/gc/align.c
    +++ b/src/cmd/gc/align.c
    @@ -615,12 +615,12 @@ typeinit(void)\n     }\n     \n     Array_array = rnd(0, widthptr);\n    -    Array_nel = rnd(Array_array+widthptr, types[TUINT32]->width);\n    -    Array_cap = rnd(Array_nel+types[TUINT32]->width, types[TUINT32]->width);\n    -    sizeof_Array = rnd(Array_cap+types[TUINT32]->width, widthptr);\n    +    Array_nel = rnd(Array_array+widthptr, widthint);\n    +    Array_cap = rnd(Array_nel+widthint, widthint);\n    +    sizeof_Array = rnd(Array_cap+widthint, widthptr);\n     \n     // string is same as slice wo the cap\n    -    sizeof_String = rnd(Array_nel+types[TUINT32]->width, widthptr);\n    +    sizeof_String = rnd(Array_nel+widthint, widthptr);\n     \n     dowidth(types[TSTRING]);\n     dowidth(idealstring);
    
  3. src/cmd/gc/go.h: widthintの宣言が追加されています。

    --- a/src/cmd/gc/go.h
    +++ b/src/cmd/gc/go.h
    @@ -905,6 +905,7 @@ EXTERN	int	hasdefer;		// flag that curfn has defer statetment
     EXTERN	Node*	curfn;
     
     EXTERN	int	widthptr;
    +EXTERN	int	widthint;
     
     EXTERN	Node*	typesw;
     EXTERN	Node*	nblank;
    

コアとなるコードの解説

上記の変更は、Goコンパイラがlencapの戻り値の型をより柔軟に扱うための重要なステップです。

  • src/cmd/6g/cgen.cの変更:

    • OLENOCAPは、それぞれlen()cap()組み込み関数のコンパイラ内部表現です。
    • 以前は、これらの関数の戻り値の型をtypes[TINT32](32ビット符号付き整数)と明示的に指定していました。これは、当時のGoのintが32ビットシステムでは32ビット、64ビットシステムでも実質的に32ビットとして扱われることが多かったためです。
    • しかし、将来的にintが64ビットシステムで64ビット幅を持つようになることを考慮し、型をtypes[simtype[TINT]]またはtypes[simtype[TUINT]]に変更しました。simtypeは、Goコンパイラが型を抽象化して扱うための内部的なマッピングです。これにより、intの実際のサイズが変更されても、コンパイラのコード生成ロジックを大幅に変更することなく対応できるようになります。
    • OCAPにおけるn2.xoffset = 4;n2.xoffset = widthint;に変更されたのは、チャネルの容量が格納されているオフセットが、intのサイズに依存するためです。intが32ビットなら4バイト、64ビットなら8バイトとなるため、widthintを使用することで、このオフセットが動的に調整されるようになります。
  • src/cmd/gc/align.cの変更:

    • Array_nelArray_capsizeof_Arraysizeof_Stringは、Goのスライスや文字列の内部構造におけるフィールドのオフセットや全体のサイズを定義する定数です。
    • これらの計算にtypes[TUINT32]->width(32ビット符号なし整数の幅、つまり4バイト)が直接使われていました。
    • これをwidthintに置き換えることで、intのサイズが変更された場合でも、これらの構造体のメモリレイアウトが自動的に調整されるようになります。例えば、intが64ビットになればwidthintは8となり、Array_nelなどのオフセットも8バイト単位で計算されるようになります。これは、Goのデータ構造が将来の64ビットintにシームレスに対応するための重要な変更です。
  • src/cmd/gc/go.hの変更:

    • widthintの宣言は、この新しい抽象化されたintのサイズをコンパイラ全体で利用可能にするためのものです。これにより、各コンパイラバックエンド(5g, 6g, 8g)がそれぞれのアーキテクチャに合わせてwidthintの値を設定し、その値に基づいてコード生成やメモリレイアウトの計算を行うことができるようになります。

これらの変更は、Go言語の将来的な進化、特に64ビットシステムでのint型のフル活用に向けた、コンパイラ内部の重要な基盤整備と言えます。

関連リンク

参考にした情報源リンク