[インデックス 16020] ファイルの概要
このコミットは、Go言語のコンパイラツールチェーンの一部であるcmd/6g
(x86-64アーキテクチャ向けGoコンパイラ)において、Plan 9オペレーティングシステム上で発生していたC言語のコンパイラ警告を修正するものです。具体的には、未使用変数の警告、ストレージクラスの不適切な指定、およびprint
関数のフォーマット文字列と引数の不一致に関する警告が対象となっています。これらの修正により、コンパイラの出力がクリーンになり、潜在的なバグや未定義動作のリスクが低減されます。
コミット
commit 3a14b42edfc5626ef805578e462df838359e7130
Author: David du Colombier <0intro@gmail.com>
Date: Sat Mar 30 09:31:49 2013 -0700
cmd/6g: fix warnings on Plan 9
src/cmd/6g/peep.c:471 set and not used: r
src/cmd/6g/peep.c:560 overspecified class: regconsttyp GLOBL STATIC
src/cmd/6g/peep.c:761 more arguments than format IND STRUCT Prog
src/cmd/6g/reg.c:185 set and not used: r1
src/cmd/6g/reg.c:786 format mismatch d VLONG, arg 3
src/cmd/6g/reg.c:1064 format mismatch d VLONG, arg 5
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/8197044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3a14b42edfc5626ef805578e462df838359e7130
元コミット内容
cmd/6g: fix warnings on Plan 9
src/cmd/6g/peep.c:471 set and not used: r
src/cmd/6g/peep.c:560 overspecified class: regconsttyp GLOBL STATIC
src/cmd/6g/peep.c:761 more arguments than format IND STRUCT Prog
src/cmd/6g/reg.c:185 set and not used: r1
src/cmd/6g/reg.c:786 format mismatch d VLONG, arg 3
src/cmd/6g/reg.c:1064 format mismatch d VLONG, arg 5
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/8197044
変更の背景
Go言語の初期のコンパイラおよびツールチェーンは、Plan 9オペレーティングシステムの設計思想やツールチェイン(特に8c
, 6c
, 5c
といったCコンパイラや8l
, 6l
, 5l
といったリンカ)に強く影響を受けていました。cmd/6g
もその一つで、Go言語のソースコードをx86-64アーキテクチャ向けにコンパイルする際に使用されるC言語で書かれたコンパイラです。
このコミットが行われた2013年当時、Go言語の開発は活発であり、様々なプラットフォームでの動作保証が求められていました。Plan 9環境でcmd/6g
をビルドする際に、Cコンパイラが特定の警告を出力していました。これらの警告は、コードの品質を低下させ、潜在的なバグを示唆する可能性があり、またビルドプロセスを煩雑にするため、修正が必要とされました。
具体的に報告された警告は以下の通りです。
- 未使用変数: 変数が宣言されたものの、コード内で一度も使用されていない場合に発生します。これはリソースの無駄や、意図しないロジックの欠陥を示すことがあります。
- ストレージクラスの不適切な指定: 変数や関数のスコープやリンケージを定義するストレージクラス(例:
static
,extern
,GLOBL
)が矛盾して指定されている場合に発生します。 - フォーマット文字列の不一致:
printf
のような可変引数関数(この場合はGoコンパイラ内部のprint
関数)において、フォーマット指定子(例:%d
,%lld
)と、それに対応する引数の型が一致しない場合に発生します。これは未定義動作を引き起こす可能性があり、非常に危険な警告です。
これらの警告を修正することで、cmd/6g
のコードベースの健全性を保ち、Plan 9を含む様々な環境での安定したビルドと動作を保証することが目的でした。
前提知識の解説
cmd/6g
cmd/6g
は、Go言語のコンパイラツールチェーンの一部であり、具体的にはx86-64(64ビットIntel/AMD)アーキテクチャ向けのGoソースコードをコンパイルする役割を担っていました。Go言語の初期のコンパイラは、C言語で書かれており、Plan 9のツールチェインの慣習に従っていました。6g
の6
はx86-64アーキテクチャを指し、g
はGoコンパイラであることを示します。同様に、8g
はx86-32、5g
はARMを指していました。現在では、GoコンパイラはGo言語自体で書かれており、これらの命名規則は使われていませんが、当時のGo言語の内部構造を理解する上で重要な要素です。
Plan 9
Plan 9 from Bell Labsは、ベル研究所によって開発された分散オペレーティングシステムです。Unixの後継として設計され、ネットワーク透過性、ファイルシステム中心の設計、UTF-8の採用など、多くの革新的な概念を導入しました。Go言語の開発者の一部はPlan 9の開発にも携わっており、Go言語の設計思想やツールチェインにはPlan 9の影響が色濃く見られます。特に、Goの初期のコンパイラやアセンブラは、Plan 9のツールチェイン(8c
, 6c
, 5c
など)の設計を模倣していました。
C言語のコンパイラ警告
C言語のコンパイラは、プログラムの潜在的な問題や非効率性を示すために警告を発します。これらはエラーとは異なり、プログラムのコンパイル自体は可能ですが、無視すべきではありません。
-
set and not used
(未使用変数): 変数が宣言され、値が代入されたにもかかわらず、その変数が後続のコードで一度も読み取られたり使用されたりしない場合に発生します。これは、プログラマの意図しないミス、デバッグ用のコードの残骸、または将来の機能のためのプレースホルダーである可能性があります。コンパイラは通常、このような変数を最適化で削除しますが、警告を出すことでプログラマに注意を促します。 GoコンパイラのCコードでは、特定の変数がデバッグ目的や、特定のAPIシグネチャに合わせるために存在する必要があるが、特定のコンパイルパスでは使用されない場合があります。このような場合、警告を抑制するためにUSED()
マクロ(通常は(void)var;
のように展開される)が使われることがあります。 -
overspecified class
(ストレージクラスの不適切な指定): C言語では、変数のスコープ(可視性)とリンケージ(結合性)を制御するためにストレージクラス指定子(static
,extern
,auto
,register
など)を使用します。static
: 関数内で使用された場合、その変数は静的ストレージ期間を持ち、プログラムの実行期間中メモリに存在し続けます。ファイルスコープ(グローバル)で宣言された場合、その変数や関数は宣言されたファイル内でのみ可視となります(内部リンケージ)。GLOBL
: Plan 9のツールチェインにおけるCコンパイラ(6c
など)の文脈で使われる可能性のあるキーワードで、シンボルがグローバルに可視であることを示唆します。GLOBL STATIC
のような組み合わせは、シンボルがグローバルに可視であると同時に、現在のファイル内でのみ可視であるという矛盾した指定となり、コンパイラが警告を発します。
-
format mismatch
(フォーマット文字列の不一致):printf
や、それに類するカスタムのprint
関数において、フォーマット指定子(例:%d
、%s
、%lld
)と、それに対応する引数のデータ型が一致しない場合に発生します。例えば、int
型の引数に対して%lld
(long long
用)を使用したり、逆にlong long
型の引数に対して%d
(int
用)を使用したりすると、この警告が出ます。これは、メモリ上のデータの解釈が誤るため、未定義動作(クラッシュ、不正な出力、セキュリティ脆弱性など)を引き起こす可能性のある重大な問題です。
技術的詳細
このコミットは、src/cmd/6g/peep.c
とsrc/cmd/6g/reg.c
の2つのファイルに対して行われました。それぞれの警告と修正内容を詳細に見ていきます。
src/cmd/6g/peep.c
の修正
-
set and not used: r
(行471):elimshortmov
関数内でReg *r
が引数として渡されていますが、関数内でr
が直接使用されていませんでした。GoコンパイラのCコードでは、このような場合にUSED()
マクロを使用して警告を抑制する慣習があります。- 修正:
USED(r);
を追加。これにより、コンパイラはr
が使用されていると判断し、警告を出さなくなります。USED()
マクロは通常、(void)r;
のように展開され、変数の値を実際に使用することなく、コンパイラに「使用済み」と認識させるためのものです。
- 修正:
-
overspecified class: regconsttyp GLOBL STATIC
(行560):regconsttyp
関数が、グローバルシンボル(GLOBL
)でありながら、同時に静的(STATIC
)であるという矛盾したストレージクラス指定を受けていました。これは、関数がそのファイル内でのみ使用されるべきであるにもかかわらず、外部からも参照可能であるかのような誤解を招く可能性がありました。- 修正:
int regconsttyp(Adr *a)
をstatic int regconsttyp(Adr *a)
に変更。これにより、regconsttyp
関数は現在のファイル内でのみ可視となり、リンケージの矛盾が解消されます。
- 修正:
-
more arguments than format IND STRUCT Prog
(行761):subprop
関数内のデバッグ出力用のprint
関数呼び出しで、フォーマット文字列が引数よりも少ない状態でした。具体的には、print("\\tran off end; return 0\\n", p);
という行で、フォーマット文字列"\\tran off end; return 0\\n"
は引数を期待していないにもかかわらず、p
という引数が渡されていました。- 修正:
print("\\tran off end; return 0\\n", p);
をprint("\\tran off end; return 0\\n");
に変更。余分な引数p
を削除することで、フォーマット文字列と引数の数が一致し、警告が解消されます。
- 修正:
src/cmd/6g/reg.c
の修正
-
set and not used: r1
(行185):regopt
関数内でr1 = R;
という初期化が行われていましたが、その後r1
が使用されていませんでした。- 修正:
r1 = R;
の行を削除。この変数は完全に不要であったため、宣言と初期化自体を削除することで警告を解消しました。
- 修正:
-
format mismatch d VLONG, arg 3
(行786):print
関数呼び出しにおいて、フォーマット指定子%d
が使用されていましたが、対応する引数がVLONG
(おそらくlong long
型、またはそれに相当する大きな整数型)でした。%d
は通常int
型に対応するため、型が一致していませんでした。- 修正:
print("registerize %N+%d (bit=%2d et=%2E) in %R\\n", ...)
をprint("registerize %N+%lld (bit=%2d et=%2E) in %R\\n", ...)
に変更。%d
を%lld
に修正することで、long long
型の引数に対応する正しいフォーマット指定子となり、警告が解消されます。
- 修正:
-
format mismatch d VLONG, arg 5
(行1064):mkvar
関数内のprint
関数呼び出しでも同様に、%d
がVLONG
型の引数に対して使用されていました。- 修正:
print("bit=%2d et=%2E w=%d+%d %#N %D flag=%d\\n", ...)
をprint("bit=%2d et=%2E w=%d+%lld %#N %D flag=%d\\n", ...)
に変更。ここでも%d
を%lld
に修正することで、long long
型の引数に対応する正しいフォーマット指定子となり、警告が解消されます。
- 修正:
これらの修正は、C言語のコンパイラが生成する警告を真摯に受け止め、コードの正確性と移植性を向上させるための典型的なプラクティスを示しています。特にフォーマット文字列の不一致は、未定義動作に直結するため、その修正は重要です。
コアとなるコードの変更箇所
src/cmd/6g/peep.c
--- a/src/cmd/6g/peep.c
+++ b/src/cmd/6g/peep.c
@@ -472,6 +472,7 @@ elimshortmov(Reg *r)
{\n \tProg *p;\n \n+\tUSED(r);\n \tfor(r=firstr; r!=R; r=r->link) {\n \t\tp = r->prog;\n \t\tif(regtyp(&p->to)) {\n@@ -555,7 +556,7 @@ elimshortmov(Reg *r)\n \t}\n }\n \n-int\n+static int\n regconsttyp(Adr *a)\n {\n \tif(regtyp(a))\n@@ -758,7 +759,7 @@ subprop(Reg *r0)\n \t\t}\n \t}\n \tif(debug['P'] && debug['v'])\n-\t\tprint("\\tran off end; return 0\\n", p);\n+\t\tprint("\\tran off end; return 0\\n");\n \treturn 0;\n \n gotit:
src/cmd/6g/reg.c
--- a/src/cmd/6g/reg.c
+++ b/src/cmd/6g/reg.c
@@ -182,7 +182,6 @@ regopt(Prog *firstp)
\t\treturn;\n \t}\n \n-\tr1 = R;\n \tfirstr = R;\n \tlastr = R;\n \n@@ -783,7 +782,7 @@ brk:\n \t\t\t\tVar *v;\n \n \t\t\t\tv = var + rgp->varno;\n-\t\t\t\tprint("registerize %N+%d (bit=%2d et=%2E) in %R\\n",\n+\t\t\t\tprint("registerize %N+%lld (bit=%2d et=%2E) in %R\\n",\n \t\t\t\t\t\tv->node, v->offset, rgp->varno, v->etype, rgp->regno);\n \t\t\t}\n \t\t\tpaint3(rgp->enter, rgp->varno, vreg, rgp->regno);\n@@ -1061,7 +1060,7 @@ mkvar(Reg *r, Adr *a)
\tv->node = node;\n \n \tif(debug['R'])\n-\t\tprint("bit=%2d et=%2E w=%d+%d %#N %D flag=%d\\n", i, et, o, w, node, a, v->addr);\n+\t\tprint("bit=%2d et=%2E w=%d+%lld %#N %D flag=%d\\n", i, et, o, w, node, a, v->addr);\n \n \tostats.nvar++;\n \n```
## コアとなるコードの解説
### `src/cmd/6g/peep.c`
* **行472 (`elimshortmov`関数内)**:
`+ USED(r);` の追加は、引数として渡された`r`がコード内で直接使用されていないことによる「未使用変数」警告を抑制するためのものです。`USED()`マクロは、コンパイラに`r`が意図的に使用されていないわけではないことを伝え、警告を回避します。
* **行556 (`regconsttyp`関数宣言)**:
`- int` から `+ static int` への変更は、`regconsttyp`関数が`GLOBL`と`STATIC`という矛盾したストレージクラス指定を受けていた問題を解決します。`static`キーワードを追加することで、この関数が現在のコンパイルユニット(このファイル)内でのみ可視であることを明示し、リンケージの矛盾を解消します。
* **行759 (`subprop`関数内)**:
`- print("\\tran off end; return 0\\n", p);` から `+ print("\\tran off end; return 0\\n");` への変更は、`print`関数のフォーマット文字列が引数を期待していないにもかかわらず、`p`という余分な引数が渡されていた問題を修正します。引数`p`を削除することで、フォーマット文字列と引数の数が一致し、コンパイラ警告が解消されます。
### `src/cmd/6g/reg.c`
* **行183 (`regopt`関数内)**:
`- r1 = R;` の削除は、変数`r1`が宣言され初期化されたものの、その後コード内で全く使用されていなかったことによる「未使用変数」警告を解消するためのものです。不要な変数を削除することで、コードの冗長性が減り、クリーンになります。
* **行784 (`brk`ラベル内)**:
`- print("registerize %N+%d (bit=%2d et=%2E) in %R\\n",` から `+ print("registerize %N+%lld (bit=%2d et=%2E) in %R\\n",` への変更は、`print`関数呼び出しにおけるフォーマット文字列の不一致を修正します。`%d`は通常`int`型に対応しますが、対応する引数が`VLONG`(おそらく`long long`型)であったため、`%lld`(`long long`型用)に修正することで、正しい型で値が解釈されるようになります。これは未定義動作を防ぐ上で非常に重要な修正です。
* **行1062 (`mkvar`関数内)**:
`- print("bit=%2d et=%2E w=%d+%d %#N %D flag=%d\\n", ...)` から `+ print("bit=%2d et=%2E w=%d+%lld %#N %D flag=%d\\n", ...)` への変更も、同様に`print`関数呼び出しにおけるフォーマット文字列の不一致を修正します。ここでも`%d`が`VLONG`型の引数に対して使用されていたため、`%lld`に修正されています。
これらの修正は、GoコンパイラのC言語コードベースにおける品質と堅牢性を向上させるための、細かではあるが重要な改善です。特にフォーマット文字列の修正は、デバッグ出力の正確性を保証し、潜在的なクラッシュや誤った情報表示を防ぎます。
## 関連リンク
* Go CL 8197044: [https://golang.org/cl/8197044](https://golang.org/cl/8197044)
## 参考にした情報源リンク
* Go言語の初期のコンパイラとPlan 9の影響に関する一般的な情報
* C言語のコンパイラ警告(未使用変数、ストレージクラス、フォーマット文字列の不一致)に関する一般的な情報
* `USED()`マクロの一般的な用途
* `printf`フォーマット指定子に関するC言語のドキュメント