[インデックス 19015] ファイルの概要
このコミットは、Goコンパイラ(cmd/gc
)のビルドに関する問題を修正するものです。具体的には、古いバージョンのGCC(特にDarwin 10.6上のGCC 4.2)がデフォルトでC90モードで動作し、C99モードではないために発生する警告を解消することを目的としています。修正は、src/cmd/gc/plive.c
ファイル内の定数定義にUL
サフィックスを追加することで行われています。
コミット
commit 568f50e3fcf3643cd5dd0ebf645ad2611cd34be5
Author: Dave Cheney <dave@cheney.net>
Date: Thu Apr 3 11:34:31 2014 +1100
cmd/gc: fix build
Darwin 10.6 (gcc 4.2) and some older versions of gcc default to C90 mode, not C99 mode. Silence the warning.
LGTM=aram, iant
R=golang-codereviews, aram, iant
CC=golang-codereviews
https://golang.org/cl/83090050
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/568f50e3fcf3643cd5dd0ebf645ad2611cd34be5
元コミット内容
cmd/gc: fix build
Darwin 10.6 (gcc 4.2) and some older versions of gcc default to C90 mode, not C99 mode. Silence the warning.
変更の背景
この変更の背景には、Goコンパイラ(cmd/gc
)がC言語で書かれた一部のコードを含んでおり、そのコンパイルにGCCを使用しているという事実があります。問題は、特定の環境、特にDarwin 10.6(Mac OS X Snow Leopard)上で動作するGCC 4.2や、その他の古いGCCバージョンが、C言語の標準としてC90(ISO/IEC 9899:1990)モードをデフォルトとして採用していることにありました。
C90では、整数定数の型推論において、その値がint
の範囲を超える場合にlong
やunsigned long
として扱われることが保証されていませんでした。特に、符号なし整数として扱いたい大きな数値リテラル(このケースではFNV-1ハッシュ関数の定数)が、デフォルトで符号付きのint
として解釈され、その結果としてオーバーフロー警告が発生する可能性がありました。
C99(ISO/IEC 9899:1999)では、整数定数の型推論規則がより明確になり、U
(unsigned)やL
(long)、LL
(long long)といったサフィックスを使用して、リテラルの型を明示的に指定できるようになりました。これにより、コンパイラはリテラルを意図した型で解釈し、警告を回避できます。
このコミットは、Goコンパイラのビルドプロセスにおいて、これらの古いGCC環境での警告を解消し、ビルドの健全性を保つために行われました。警告はビルドを停止させるものではないかもしれませんが、開発者にとってはノイズとなり、潜在的な問題を示唆していると誤解される可能性もあります。そのため、警告を抑制し、クリーンなビルドを維持することは、ソフトウェア開発において重要なプラクティスです。
前提知識の解説
1. Goコンパイラ (cmd/gc
)
Go言語の公式コンパイラは、gc
(Go Compiler)と呼ばれます。Go言語自体は比較的新しい言語ですが、そのコンパイラの一部、特に初期の段階では、C言語で書かれたコードを含んでいました。これは、既存のツールチェインとの統合や、パフォーマンス上の理由から採用されたアプローチです。src/cmd/gc
ディレクトリには、このコンパイラのソースコードが含まれています。
2. C言語の標準 (C90, C99)
C言語には、その仕様を定義する国際標準が存在します。
- C90 (ANSI C / C89 / ISO/IEC 9899:1990): 1989年にANSIによって標準化され、1990年にISOによって採択されたC言語の最初の国際標準です。多くの古いシステムやコンパイラがこの標準をベースにしています。
- C99 (ISO/IEC 9899:1999): 1999年に採択されたC言語の改訂版です。この標準では、
//
コメント、long long
型、可変長配列、restrict
キーワード、複合リテラル、そして整数定数の型推論規則の改善など、多くの新機能や改善が導入されました。
3. GCC (GNU Compiler Collection)
GCCは、GNUプロジェクトによって開発された、様々なプログラミング言語に対応するコンパイラの集合体です。C言語のコンパイルにも広く使用されており、-std=c90
や-std=c99
といったオプションで、準拠するC言語の標準を指定できます。しかし、これらのオプションが明示的に指定されない場合、GCCはデフォルトの標準モードで動作します。古いバージョンのGCCでは、デフォルトがC90モードであることが一般的でした。
4. 整数定数の型とUL
サフィックス
C言語において、数値リテラル(例: 123
, 0xFF
)は、その値と形式に基づいてコンパイラによって型が推論されます。
- デフォルトの型推論: 整数リテラルは、まず
int
、次にlong
、long long
の順で、その値を表現できる最小の符号付き整数型として推論されます。 - 符号なし整数: 符号なし整数としてリテラルを扱いたい場合、
U
またはu
サフィックスを付けます(例:123U
)。これにより、unsigned int
、unsigned long
、unsigned long long
の順で型が推論されます。 L
サフィックス:L
またはl
サフィックスは、リテラルをlong
型として扱いたい場合に使用します(例:123L
)。UL
サフィックス:UL
またはul
サフィックスは、リテラルをunsigned long
型として扱いたい場合に使用します(例:123UL
)。
このコミットで問題となったのは、2166136261
や16777619
といった数値が、C90モードのGCCではデフォルトでint
として解釈され、これらの値がint
の最大値(通常2,147,483,647)を超える場合にオーバーフロー警告が発生したことです。UL
サフィックスを追加することで、これらの数値がunsigned long
として明示的に扱われるようになり、警告が解消されます。
5. FNV-1 ハッシュ関数
FNV-1(Fowler-Noll-Vo)ハッシュ関数は、高速で衝突が少ないことで知られる非暗号学的ハッシュ関数です。このハッシュ関数は、主にデータ構造のハッシュテーブルやチェックサムの計算などに利用されます。FNV-1ハッシュ関数には、初期値(H0
)と素数(Hp
)という2つの重要な定数があります。
H0
(FNV_prime): 2166136261Hp
(FNV_offset_basis): 16777619
これらの定数は、ハッシュ計算の基盤となる値であり、正確な型で扱われることが重要です。
技術的詳細
このコミットの技術的な核心は、C言語の整数リテラルの型推論と、異なるC標準(C90とC99)間でのその挙動の違いにあります。
Goコンパイラのsrc/cmd/gc/plive.c
ファイルには、FNV-1ハッシュ関数の定数であるH0
とHp
が#define
プリプロセッサディレクティブを使って定義されていました。
変更前:
#define H0 2166136261
#define Hp 16777619
ここで定義されている2166136261
という数値は、32ビット符号付き整数(int
)の最大値である2^31 - 1 = 2,147,483,647
を超えています。
C90標準では、このような大きな整数リテラルがint
の範囲を超える場合、コンパイラはそれをlong int
、unsigned long int
の順で、その値を表現できる最小の型に推論します。しかし、この推論プロセスはコンパイラの実装に依存する部分があり、特に古いGCCバージョンでは、デフォルトでint
として解釈しようとし、値が大きすぎるためにオーバーフロー警告を出すことがありました。これは、リテラルが符号付きのint
として扱われると、その値が負の数として解釈されるか、あるいは単にオーバーフローとして警告されるためです。
C99標準では、整数リテラルの型推論規則がより厳密に定義され、U
やL
などのサフィックスを使って明示的に型を指定することが推奨されています。UL
サフィックスは、リテラルをunsigned long
型として扱うことをコンパイラに指示します。unsigned long
型は、通常32ビットまたは64ビットの符号なし整数であり、2166136261
のような大きな正の値を問題なく表現できます。
変更後:
#define H0 2166136261UL
#define Hp 16777619UL
UL
サフィックスを追加することで、コンパイラはこれらの定数を明示的にunsigned long
として解釈するようになります。これにより、int
の範囲を超過することによるオーバーフロー警告が完全に抑制され、ビルドプロセスがクリーンになります。この修正は、コードのセマンティクス(意味)を変えるものではなく、単にコンパイラの警告を解消し、異なるC標準モードでの互換性を向上させるためのものです。
この問題は、Goコンパイラがクロスコンパイル環境や様々なOS/アーキテクチャでビルドされることを考えると、特に重要です。異なる環境で異なるバージョンのGCCが使用される可能性があるため、このような移植性の問題に対処することは、Goエコシステムの健全性を維持するために不可欠です。
コアとなるコードの変更箇所
変更はsrc/cmd/gc/plive.c
ファイル内の2行に限定されています。
--- a/src/cmd/gc/plive.c
+++ b/src/cmd/gc/plive.c
@@ -1633,8 +1633,8 @@ livenessepilogue(Liveness *lv)
}
// FNV-1 hash function constants.
-#define H0 2166136261
-#define Hp 16777619
+#define H0 2166136261UL
+#define Hp 16777619UL
具体的には、H0
とHp
という2つの#define
定数の定義において、数値リテラルの末尾にUL
サフィックスが追加されました。
コアとなるコードの解説
このコミットのコアとなるコード変更は、C言語のプリプロセッサマクロ定義における数値リテラルの型指定に関するものです。
-
#define H0 2166136261
から#define H0 2166136261UL
への変更:H0
はFNV-1ハッシュ関数の初期オフセット値(FNV_offset_basis)です。この値2166136261
は、32ビット符号付き整数(int
)の最大値(約2.147億)を超えています。- C90モードのGCCでは、この数値リテラルがデフォルトで
int
として解釈され、オーバーフロー警告が発生していました。 UL
サフィックス(Unsigned Long)を追加することで、コンパイラに対してこのリテラルを明示的にunsigned long
型として扱うように指示します。これにより、値がunsigned long
の範囲内に収まるため、オーバーフロー警告が解消されます。
-
#define Hp 16777619
から#define Hp 16777619UL
への変更:Hp
はFNV-1ハッシュ関数の素数(FNV_prime)です。この値16777619
はint
の範囲内ですが、H0
との一貫性を保ち、将来的な互換性や潜在的な型推論の問題を避けるために、同様にUL
サフィックスが追加されました。これにより、両方の定数が同じunsigned long
型として扱われることが保証されます。
この変更は、コードの論理的な動作には影響を与えません。FNV-1ハッシュ関数の計算自体は、これらの定数が正しく符号なし整数として扱われる限り、変更前と同じ結果を生成します。この修正の唯一の目的は、特定のコンパイラ環境(古いGCCのC90モード)でのビルド時の警告を抑制し、ビルドプロセスをよりクリーンでエラーフリーにすることです。これは、Goコンパイラの移植性と堅牢性を高めるための、細かではあるが重要な改善と言えます。
関連リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Go言語のコードレビューシステム (Gerrit): https://go.dev/cl/83090050 (コミットメッセージに記載されているCLリンク)
参考にした情報源リンク
- C言語の整数定数サフィックスに関する情報:
- C90とC99の比較に関する情報:
- FNV-1ハッシュ関数に関する情報:
- GCCのC標準モードに関する情報:
- Darwin 10.6 (Mac OS X Snow Leopard) およびその時代のGCCに関する一般的な情報。