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

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

このコミットは、Go言語のCコンパイラ(cmd/cc)において、ワイド文字列定数で21ビットのルーン(rune)をサポートするための変更を導入します。これは、Unicodeのより広い範囲の文字を正確に表現し、処理するために必要となる重要な更新です。また、以前の暫定的な修正の巻き戻しと、Plan 9コンパイラからのコードの取り込みも含まれています。

コミット

commit 781b2a25190f163af848c04a71adfbbf7ce457e2
Author: Anthony Martin <ality@pbrane.org>
Date:   Tue Jun 4 16:30:55 2013 -0700

    cmd/cc: support 21-bit runes in wide string constants
    
    Changeset 7557a627e9b5 added a temporary stop-gap to silence
    a print format warning for %S. This has been reverted.
    
    None of this code is original. It was copied from the latest
    Plan 9 compilers.
    
    R=golang-dev, r, rsc
    CC=golang-dev
    https://golang.org/cl/8630044

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

https://github.com/golang/go/commit/781b2a25190f163af848c04a71adfbbf7ce457e2

元コミット内容

cmd/cc: support 21-bit runes in wide string constants

このコミットは、ワイド文字列定数における21ビットルーンのサポートを追加します。以前の変更セット 7557a627e9b5%S の出力フォーマット警告を抑制するための暫定的な修正が追加されましたが、これはこのコミットで元に戻されています。このコードは独自のものではなく、最新のPlan 9コンパイラからコピーされたものです。

変更の背景

Go言語はUnicodeをネイティブにサポートしており、文字列はUTF-8でエンコードされたバイト列として扱われます。Goの「ルーン(rune)」は、Unicodeのコードポイントを表すint32型のエイリアスです。UnicodeのコードポイントはU+0000からU+10FFFFまでの範囲にあり、最大で21ビットを必要とします。

従来のC言語のワイド文字(wchar_t)は、システムによってサイズが異なり、通常は16ビットまたは32ビットです。16ビットのワイド文字(ushortなど)では、U+FFFFを超えるUnicodeコードポイント(サロゲートペアなど)を単一の文字として直接表現することができませんでした。GoのCコンパイラがこれらのワイド文字列定数を正しく処理するためには、21ビットのルーンを適切に扱えるように内部表現と処理ロジックを更新する必要がありました。

この変更のもう一つの背景は、GoのツールチェインがPlan 9の思想とコードベースに深く根ざしていることです。Plan 9のコンパイラは、その設計思想と実装において、Goの初期のコンパイラに大きな影響を与えました。このコミットは、Plan 9コンパイラの最新の改善を取り込むことで、GoのCコンパイラの堅牢性とUnicodeサポートを強化することを目的としています。

前提知識の解説

  • ルーン (Rune): Go言語におけるUnicodeコードポイントを表す型です。int32のエイリアスであり、単一のUnicode文字を表現します。UTF-8エンコーディングでは1バイトから4バイトの可変長ですが、ルーン自体は固定長のコードポイントです。
  • ワイド文字列定数: C言語において、通常の文字列リテラル("abc")がcharの配列であるのに対し、ワイド文字列リテラル(L"abc")はwchar_tの配列として扱われます。GoのCコンパイラでは、Goのルーンの概念と結びついて、より広い文字セットを扱うための内部表現として使用されます。
  • Plan 9 コンパイラ: ベル研究所で開発されたオペレーティングシステムPlan 9のCコンパイラ群です。Go言語の初期のコンパイラ(gc)は、Plan 9のコンパイラ(8c, 6c, 5cなど)のコードベースをフォークして開発されました。そのため、GoのツールチェインにはPlan 9の設計思想やコードが色濃く残っています。
  • Bison (Yacc): GNU Bisonは、Yacc(Yet Another Compiler Compiler)互換のパーサジェネレータです。文法定義ファイル(.yまたは.yy)から、C言語などのソースコード(パーサ)を生成します。このコミットでは、Bisonのバージョンアップに伴う生成コードの変更がy.tab.cy.tab.hに現れています。
  • ushort: 通常16ビットの符号なし整数型です。多くのシステムでwchar_tが16ビットの場合、これにマッピングされます。しかし、Unicodeの全範囲をカバーするには不十分です。
  • 21ビットルーン: UnicodeのコードポイントはU+0000からU+10FFFFまで存在し、最大で21ビットが必要です。例えば、絵文字や一部の漢字などは16ビットでは表現できません。

技術的詳細

このコミットの主要な技術的変更点は、GoのCコンパイラが内部的にワイド文字列を表現するために使用する型を、従来のushortから、21ビットルーンを適切に格納できるTRune型へと変更したことです。

  1. TRune型の導入:

    • src/cmd/cc/cc.htypedef Rune TRune;が追加されました。RuneはGoの内部でUnicodeコードポイントを表すために使用される型であり、通常はint32に相当します。これにより、TRuneは少なくとも32ビットの幅を持つことが保証され、21ビットのルーンを問題なく格納できるようになります。
    • enum定義内でTRUNE = sizeof(TRune)==4? TUINT: TUSHORT,という行が追加されました。これは、TRuneのサイズが4バイト(32ビット)であればTUINT(符号なし整数型)として扱い、そうでなければTUSHORT(符号なし短整数型)として扱うことを示しています。これにより、ターゲットシステムにおけるTRuneの実際のサイズに応じて適切な型が選択されるようになります。
  2. ワイド文字列内部表現の変更:

    • struct Node内のrstringフィールドの型がushort*からTRune*に変更されました。Nodeはコンパイラの抽象構文木(AST)のノードを表す構造体であり、rstringはワイド文字列定数の値を保持します。この変更により、ASTレベルで21ビットルーンを直接扱えるようになります。
  3. パーサとレキサの更新:

    • src/cmd/cc/cc.y(Bisonの文法定義ファイル)とsrc/cmd/cc/lex.c(レキサのソースファイル)において、ワイド文字列定数(lstring)の処理がushortからTRuneに切り替わりました。これには、文字列の長さ計算(sizeof(ushort)からsizeof(TRune)へ)、メモリ割り当て、および値の格納方法の変更が含まれます。
    • 特にlex.cでは、文字定数や文字列定数をスキャンする際に、convvtox関数に渡す型がTUSHORTからTRUNEに変更されています。また、getnsc関数で空白文字をスキップするロジックにc >= Runeselfという条件が追加されており、これはUnicodeの非ASCII文字(ルーン)を正しく扱うための調整です。
  4. 出力処理の変更:

    • src/cmd/cc/pswt.coutlstring関数は、ワイド文字列をバイト列として出力する役割を担います。この関数もushort *sからTRune *sを受け取るように変更され、内部のバッファサイズとバイトオーダー処理がsizeof(TRune)に基づいて動的に行われるようになりました。これにより、TRuneが16ビットでも32ビットでも正しくバイト列に変換できるようになります。
    • src/cmd/cc/com.cでは、ワイド文字列のパディング処理において、ushortのゼロ値ではなくTRuneのゼロ値を使用するように変更されています。
    • src/cmd/cc/sub.cprtree1関数では、デバッグ出力のためにワイド文字列をフォーマットする際に、sizeof(TRune) == sizeof(Rune)の場合にのみ%Sフォーマットを使用し、そうでない場合は汎用的な"..."を出力するように変更されました。これは、%Sフォーマットが常にRune型(int32)を期待する可能性があるため、型が一致しない場合に誤った出力を避けるための安全策と考えられます。
  5. Bisonバージョンアップに伴う変更:

    • src/cmd/cc/y.tab.csrc/cmd/cc/y.tab.hは、Bisonパーサジェネレータによって自動生成されるファイルです。このコミットでは、Bisonのバージョンが2.3から2.7.12-4996に更新されたため、これらのファイルの内容が大幅に変更されています。これには、パーサの内部構造、エラー処理ロジック、マクロ定義などが含まれます。この変更自体はルーンのサポートとは直接関係ありませんが、ツールチェインの更新の一環として行われました。

これらの変更により、GoのCコンパイラは、Unicodeの全範囲をカバーする21ビットのルーンを含むワイド文字列定数を、より正確かつ効率的に処理できるようになりました。

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

  • src/cmd/cc/cc.h:

    --- a/src/cmd/cc/cc.h
    +++ b/src/cmd/cc/cc.h
    @@ -55,6 +55,8 @@ typedef	struct	Bits	Bits;
     typedef	struct	Dynimp	Dynimp;
     typedef	struct	Dynexp	Dynexp;
     
    +typedef	Rune	TRune;	/* target system type */
    +
     #define	BUFSIZ		8192
     #define	NSYMB		500
     #define	NHASH		1024
    @@ -85,7 +87,7 @@ struct	Node
     	double	fconst;		/* fp constant */
     	vlong	vconst;		/* non fp const */
     	char*	cstring;	/* character string */
    -	ushort*	rstring;	/* rune string */
    +	TRune*	rstring;	/* rune string */
     
     	Sym*	sym;
     	Type*	type;
    @@ -367,6 +369,9 @@ enum
     	TFILE,
     	TOLD,
     	NALLTYPES,
    +
    +	/* adapt size of Rune to target system's size */
    +	TRUNE = sizeof(TRune)==4? TUINT: TUSHORT,
     };
     enum
     {
    @@ -766,7 +771,7 @@ void	gclean(void);
     void	gextern(Sym*, Node*, int32, int32);
     void	ginit(void);
     int32	outstring(char*, int32);
    -int32	outlstring(ushort*, int32);
    +int32	outlstring(TRune*, int32);
     void	sextern(Sym*, Node*, int32, int32);
     void	xcom(Node*);
     int32	exreg(Type*);
    @@ -800,7 +805,6 @@ int	machcap(Node*);
     #pragma	varargck	type	"Q"	int32
     #pragma	varargck	type	"O"	int
     #pragma	varargck	type	"O"	uint
    -#pragma	varargck	type	"S"	ushort*
     #pragma	varargck	type	"T"	Type*
     #pragma	varargck	type	"U"	char*
     #pragma	varargck	type	"|"	int
    
  • src/cmd/cc/cc.y:

    --- a/src/cmd/cc/cc.y
    +++ b/src/cmd/cc/cc.y
    @@ -891,9 +891,9 @@ lstring:
     	LLSTRING
     	{
     		$$ = new(OLSTRING, Z, Z);
    -		$$->type = typ(TARRAY, types[TUSHORT]);
    -		$$->type->width = $1.l + sizeof(ushort);
    -		$$->rstring = (ushort*)$1.s;
    +		$$->type = typ(TARRAY, types[TRUNE]);
    +		$$->type->width = $1.l + sizeof(TRune);
    +		$$->rstring = (TRune*)$1.s;
     		$$->sym = symstring;
     		$$->etype = TARRAY;
     		$$->class = CSTATIC;
    @@ -903,16 +903,16 @@ lstring:
     		char *s;
     		int n;
     
    -		n = $1->type->width - sizeof(ushort);
    +		n = $1->type->width - sizeof(TRune);
     		s = alloc(n+$2.l+MAXALIGN);
     
     		memcpy(s, $1->rstring, n);
     		memcpy(s+n, $2.s, $2.l);
    -		*(ushort*)(s+n+$2.l) = 0;
    +		*(TRune*)(s+n+$2.l) = 0;
     
     		$$ = $1;
     		$$->type->width += $2.l;
    -		$$->rstring = (ushort*)s;
    +		$$->rstring = (TRune*)s;
     	}
     
     zelist:
    
  • src/cmd/cc/pswt.c:

    --- a/src/cmd/cc/pswt.c
    +++ b/src/cmd/cc/pswt.c
    @@ -102,28 +102,29 @@ newcase(void)
     }
     
     int32
    -outlstring(ushort *s, int32 n)
    +outlstring(TRune *s, int32 n)
     {
    -	char buf[2];
    -	int c;
    +	char buf[sizeof(TRune)];
    +	uint c;
    +	int i;
     	int32 r;
     
     	if(suppress)
     		return nstring;
    -	while(nstring & 1)
    +	while(nstring & (sizeof(TRune)-1))
     		outstring("", 1);
     	r = nstring;
     	while(n > 0) {
     		c = *s++;
     		if(align(0, types[TCHAR], Aarg1, nil)) {
    -			buf[0] = c>>8;
    -			buf[1] = c;
    +			for(i = 0; i < sizeof(TRune); i++)
    +				buf[i] = c>>(8*(sizeof(TRune) - i - 1));
     		} else {
    -			buf[0] = c;
    -			buf[1] = c>>8;
    +			for(i = 0; i < sizeof(TRune); i++)
    +				buf[i] = c>>(8*i);
     		}
    -		outstring(buf, 2);
    -		n -= sizeof(ushort);
    +		outstring(buf, sizeof(TRune));
    +		n -= sizeof(TRune);
     	}
     	return r;
     }
    

コアとなるコードの解説

上記の変更箇所は、GoのCコンパイラがワイド文字列定数を処理する方法の根本的な変更を示しています。

  1. cc.hにおけるTRuneの定義と利用:

    • typedef Rune TRune;は、Goの内部でUnicodeコードポイントを表すRune型を、Cコンパイラの文脈でTRuneとしてエイリアスしています。これにより、ルーンのサイズがint32(4バイト)であることが保証され、Unicodeの21ビットコードポイントを完全に表現できるようになります。
    • struct Noderstringフィールドがushort*からTRune*に変更されたことで、抽象構文木(AST)のノードがワイド文字列定数を保持する際に、16ビットのushortではなく、より広い範囲のUnicode文字を格納できるTRuneのポインタを持つようになります。これは、コンパイラの内部表現がUnicodeの要件に適合するように更新されたことを意味します。
    • TRUNE = sizeof(TRune)==4? TUINT: TUSHORT,という定義は、コンパイル時のTRuneの実際のサイズに応じて、コンパイラがその型をTUINT(符号なし整数)またはTUSHORT(符号なし短整数)として扱うことを示しています。これにより、異なるアーキテクチャやコンパイル設定においても、TRuneのサイズに合わせた適切な型推論と処理が可能になります。
    • outlstring関数のシグネチャ変更も、ワイド文字列の出力処理がTRune型を直接扱うように更新されたことを示しています。
  2. cc.yにおけるlstringルールの変更:

    • lstringルールは、ワイド文字列定数の構文解析とASTノードの生成を担当します。このルール内で、$$->type = typ(TARRAY, types[TUSHORT]);$$->type = typ(TARRAY, types[TRUNE]);に変更されたことで、生成されるワイド文字列定数の型がTRuneの配列として扱われるようになります。
    • 文字列の幅の計算もsizeof(ushort)からsizeof(TRune)に変更され、メモリ割り当てや文字列の結合時にTRuneのサイズが考慮されるようになりました。これにより、可変長のUTF-8バイト列ではなく、固定長のルーンとしてワイド文字列が正しく処理されます。
  3. pswt.cにおけるoutlstring関数の変更:

    • outlstring関数は、ワイド文字列をターゲットのバイナリ形式で出力する役割を担います。この関数がTRune *sを受け取るように変更されたことで、入力がTRuneのシーケンスであることを前提として処理が行われます。
    • 内部のバッファbufのサイズがsizeof(TRune)に、ループ条件がnstring & (sizeof(TRune)-1)に、そしてoutstringに渡すサイズがsizeof(TRune)に変更されたことで、TRuneの実際のサイズ(例えば4バイト)に基づいてバイト列のパディングと出力が行われるようになります。これにより、異なるバイトオーダーのシステムでも、TRuneの各バイトが正しく配置されて出力されるようになります。

これらの変更は、GoのCコンパイラがUnicodeのより広い範囲の文字を、内部的に一貫したTRune型で扱い、正確にコンパイルするための基盤を強化するものです。

関連リンク

参考にした情報源リンク

  • コミットメッセージと差分情報: /home/orange/Project/comemo/commit_data/16492.txt
  • Go言語の公式ドキュメント(strings, runesに関する記述)
  • Bisonのドキュメント(バージョンアップによる変更点に関する一般的な情報)
  • Plan 9のCコンパイラに関する一般的な情報
  • Unicodeのコードポイントとエンコーディングに関する一般的な情報