[インデックス 18474] ファイルの概要
このコミットは、Goコンパイラツールチェーンにおけるcmd/cc
、cmd/gc
、cmd/ld
といった各ツールが持つ出力フォーマットルーチンを、共通のliblink
ライブラリに集約することを目的としています。これにより、コードの重複を排除し、保守性を向上させることが期待されます。
コミット
commit 2cae0591cd77cf307a661a9adc44cff2a56c64a9
Author: Anthony Martin <ality@pbrane.org>
Date: Wed Feb 12 14:29:11 2014 -0500
cmd/cc, cmd/gc, cmd/ld: consolidate print format routines
We now use the %A, %D, %P, and %R routines from liblink
across the board.
Fixes #7178.
Fixes #7055.
LGTM=iant
R=golang-codereviews, gobot, rsc, dave, iant, remyoudompheng
CC=golang-codereviews
https://golang.org/cl/49170043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2cae0591cd77cf307a661a9adc44cff2a56c64a9
元コミット内容
cmd/cc
, cmd/gc
, cmd/ld
: print format routinesを統合
liblinkの%A, %D, %P, %Rルーチンを全体的に使用するようになりました。
Fixes #7178. Fixes #7055.
変更の背景
このコミットの主な背景は、Goコンパイラツールチェーン内のコードの重複を減らし、保守性を高めることです。以前は、cmd/cc
(Cコンパイラ)、cmd/gc
(Goコンパイラ)、cmd/ld
(リンカ) といった異なるツールが、それぞれ独自の出力フォーマットルーチン(例えば、アセンブリ命令、アドレス、プログラム構造体、レジスタなどを文字列に変換する機能)を持っていました。これは、各ツールが独立して開発された歴史的経緯や、特定のアーキテクチャ(5g/5lはARM、6g/6lはAMD64、8g/8lはx86)に特化した実装を持っていたためと考えられます。
しかし、このような重複は、機能の追加やバグ修正が必要になった際に、複数の場所で同じ変更を行う必要があり、開発効率の低下やバグの混入リスクを高めます。このコミットは、これらの共通のフォーマットルーチンをliblink
という共有ライブラリに集約することで、一度の変更で全てのツールに反映されるようにし、コードベース全体の整合性と品質を向上させることを目指しています。
コミットメッセージにはFixes #7178
とFixes #7055
と記載されていますが、これらのIssueに関する詳細な情報は現在のところ見つかっていません。しかし、一般的にこのような変更は、特定の出力フォーマットの不整合や、新しいアーキテクチャサポートの際に既存のフォーマットルーチンを再利用したいといった要望から発生することが多いです。
前提知識の解説
このコミットを理解するためには、以下のGoコンパイラツールチェーンの構成要素とC言語のフォーマット機能に関する知識が必要です。
Goコンパイラツールチェーンの主要コンポーネント
cmd/gc
(Go Compiler): Go言語のソースコードを中間表現(アセンブリに近い形式)にコンパイルするGo言語のコンパイラです。各アーキテクチャ(例:5g
for ARM,6g
for AMD64,8g
for x86)ごとに存在していました。cmd/cc
(C Compiler): Goのランタイムや一部の標準ライブラリはC言語で書かれているため、それらをコンパイルするためのCコンパイラです。cmd/ld
(Linker): コンパイルされたオブジェクトファイルやライブラリを結合し、実行可能なバイナリを生成するリンカです。各アーキテクチャ(例:5l
for ARM,6l
for AMD64,8l
for x86)ごとに存在していました。liblink
: Goツールチェーン内で共有される低レベルのライブラリで、主にリンカやコンパイラのバックエンドが共通して利用するユーティリティ関数やデータ構造を提供します。アセンブリコードの生成やシンボル解決など、プラットフォームに依存しない共通の処理が含まれます。
C言語のフォーマット機能 (fmtinstall
, varargck
)
Goコンパイラツールチェーンの多くの部分はC言語で書かれており、出力処理にはPlan 9 Cコンパイラ由来のfmt
ライブラリが使用されています。
fmtinstall(char verb, Fmtconv conv)
: これは、fmt
ライブラリの拡張機能です。printf
のようなフォーマット関数で使用されるカスタムの「動詞」(%d
,%s
のようなもの)を登録するために使用されます。verb
: 登録するカスタム動詞の文字(例:'A'
,'D'
,'P'
,'R'
)。conv
: その動詞が指定されたときに呼び出される変換関数へのポインタ。この関数は、対応する引数を文字列に変換し、出力ストリームに書き込む責任を持ちます。
#pragma varargck type "verb" Type
: これは、Plan 9 Cコンパイラ(Goツールチェーンのビルドにも使用される)のコンパイラディレクティブ(プラグマ)です。printf
のような可変引数関数において、特定のカスタム動詞(verb
)に対応する引数の型(Type
)をコンパイラに教えるために使用されます。これにより、コンパイラは型チェックを行い、誤った型の引数が渡された場合に警告やエラーを出すことができます。- 例えば、
#pragma varargck type "P" Prog*
は、%P
という動詞がProg*
型の引数を期待することを示します。
- 例えば、
このコミットでは、各ツールが個別に持っていたPconv
, Aconv
, Dconv
, Rconv
といった変換関数をliblink
に移動し、各ツールからはliblink
の共通関数を呼び出すように変更しています。
技術的詳細
このコミットの核心は、Goコンパイラおよびリンカの内部で利用されるアセンブリコードやデータ構造のテキスト表現を生成するルーチンの一元化です。具体的には、以下の4つの主要なフォーマット動詞(およびそれに対応する変換関数)が対象となります。
%A
(Assembly instruction): アセンブリ命令(例:MOV
,ADD
,CALL
など)を文字列に変換します。%D
(Address): メモリのアドレスやオペランドを文字列に変換します。これには、レジスタ、オフセット、シンボルなどが含まれます。%P
(Program instruction): Goコンパイラやリンカの内部表現であるProg
構造体(単一のアセンブリ命令とそのオペランド、メタデータを含む)全体を整形して出力します。%R
(Register): レジスタ(例:R0
,AX
,SP
など)を文字列に変換します。
変更前は、これらの変換関数(例: Aconv
, Dconv
, Pconv
, Rconv
)が、src/cmd/5g/list.c
, src/cmd/6g/list.c
, src/cmd/8g/list.c
といった各アーキテクチャのコンパイラや、リンカのsrc/cmd/5l/list.c
などに個別に存在していました。それぞれの実装は似ていますが、細かな差異や重複がありました。
このコミットでは、以下の手順で統合が行われました。
liblink
へのルーチンの移動: 各cmd/*/list.c
ファイルから、共通のフォーマット変換関数(Pconv
,Aconv
,Dconv
,Rconv
など)の実装が削除され、src/liblink/list5.c
,src/liblink/list6.c
,src/liblink/list8.c
といったliblink
内のファイルに集約されました。これらのファイルは、それぞれARM、AMD64、x86アーキテクチャに対応する共通のフォーマットルーチンを提供します。fmtinstall
の変更: 各ツール(例:src/cmd/5a/lex.c
,src/cmd/6a/lex.c
,src/cmd/8a/lex.c
など)の初期化ルーチン(listinit
関数内)から、個別のfmtinstall
呼び出しが削除されました。代わりに、liblink
が提供する共通の初期化関数(例:listinit5()
,listinit6()
,listinit8()
)が呼び出されるようになりました。これらの共通初期化関数内で、liblink
に移動された変換関数がfmtinstall
によって登録されます。#pragma varargck
の調整:include/link.h
に、%A
,%D
,%P
,%R
などの新しいvarargck
プラグマが追加されました。これにより、liblink
の共通ルーチンが期待する引数の型が明示され、コンパイラによる型チェックが強化されます。同時に、各ツール固有のヘッダファイル(例:src/cmd/5c/gc.h
,src/cmd/6c/gc.h
,src/cmd/5l/l.h
など)から、重複するvarargck
プラグマや、もはや不要になった変換関数のプロトタイプ宣言が削除されました。- 内部表現の統一: 一部のファイルでは、
Prog
構造体のフィールド名がloc
からpc
に変更されています。これは、プログラムカウンタ(Program Counter)を意味するpc
という名称に統一することで、より明確な意味を持たせるための変更です。
この変更により、GoツールチェーンのコードベースはよりDRY (Don't Repeat Yourself) になり、将来的なメンテナンスや機能拡張が容易になりました。例えば、新しいアーキテクチャのサポートを追加する際に、既存のフォーマットルーチンを再利用できるため、開発コストが削減されます。
コアとなるコードの変更箇所
このコミットでは、多数のファイルが変更されていますが、特に重要な変更箇所は以下の通りです。
include/link.h
:Prog
構造体からuint32 loc;
が削除され、代わりにpc
が使用されるようになったことを示唆しています。#pragma varargck
ディレクティブが追加され、%A
,%D
,%P
,%R
などのフォーマット動詞に対応する型が定義されています。これは、これらのフォーマットルーチンがliblink
に集約され、共通のインターフェースを持つようになったことを示しています。
include/plan9/link.h
:- 多数の
#pragma varargck
ディレクティブが削除されています。これは、これらの定義がinclude/link.h
に移動され、一元化されたことを意味します。
- 多数の
src/cmd/5c/gc.h
,src/cmd/6c/gc.h
,src/cmd/8c/gc.h
:Var
構造体の定義や、Pconv
,Aconv
,Dconv
などの個別の変換関数のプロトタイプ宣言が削除されています。これは、これらの機能がliblink
に移動されたため、各コンパイラのヘッダからは不要になったことを示します。
src/cmd/5c/list.c
,src/cmd/6c/list.c
,src/cmd/8c/list.c
:Bconv
などの変換関数の実装が削除され、listinit
関数内でlistinit5()
などのliblink
の初期化関数を呼び出すように変更されています。
src/cmd/5g/list.c
,src/cmd/6g/list.c
,src/cmd/8g/list.c
:- これらのファイルは削除されています。これは、各アーキテクチャのGoコンパイラが独自に持っていた出力フォーマットルーチンが完全に
liblink
に統合されたことを意味する最も顕著な変更です。
- これらのファイルは削除されています。これは、各アーキテクチャのGoコンパイラが独自に持っていた出力フォーマットルーチンが完全に
src/cmd/5l/l.h
,src/cmd/6l/l.h
,src/cmd/8l/l.h
:- リンカのヘッダファイルからも、個別の
#pragma varargck
ディレクティブや変換関数のプロトタイプ宣言が削除されています。
- リンカのヘッダファイルからも、個別の
src/cmd/5l/list.c
,src/cmd/6l/list.c
,src/cmd/8l/list.c
:- リンカの
list.c
ファイルからも、個別の変換関数の実装が削除され、liblink
の初期化関数を呼び出すように変更されています。
- リンカの
src/liblink/list5.c
,src/liblink/list6.c
,src/liblink/list8.c
:- これらのファイルに、削除された各
cmd/*/list.c
ファイルから移動されたPconv
,Aconv
,Dconv
,Rconv
などの共通のフォーマット変換ルーチンが追加されています。
- これらのファイルに、削除された各
コアとなるコードの解説
このコミットのコアとなる変更は、Goツールチェーンのビルドシステムにおけるコードの再利用性と保守性の向上にあります。
例えば、src/cmd/5g/list.c
のようなファイルが完全に削除されたことは、以前はARMアーキテクチャのGoコンパイラが独自にアセンブリ命令やアドレスの表示方法を定義していたことを意味します。同様に、他のアーキテクチャ(AMD64, x86)のコンパイラやリンカもそれぞれ独自の定義を持っていました。
これらの重複するコードは、src/liblink/list5.c
, src/liblink/list6.c
, src/liblink/list8.c
といったliblink
内のファイルに集約されました。これにより、例えば%D
フォーマット(アドレス表示)のロジックにバグが見つかった場合、以前は各アーキテクチャのコンパイラとリンカの合計6箇所以上を修正する必要があったかもしれませんが、変更後はliblink
内の対応するファイル(例: list5.c
)を修正するだけで、そのアーキテクチャの全てのツールに修正が適用されるようになります。
#pragma varargck
の変更は、この統合されたフォーマットルーチンが期待する引数の型をコンパイラに正確に伝えるためのものです。これにより、開発者はフォーマット文字列と引数の型が一致しない場合にコンパイル時に警告を受け取ることができ、実行時エラーのリスクを減らすことができます。
また、Prog
構造体のloc
フィールドがpc
に変更されたことは、コードの可読性と意味の明確化に貢献します。pc
(プログラムカウンタ)という用語は、アセンブリや低レベルプログラミングにおいて命令のアドレスを指す標準的な用語であり、より直感的に理解できます。
全体として、このコミットはGoツールチェーンの内部構造をよりモジュール化し、共通の機能を共有ライブラリに集約することで、長期的な開発とメンテナンスを容易にするための重要なリファクタリングです。
関連リンク
参考にした情報源リンク
- https://github.com/golang/go/commit/2cae0591cd77cf307a661a9adc44cff2a56c64a9
- Goコンパイラツールチェーンの一般的な知識
- Plan 9 Cコンパイラの
fmt
ライブラリとvarargck
プラグマに関する一般的な知識