これまでの補足とIntel386

岸 哲夫

 今回は最適化オプションのまとめとLINK関連のオプションの補足,および出力の種類の制御オプションの補足,またIntel386とAMD x86-64オプションについて説明する. (筆者)

space

 本来は最適化オプションを付けなくとも理想的なコードを生成することがコンパイラの役目です.しかしそのようなレベルに達していない以上,使う側が理解して最適化オプションを付加してやらなければなりません.

 特にこだわらないのであれば,普遍的な最適化をコマンド・オプション一つで行うことが可能です.

-O

 この最適化オプションを付けることで,無理のない最適化を行うことができます.あまりコンパイル時間をかけず,メモリも消費しない程度に,コード・サイズと実行時間を減らす最適化を行います.なお,フレーム・ポインタなしでもデバッグをサポートできる機種では,-fomit-frame-pointerをオンにします.

 -O1オプションも意味は同じです.

 -Oは以下の最適化フラグを付けます.

  -fdefer-pop
  -fmerge-constants 
  -fthread-jumps
  -floop-optimize 
  -fcrossjumping 
  -fif-conversion 
  -fif-conversion2 
  -fdelayed-branch 
  -fguess-branch-probability 
  -fcprop-registers
-O2

 より高度な最適化を行います.結果としてコンパイル時にリソースを消費し,時間もかかることになります.やはりフレーム・ポインタなしでもデバッグをサポートできる機種では,-fomit-frame-pointerをオンにします.結果として,デバッグと干渉しない機種ではフレーム・ポインタが削除されます.

 -O2は-Oに加えて以下の最適化フラグを付けます.

 これは変数の定義が唯一になるように変数の名前を付け直しているわけです.変数aは通常の場合,一つのアドレスに収められる変数ですが,SSA形式に変換すると代入した数だけ,変数aが作られます.それによって共通部分式除去などの一般的最適化が容易になり,効率も上がるということです.

  -fforce-mem 
  -foptimize-sibling-calls 
  -fstrength-reduce 
  -fcse-follow-jumps  
  -fcse-skip-blocks 
  -frerun-cse-after-loop  
  -frerun-loop-opt 
  -fgcse   
  -fgcse-lm   
  -fgcse-sm 
  -fdelete-null-pointer-checks 
  -fexpensive-optimizations 
  -fregmove 
  -fschedule-insns  
  -fschedule-insns2 
  -fsched-interblock 
  -fsched-spec 
  -fcaller-saves 
  -fpeephole2 
  -freorder-blocks  
  -freorder-functions 
  -fstrict-aliasing 
  -falign-functions  
  -falign-jumps 
  -falign-loops  
  -falign-labels
-O3

 より高度な最適化を行います.

 -O3は-O,-O2に加えて以下の最適化フラグを付けます.

  -finline-functions 
  -frename-registers
-O0

 これがデフォルトの値です.GDBの動作がおかしいような場合,このオプションを付けてください.

 このオプションでは最適化を行いません.

-Os

 サイズを小さくする最適化を行います.-O2最適化の中で,コード・サイズを増大させない最適化を行います.それに加えて次のような最適化を無効にします.

  -falign-functions  
  -falign-jumps  
  -falign-loops 
  -falign-labels  
  -freorder-blocks  
  -fprefetch-loop-arrays
リンクの際に使用するオプションに
ついての補足

 GCCでコンパイルしたコードは,スタートアップ・コードを設定したり,必要なライブラリを呼び出すために内部処理としてリンクを行っています.

 その際のオプションについて説明します.

object-file-name

 特別な拡張子が付かないファイル名は,オブジェクト・ファイルの名前,もしくは,ライブラリの名前とみなされます.オブジェクト・ファイルとライブラリとの区別は,リンカが自動認識します.リンク処理が行われる場合,これらのオブジェクト・ファイルはリンカへの入力として使われます.

  gcc xxx.c xx1.o xx2 -o test
のようなコマンドの場合,xxx.cがコンパイルされ,xx1.oとライブラリまたはオブジェクト・ファイルであるxx2とともにリンクされて実行形式のtestというファイルができます.

-c

 ソース・ファイルのコンパイルやアセンブルを行いますが,リンクは行いません.出力されるものは個々のソース・ファイルに対応するオブジェクト・ファイルとなります.デフォルトでは,ソース・ファイルに対応するオブジェクト・ファイルの名前は,ソース・ファイル名の拡張子‘.c’,‘.i’,‘.s’などを‘.o’に置き換えることによって作られます.

 拡張子が認識されない入力ファイルは,コンパイルもアセンブルも必要とされないので,無視されます.

-S

 アセンブラ・ソースの生成のみを行い,アセンブル処理を実行しません.出力されるものは,指定された入力ファイルのうちアセンブラ・コードで記述されていないものに対するアセンブラ・コードのファイルとなります.

 デフォルトでは,ソース・ファイルに対応するアセンブラ・ファイルの名前は,ソース・ファイル名の拡張子‘.c’,‘.i’などを‘.s’に置き換えることによって作られます.

 コンパイルの必要がない入力ファイルは無視されます.

-E

 プリコンパイルのみを行います.出力形態は,前処理済みのソース・コードとなり,これは標準出力に出力されます.前処理の必要がない入力ファイルは無視されます.

 上のオプション-C,-S,-Eは,事実上リンカを実行しません.

-llibrary,-l library

 リンク時に,libraryにより指定される名前のライブラリを探す処理を行います.

 たとえばマルチスレッドでpthreadライブラリを使う場合に,

  gcc -lpthread test201.c -o test201

のように指定します(本連載の第15回を参照).

 コマンドの中のどこにこのオプションを指定するかによって違いが出てきます.

 リンカは,ライブラリとオブジェクト・ファイルを,それらが指定された順番に探して処理します.したがって,

  foo.o -lz bar.o
と指定すると,ライブラリ‘z’は,ファイル‘foo.o’よりも後に探されますが,ファイル‘bar.o’よりは前に探されます.もし,‘bar.o’が‘z’の中の関数を参照しているとすると,その関数は組み込まれません.リンカは,標準ディレクトリをサーチして,ライブラリを探します.ライブラリとは,実際には‘liblibrary.a’という名前のファイルです.

 サーチされるディレクトリには,標準システム・ディレクトリに加えて,‘-L’によって指定された任意のディレクトリが含まれます.通常,このような方法で見つかるファイルはライブラリ・ファイルです.

 リンカは,アーカイブ・ファイルの中を調べて,参照されているのに定義されていないシンボルを定義しているメンバを探します.見つかったファイルが通常のオブジェクト・ファイルであれば,それは通常の方法によってリンクされます.‘-l’オプションを使うことと,ファイル名を指定することの唯一の違いは,‘-l’オプションの場合には,libraryの前後に‘lib’と‘.a’とが付加され,サーチ対象のディレクトリがサーチされるという点です.

-lobjc

 Objective Cで作ったものをリンクする際に必要になります.Mac OS Xなどでは使用することもあるかと思います.

-nostartfiles

 リンク時に標準システム・スタートアップ・ファイルを使いません.通常は標準システム・ライブラリが使用されますが,オプション-nostdlibや-nodefaultlibsが使用された場合には使用されません.

 組み込み用途では,標準システム・スタートアップ・ファイルを使用しないこともあります.

-nodefaultlibs

 リンク時に標準システム・ライブラリを使いません.指定されたライブラリだけがリンカに渡されます.標準スタートアップ・ファイルが,-nostartfilesが使われない限り,通常は使用されます.

 コンパイラが,System V(およびANSI C)環境においてmemcmp,memset,およびmemcpyに対する呼び出しを生成すること,また,BSD環境においてbcopyやbzeroに対する呼び出しを生成することがあるかもしれません.これらのエントリは,通常はlibcのエントリによって解決されます.このオプションが指定された場合には,これらのエントリ・ポイントはほかのメカニズムを通して供給されるべきです.

出力の種類の制御で使用する
オプションについての補足

 コンパイルの過程は,四つの段階に分けることができます.プリプロセス,コンパイル,アセンブル,リンクです.

 前の三つの段階は個々のソース・ファイルに対して適用され,オブジェクト・ファイルの生成で終わります.リンクでは,すべてのオブジェクト・ファイルを組み合わせて,一つの実行形式ファイルを作ります.

 指定された入力ファイルそれぞれに対し,拡張子からコンパイル過程のどれが実行されるかが決定されます.

 以下のような対応になります.

  • hogehoge.f
  • hogehoge.for
  • hogehoge.FOR

 これらはFortranのソース・ファイルです.これはプリプロセスを必要としません.

  • hogehoge.F
  • hogehoge.fpp
  • hogehoge.FPP

 これらもFortranのソース・ファイルです.しかし,これはプリプロセスを使います.cpp(Cのプリプロセッサ)を使い,#define,#includeを使うことができます.C形式の/* */のコメントも可能になります.

hogehoge.r

 RATFORプリプロセッサ(GCCに含められていない)によって前処理されなければならないFortranのソース・コードです.

 通常使われるのは,ratfor77のプリプロセッサで,方言の一つであるRational Fortranを通常のFortran 77に変換します.そして,出力はGCCでコンパイルできます.

hogehoge.ads

 宣言に新しく名前を付けるライブラリ・ユニット宣言,またはライブラリ・ユニットを含んでいるAdaのソース・コード・ファイルです.このファイルは別名スペックと呼ばれています.Cでいうヘッダのようなものです.

hogehoge.adb

 ライブラリ・ユニット・ボディを含んでいるAdaのソース・コード・ファイルです.このファイルはボディと呼ばれています.

--target-help

 ターゲットのCPUの具体的なコマンドライン・オプションの説明を表示します.

 図1のような内容を表示します.

--version

 バージョンを表示します.

--help

 GCCのヘルプを図2のように表示します.

Intel386とAMD x86-64の
オプションについて

 広い意味でのIntel互換マシンで使用するオプションについて説明します.

-mcpu=

 指定したCPUに対して最適化します,次のCPUに対応しています.

 i386 i486 i586 i686 pentium pentium-mmx pentiumpro pentium2 pentium3 pentium4 prescott nocona k6 k6-2 k6-3 athlon athlon-tbird athlon-4 athlon-xp athlon-mp winchip-c6 winchip2 c3

 3のようなコマンドを実行してそれぞれの生成コードを比較してみます(リスト1リスト21,それぞれ最初の20行のみ).

 ここでlibmng_pixels_i386.sは空になります.これは,何も指定しなければデフォルトとしてi386が指定されるからです.

-m386,-m486,-mpentium,-mpentiumpro

 それぞれのCPUで最適化されますが,-mpcu=xxxを使うことが推奨されています.

-mfpmath=unit

 指定した浮動小数点プロセッサのためのコードを生成します.

  • 387

 標準の387浮動小数点コプロセッサまたはエミュレートされたものを使います.このオプションによってコンパイルされたコードはほとんどどこでも動くはずです.80ビットの精度で計算されます.

 これはi386コンパイラのためのデフォルトです.

  • sse

 SSE命令セットに存在するスカラ浮動小数点手順を使います.この命令セットはAthlon4,Athlon XP,およびAthlon MPチップによるAMDラインの中でPentium3と,これより新しいチップによってサポートされます.SSE命令セットの初期バージョンは単精度だけをサポートし,拡張された精度は387が使われます.最近のバージョンでは,Pentium4やAMD x86-64チップを使うと倍精度をサポートしています.

 i387のためにSSE拡張を可能にし,このオプションを効果的にするために-march=CPUタイプ,-msse,または-msse2スイッチを使う必要があります.x86-64コンパイラの場合,これらの拡張はデフォルトです.

 生成されたコードはより速く動作し,387用に生成されたコードより安定しています.しかし80ビットの精度として設計された既存のコードは動作しないかもしれません.

 これはx86-64コンパイラのためのデフォルト選択です.

  • sse,387

 両方の命令セットを利用できます.しかし,これはまだ実験的なものなので,注意してこのオプションを使ってください.

*          *

 次回は今回に続いてIntel386とAMD x86-64のオプションについて説明します.次回でGCCのオプションに関する説明は終わります.


NEW記事内インデックス    連載インデックスはこちら   Interfaceのトップ
◆これまでの補足とIntel386

リスト

Copyright 2005 岸 哲夫

Copyright 1997-2005 CQ Publishing Co.,Ltd.