第4章 コンパイルおよびビルド
4.1. GNU コンパイラーコレクション (GCC)
gcc
や g++
など)、ランタイムライブラリー (libgcc
、libstdc++
、libgfortran
、libgomp
など)、その他のユーティリティーが含まれます。
4.1.1. GCC ステータスおよび機能
- inliner、実行されないコードの除外ルーチン、コンパイル時間、メモリ使用コードが改善されました。このリリースでは、新たなレジスタアロケータ、指示スケジューラ、ソフトウェアパイプラインの機能があります。
- OpenMP のバージョン 3.0 仕様が C、C++、Fortran コンパイラー対応となっています。
- 予定されている ISO C++ 標準 (C++0x) の体験サポートが含まれています。これには、自動/inline ネームスペース、文字タイプ、スコープ列挙のサポートがあります。これを有効にするには、コンパイラーオプションの
-std=c++0x
(GNU 拡張機能を無効化) か-std=gnu++0x
を使用します。C++0x の改善点の詳細リストについては、以下のリンク先を参照してください。 - GCC は Variable Tracking at Assignments (VTA) インフラストラクチャーを組み込むようになりました。これにより、最適化中の GCC の変数追跡機能が向上され、GNU Project Debugger や SystemTap、その他のツール用に作成されるデバッグ情報 (つまり、DWARF) が改善されます。VTA の概要については「Variable Tracking at Assignments」を参照してください。VTA を使うと、以前の GCC リリースと比べてはるかに最適化されたコードのデバッグがうまく実行でき、優れたデバッグ体験を提供するために -O0 でコンパイルする必要がなくなります。
- Fortran 2003 のサポートが延長され、Fortran 2008 がサポート対象となりました。
- 『Updates in the 4.2 Series』: http://gcc.gnu.org/gcc-4.2/changes.html
- 『Updates in the 4.3 Series』: http://gcc.gnu.org/gcc-4.3/changes.html
- 『Updates in the 4.4 Series』: http://gcc.gnu.org/gcc-4.4/changes.html
- 最適化された C++ コードのデバッグ用の DWARF3 デバッグの改善
- Fortran 最適化の改善
- ix86、Intel 64 および AMD64、s390 の指示の長さ情報の精度改善
- Intel Atom のサポート
- POWER7 のサポート
- C++ raw 文字列サポート、u/U/u8 文字列リテラルサポート
4.1.2. 言語の互換性
- 呼び出し規約。これは、引数の関数への渡され方と関数からの結果の返し方を指定します。
- レジスタ使用量規約。これはプロセッサーレジスタの割り当ておよび使用方法を指定します。
- オブジェクトファイル形式。これは、バイナリーオブジェクトコードの表示方法を指定します。
- サイズ、レイアウト、データ配置のタイプ。これは、メモリでのデータの配置方法を指定します。
- ランタイム環境が提供するインターフェース。記録されたセマンティクスがあるバージョンから別のバージョンで変更されない部分では、これらは利用可能である必要があり、常に同じ名前を使う必要があります。
- 名前のマングル化およびデマングル化
- 例外の生成および伝達
- ランタイムタイプ情報のフォーマット
- コンストラクターおよびデストラクター
- クラスおよび派生クラスのレイアウト、配置、パディング
- 仮想テーブルのレイアウトおよび配置といった仮想機能実装の詳細
以下は、Red Hat Enterprise Linux 6 と 5 のツールチェーン間での既知の非互換性です。
- 柔軟性のある配列メンバーの構造体を値でパス/返却すると、Intel 64 および AMD64 で変更する場合がある。
- long double メンバーのユニオンを値でパス/返却すると、Intel 64 および AMD64 で変更する場合がある。
- 複雑な浮動メンバーの構造体を値でパス/返却すると、Intel 64 および AMD64 で変更する場合がある。
-mavx
を使用する場合、x86、Intel 64、AMD64 のプラットフォームでの 256 ビットのベクターのパスが変更される。- _Decimal{32,64,128} タイプおよびいくつかのターゲットで値ごとのものを含む総計のパスに複数の変更がある。
- パックされた char ビットフィールドの圧縮が変更されるケースがある。
以下は、Red Hat Enterprise Linux 5 と 4 のツールチェーン間での既知の非互換性です。
- C++ ABI が指定するライブラリーインターフェースで、機能スコープの静的変数のスレッドセーフな初期化に変更があります。
- Intel 64 および AMD64 では、データセグメントが 4GB を超えるアプリケーション構築用の中型モデルは、当時の最新 ABI ドラフトに一致するように再設計されました。ABI の変更が中型モデルのオブジェクト間での非互換性につながっています。
-Wabi
は、これらの構造文がソースコードのどこに現れるかを示す診断を得るために使用できますが、すべてのケースを捕捉するわけではありません。このフラグは、ベンダー中立の C++ ABI と互換性がないと分かっているコードをコンパイラー生成する際はいつでも警告するので、C++ コードでは特に便利です。
-fabi-version=1
オプションを使用していました。この方法は、推奨されません。この方法で作成されるオブジェクトは、最新の安定的な ABI に適合するオブジェクトと区別がつかず、異なる ABI の間で (誤って) リンクされる可能性があります。特に、新しいコンパイラーを使ってコードを作成し、これを Red Hat Enterprise Linux 4 より前のツールで構築された古いライブラリーにリンクする場合などです。
警告
4.1.3. オブジェクトの互換性と相互運用性
ld
(binutils
パッケージの一部として配布) や動的ローダー (glibc
パッケージの一部として配布される ld.so
) などのツールの変更や新機能は、コンパイラーが作成するオブジェクトファイルをわずかに変更する可能性があります。これらの変更では、Red Hat Enterprise Linux の以前のリリースから最新リリースに移動するオブジェクトファイルは機能を失い、ランタイムで異なる動作をし、相互運用性が減少する可能性があることになります。既知の問題分野は以下のとおりです。
ld
--build-id
Red Hat Enterprise Linux 6 では、これはデフォルトでld
に渡されます。一方、Red Hat Enterprise Linux 5 では、ld
は認識されません。as
.cfi_sections
サポートRed Hat Enterprise Linux 6 では、このディレクティブにより、.cfi*
ディレクティブから.debug_frame
か.eh_frame
、または両方を省略できるようになります。Red Hat Enterprise Linux 5 で省略可能なのは、.eh_frame
のみです。as
、ld
、ld.so
、gdb
、STB_GNU_UNIQUE
、%gnu_unique_symbol
サポートRed Hat Enterprise Linux 6 では、より多くのデバッグ情報が生成されてオブジェクトファイルに保存されます。この情報は、DWARF
標準で詳述されている新機能と標準化されていない新たな拡張機能にも依存しています。Red Hat Enterprise Linux 5 では、as
やld
、gdb
、objdump
、readelf
といったツールはこの新情報に対応できていない可能性があり、新規ツールが作成したオブジェクトとの相互運用に失敗する可能性があります。また、Red Hat Enterprise Linux 5 で作成されたオブジェクトファイルは、これらの新機能に対応していません。これらのオブジェクトファイルは Red Hat Enterprise Linux 6 ツールでは最適でない方法で処理される可能性があります。このようにデバッグの強化情報が増えたことで、システムライブラリーと共に出荷される debuginfo パッケージがインストールされていれば、システムライブラリーへの有益なソースレベルでのデバッグが可能になります。debuginfo パッケージに関する詳細情報は、「Debuginfo パッケージのインストール」を参照してください。
prelink
のポータブルな使用を妨げる可能性があります。
4.1.4. 後方互換性パッケージ
注記
compat-gcc-34
compat-gcc-34-c++
compat-gcc-34-g77
compat-libgfortran-41
4.1.5. Red Hat Enterprise Linux 5 での Red Hat Enterprise Linux 6 コンパイラー機能のプレビュー
gcc44
が含まれています。これは、Red Hat Enterprise Linux 6 コンパイラーのバックポートで、Red Hat Enterprise Linux 5 を実行するユーザーが Red Hat Enterprise Linux 6 コンパイラーでコードをコンパイルして新機能や最適化を体験してから、次のメジャーリリースの際にシステムをアップグレードすることができます。作成されたバイナリーーは Red Hat Enterprise Linux 6 と前方互換があるため、Red Hat Enterprise Linux 5 上で gcc44
を使用してコンパイルして、Red Hat Enterprise Linux 5、Red Hat Enterprise Linux 6 以降で実行するkとおができます。
gcc44
コンパイラーは、Red Hat Enterprise Linux 6 と同梱の GCC 4.4.x と適度に調和され、移行を容易にします。ただし、最新機能を使用するには、開発に Red Hat Enterprise Linux 6 を使用することが推奨されます。gcc44
は変換プロセスの支援としてのみ提供されています。
4.1.6. GCC の実行
binutils
と gcc
をインストールします。こられでいくつかの依存関係もインストールされます。
gcc
コマンドで動作します。これがコンパイラーの主要なドライバーです。コマンドラインから、ソースファイルの前処理またはコンパイル、オブジェクトファイルおよびライブラリーのリンク、またはこれらの組み合わせを実行できます。デフォルトでは、gcc
が提供されている libgcc
ライブラリー内の詳細とリンクの処理をします。
CDT
の一部として Eclipse IDE にも統合されます。これにより多くの利点がもたらされます。グラフィカルインターフェースと完全統合型の環境を好む開発者の場合は特にそうです。
4.1.6.1. シンプルな C の使用
#include <stdio.h> int main () { printf ("Hello world!\n"); return 0; }
手順4.1 'Hello World' C プログラムのコンパイル
- 次のコマンドを使用して hello.c を実行可能ファイルにコンパイルします。
gcc hello.c -o hello
作成されるバイナリーhello
がhello.c
と同じディレクトリーにあることを確認します。 hello
バイナリー、つまりhello
を実行します。
4.1.6.2. シンプルな C++ の使用
#include <iostream> using namespace std; int main(void) { cout << "Hello World!" << endl; return 0; }
手順4.2 'Hello World' C++ プログラムのコンパイル
- 次のコマンドを使用して hello.cc を実行可能ファイルにコンパイルします。
g++ hello.cc -o hello
生成されるバイナリーhello
がhello.cc
と同じディレクトリーにあることを確認します。 hello
バイナリー、つまりhello
を実行します。
4.1.6.3. シンプルなマルチファイルの使用
#include <stdio.h> void hello() { printf("Hello world!\n"); }
extern void hello(); int main() { hello(); return 0; }
手順4.3 マルチソースファイルによるプログラムのコンパイル
- 次のコマンドを使用して one.c を実行可能ファイルにコンパイルします。
gcc -c one.c -o one.o
生成されるバイナリーone.o
がone.c
と同じディレクトリーにあることを確認します。 - 次のコマンドを使用して two.c を実行可能ファイルにコンパイルします。
gcc -c two.c -o two.o
生成されるバイナリーtwo.o
がtwo.c
と同じディレクトリーにあることを確認します。 - 次のコマンドを使用して
one.o
およびtwo.o
を単一の実行可能ファイルにコンパイルします。gcc one.o two.o -o hello
生成されるバイナリーhello
がone.o
およびtwo.o
と同じディレクトリーにあることを確認します。 hello
バイナリー、つまりhello
を実行します。
4.1.6.4. 推奨される最適化オプション
指示のスケジューリングに正しいアーキテクチャーを選択することは、非常に重要です。デフォルトでは、GCC はほとんどの一般的なプロセッサーに最適化されたコードを作成しますが、コードが動作する CPU が分かっている場合は、それに対応する指示スケジューリングを最適化する -mtune=
オプションと、指示選択を最適化する -march=
オプションを使用すべきです。
-mtune=
は、指示スケジューリングを最適化して、ABI および利用可能な指示セットを除きすべてをチューニングすることでアーキテクチャーに適合させます。このオプションは特定の指示を選択しませんが、その代わりにプログラムのチューニングを行い、特定のアーキテクチャー上での実行を最適化します。たとえば、大体において Intel Core2 CPU を使うのであれば、-mtune=core2
を選択します。選択が間違っている場合は、プログラムは実行しますが、そのアーキテクチャー上での最善の動作にはなりません。そのプログラムが実行する可能性の最も高いアーキテクチャーを常に選択するようにします。
-march=
は、指示選択を最適化します。間違った選択をするとプログラムが失敗するので、正確な選択が重要になります。このオプションは、コード生成時に使用される指示セットを選択します。たとえば、プログラムが AMD K8 コアベースの CPU で実行されるのであれば、-march=k8
を選択します。このオプションでアーキテクチャーを指定することで、-mtune=
が暗示されます。
-mtune=
と -march=
コマンドは、異なるコード生成 (クロスコンパイル) のためではなく、あるアーキテクチャー内でのチューニングと指示選択のみに使用するようにします。たとえば、Intel 64 や AMD64 プラットフォームからの PowerPC コード生成には使用しません。
-march=
および -mtune=
の両方で利用可能なオプションの完全一覧については、GCC 4.4.4 Manual: Hardware Models and Configurations のリンクから取得可能な GCC ドキュメントを参照してください。
コンパイラーフラグ -O2
は、高速コード生成のすぐれた穏健なオプションです。作成されるコードサイズが大き過ぎない場合に、最適化されたコードが作成されます。最善のものがわからない場合は、このオプションを使用してください。
-O3
が推奨されます。このオプションはやや大きめのコードを作成しますが、関数のインラインの頻度が高いため、実行速度が速くなります。浮動小数点集約型コードではこれが理想的なものになります。
-Os
です。このフラグはサイズの最適化も行い、より小さいフットプリントがコードのローカリティを増やし、キャッシュミスを減らすような状況で高速コードを作成します。
-frecord-gcc-switches
を使用します。これにより、オブジェクトをオブジェクト自体にビルドする際に使用されるオプションが記録されます。オブジェクトがビルドされた後で、そのビルドに使用されたオプションセットを見つけ出します。このオプションセットはその後、オブジェクト内の .GCC.command.line
と呼ばれるセクションに記録され、以下のように確認可能になります。
$ gcc -frecord-gcc-switches -O3 -Wall hello.c -o hello $ readelf --string-dump=.GCC.command.line hello String dump of section '.GCC.command.line': [ 0] hello.c [ 8] -mtune=generic [ 17] -O3 [ 1b] -Wall [ 21] -frecord-gcc-switches
4.1.6.5. プロファイルフィードバックを使用した最適化ヒューリスティックのチューニング
- インライン
- 分岐予測
- 指示のスケジューリング
- 手順間の不断の伝達
- ホットもしくはコールド機能の区別
手順4.4 プロファイルフィードバックの使用
- プロファイル情報を作成するには、アプリケーションを
-fprofile-generate
でコンパイルしてインストルメント化する必要があります。 - アプリケーションを実行してプロファイル情報を蓄積し、保存します。
- アプリケーションを
-fprofile-use
で再コンパイルします。
手順4.5 プロファイリングフィードバックを使ったプログラムのコンパイル
source.c
をコンパイルし、プロファイリングインストルメンテーションを含めます。gcc source.c -fprofile-generate -O2 -o executable
executable
を実行してプロファイリング情報を収集します。./executable
- ステップ 1 で収集したプロファイリング情報を使って
source.c
を再コンパイル、最適化します。gcc source.c -fprofile-use -O2 -o executable
-fprofile-dir=DIR
でコンパイルします。この DIR
は作成先となるディレクトリーです。
警告
4.1.6.6. 64 ビットホスト上での 32 ビットコンパイラーの使用
glibc
、libgcc
、libstdc++
のサポート用ライブラリーを含める必要があります。Intel 64 および AMD64 では、以下のコマンドを実行します。
yum install glibc-devel.i686 libgcc.i686 libstdc++-devel.i686
db4-devel
を使用する場合、このライブラリーの 32 ビットバージョンは以下のコマンドでインストールできます。
yum install db4-devel.i686
注記
x86-64
ではなく) x86 プラットフォーム上の .i686
接尾辞は、当該パッケージの 32 ビットバージョンを指定します。PowerPC アーキテクチャーの場合、接尾辞は (ppc64
ではなく) ppc
になります。
-m32
オプションをコンパイラーとリンカーにパスして 32 ビットの実行可能ファイルが作成できます。64 ビットシステム上にサポート用の 32 ビットライブラリーがインストールされていれば、この実行可能ファイルは 32 ビットシステムと 64 ビットシステムの両方で実行できることになります。
手順4.6 64 ビットホスト上での 32 ビットプログラムのコンパイル
- 64 ビットシステム上では、以下のコマンドで
hello.c
を 64 ビットの実行可能ファイルにコンパイルします。gcc hello.c -o hello64
- 作成された実行可能ファイルが 64 ビットのバイナリーであることを確認します。
$ file hello64 hello64: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped $ ldd hello64 linux-vdso.so.1 => (0x00007fff242dd000) libc.so.6 => /lib64/libc.so.6 (0x00007f0721514000) /lib64/ld-linux-x86-64.so.2 (0x00007f0721893000)
64 ビットの実行可能ファイル上では、コマンドfile
の出力にELF 64-bit
が含まれ、ldd
はリンクされたメイン C ライブラリーとして/lib64/libc.so.6
をリスト表示します。 - 64 ビットシステム上では、以下のコマンドで
hello.c
を 32 ビットの実行可能ファイルにコンパイルします。gcc -m32 hello.c -o hello32
- 作成された実行可能ファイルが 32 ビットのバイナリーであることを確認します。
$ file hello32 hello32: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped $ ldd hello32 linux-gate.so.1 => (0x007eb000) libc.so.6 => /lib/libc.so.6 (0x00b13000) /lib/ld-linux.so.2 (0x00cd7000)
32 ビットの実行可能ファイル上では、コマンドfile
の出力にELF 32-bit
が含まれ、ldd
はリンクされたメイン C ライブラリとして/lib/libc.so.6
をリスト表示します。
$ gcc -m32 hello32.c -o hello32 /usr/bin/ld: crt1.o: No such file: No such file or directory collect2: ld returned 1 exit status
$ g++ -m32 hello32.cc -o hello32-c++ In file included from /usr/include/features.h:385, from /usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../include/c++/4.4.4/x86_64-redhat-linux/32/bits/os_defines.h:39, from /usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../include/c++/4.4.4/x86_64-redhat-linux/32/bits/c++config.h:243, from /usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../include/c++/4.4.4/iostream:39, from hello32.cc:1: /usr/include/gnu/stubs.h:7:27: error: gnu/stubs-32.h: No such file or directory
-m32
でビルドしても、プログラムを適応もしくは変換して 32/64 ビットの互換性から発生する問題の解決にはならないことに注意してください。移動可能なコードの書き込みと、32 ビットから 64 ビットへの変換に関するヒントは、Proceedings of the 2003 GCC Developers Summit の Porting to 64-bit GNU/Linux Systems を参照してください。
4.1.7. GCC のドキュメント
cpp
、gcc
、g++
、gcj
、gfortran
の各コマンドの man
ページを参照してください。