Chapter 11 ネイティブコードコンパイラ (ocamlopt)
このページは最後に更新されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
last mod. 2008-10-01 (水) 14:59:00
The Objective Caml system release 3.10
この章では Objective Caml の高パフォーマンスネイティブコンパイラ ocamlopt の解説をします。ocamlopt は Caml のソースファイルをネイティブコードのオブジェクトファイルにコンパイルしたり、そのオブジェクトファイルをリンクして実行可能なファイルを作成したりできます。
ネイティブコードコンパイラは特定のプラットフォームでしか使用できません。ネイティブコードは ocamlc で生成したバイトコードより実行速度が速い代わりに、コンパイルに時間がかかり実行ファイルのサイズが大きくなります。バイトコードコンパイラとの互換性が非常に高く、同一のソースコードを ocamlc と ocamlopt でコンパイルすると同じように動作するはずです。
ocamlc で生成したネイティブコードオブジェクトファイルと ocamlopt で生成したバイトコードオブジェクトファイルを一緒にすることはできません。プログラムはすべて ocamlopt でコンパイルするか、すべて ocamlc でコンパイルするかのどちらかにしてください。ocamlopt で生成したネイティブコードオブジェクトファイルを、対話式システム ocaml でロードすることはできません。
Overview of the compiler
ocamlopt コマンドは、ocamlc と非常によく似たコマンドラインインターフェイスになっています。引数にはいくつか型があります。
- .mli で終わる引数はコンパイルユニットのインターフェイスのソースファイルと認識されます。インターフェイスはコンパイルユニットが外部に公開する名前を指定します。値の名前とその型を宣言し、公開データ型を定義し、抽象データ型を宣言します。ocamlopt はファイル x.mli なら、ファイル x.cmi を生成し、インターフェイスをコンパイルしたものを出力します。生成されるインターフェイスはバイトコードコンパイラ ocamlc が生成するものと同一です。
- .ml で終わる引数はコンパイルユニットの実装のソースファイルとして認識されます。実装はユニットが外部に公開する名前の定義を持ち、副作用のある式なども持ちます。ocamlopt はファイル x.ml なら、ファイル x.o と x.cmx を生成します。前者はネイティブオブジェクトコードで、後者はユニットを使う側にリンクや最適化の追加情報を伝えるファイルです。コンパイル済みの実装は常に x.cmx の名前で参照されます (.o で参照すると、ocamlopt は Caml をコンパイルしたコードではなく、C をコンパイルしたコードが入っていると認識します) 。実装は (あればですが) インターフェイスファイル x.mli を元にチェックされます。このあたりのことは ocamlc のマニュアルで解説されています (8 章) 。
- .cmx で終わる引数はコンパイル済みオブジェクトコードと認識されます。これらのファイルは (もしあれば) .ml 引数をコンパイルして得られたオブジェクトファイルや Objective Caml 標準ライブラリと一緒にリンクされて、ネイティブコード実行ファイルが生成されます。コマンドラインでの .cmx や .ml の引数の順番には意味があります。コンパイルユニットはランタイム時その順で初期化され、初期化前のユニットのコンポーネントを使おうとしたらリンク時にエラーとなります。なのでファイル x.cmx はユニット x に参照を持つファイル .cmx より前に置かなければなりません。
- .cmxa で終わる引数はオブジェクトコードのライブラリと認識されます。ライブラリは 2 つのファイル (lib.cmxa と lib.a) にまとめられています (それぞれ .cmx/.o のオブジェクトファイルの集合です)。ライブラリは ocamlopt -a で作成できます (-a オプションの説明は下の方を見て下さい) 。ライブラリ中のオブジェクトファイルは通常の .cmx と同じようにリンクされます (順番はライブラリ作成時に指定します) 。ただ 1 つ異なる点として、ライブラリ中のオブジェクトファイルでプログラム中のどこからも参照されないものはリンクされません。
- .c で終わる引数は C コンパイラを通して .o オブジェクトファイルを生成します。このオブジェクトファイルはプログラムにリンクされます。
- .o 、.a 、.so (Windows では.obj 、.lib 、.dll) で終わる引数は C のオブジェクトファイルやライブラリとみなされます。これもプログラムにリンクされます。
リンク段階で出力されるファイルは通常の UnixやWindowsの実行ファイルです。実行に ocamlrun は必要ありません。
Options
ocamlopt に認識されるコマンドラインオプションは以下の通りです。
- -a
- コマンドラインに与えられたオブジェクトファイル (.cmx/.o) をとり、ライブラリ (.cmxa/.a) を作成します。実行ファイルは生成されません。ライブラリの名前は -o オプションで指定できます。デフォルトでは library.cmxa です。
コマンドラインに -cclib や -ccopt オプションが渡されたら、これらのオプションは出力される .cmxa ライブラリに保存され、このライブラリをリンクするときに自動的に -cclib や -ccopt が追加されます (ただしリンク時に -noautolink が指定されない場合) 。
- -c
- コンパイルのみ行います。リンクを行いません。ソースコードファイルをコンパイル済みファイルに変換しますが、実行ファイルは生成されません。このオプションはモジュールの分割コンパイル時に使用できます。
- -cc ccomp
- 最終的な実行ファイルを生成する C リンカや .c ソースファイルをコンパイルする C コンパイラとして ccomp を使用します。
- -cclib -llibname
- C リンカにオプションとして -llibnameを渡し、指定された C ライブラリをプログラムにリンクします。
- -ccopt option
- C コンパイラやリンカに与えられたオプションを渡します。例えば、-ccopt -Ldir なら C リンカは C ライブラリを探す際ディレクトリ dir を見るようになります。
- -compact
- 生成するコードを実行時間よりサイズに対して最適化します。プログラムはいくらか小さくなりますが実行速度が遅くなります。デフォルトではスピードに対して最適化します。
- -dtypes
- 詳細な型情報をダンプします。x.mlの情報はx.annotにダンプされます。型エラーがある場合は、エラーより前の段階で型チェッカーが推論したものになります。
emacs/caml-types.elは、x.annotファイルを使い対話的に型を表示できます。
- -for-pack module-path
- -packによって生成するコンパイル単位に含めるためのオブジェクトファイル(.cmx/.oファイル)を生成します。たとえば、ocamlopt -for-pack P -c A.mlはa.cmxとa.oを生成します。このファイルは、ocamlopt -pack -o P.cmx a.cmxとして使うことができます。
- -g
- コンパイル時とリンク時にデバッグ情報を追加します。このオプションは、プログラムがcatchされない例外によってプログラムが終了した際に、スタックトレースを表示する必要があります。(see section 10.2).
- -i
- 実装ファイル (.ml) をコンパイルする際、定義されている名前をすべて型と定義付きで表示します。コンパイラがどのように型推論をしているかチェックするときに使用できます。また出力はインターフェイスの文法に従っているので、インターフェイスファイル (.mli) を書くときの手助けになります。標準出力を .mli ファイルにリダイレクトして、非公開な名前の宣言を消していくだけです。
- -I directory
- コンパイル済みインターフェイスファイル (.cmi) 、コンパイル済みオブジェクトコードファイル (.cmx) 、ライブラリ (.cmxa) 、-cclib -lxxx で指定される C ライブラリを検索する際検索しに行くディレクトリのリストに与えられたディレクトリを追加します。デフォルトでは、まずカレントディレクトリが、それから標準ライブラリディレクトリが検索されます。-I で追加されたディレクトリは、カレントディレクトリより後に、標準ライブラリディレクトリより前に、コマンドラインに与えられた順に検索されます。
+ で始まるディレクトリ名は、標準ライブラリディレクトリからの相対パスとみなされます。例えば -I +labltk は標準ライブラリのサブディレクトリ labltk を検索パスに追加します。
- -inline n
- インライン化の度合いを正整数 n に設定します。-inline 0 と指定すると、どの関数もインライン化されません (ただし関数の本体が呼び出し動作より小さい場合を除きます) 。これでインライン化がコードサイズを大きくすることはありません。デフォルトでインライン化の度合いは -inline 1 に設定されています。これだと多少大きな関数でもインライン化するため、コードサイズがいくらか大きくなります。-inline オプションで高い値を設定すると、より大きな関数でもインライン化の候補となるため、コードサイズは相当に大きくなります。
- -linkall
- ライブラリに含まれるモジュールをすべて強制的にリンクします。このフラグが与えられなかったら、参照されないモジュールはリンクされません。ライブラリを作成するときに (-a flag) これを与えると、そのライブラリを使用するプログラムをリンクする際、ライブラリ内のモジュールはすべて強制的にリンクされます。
- -noassert
- assertion のチェックをやめます。assertion はコンパイルされません。すでにコンパイルされたファイルをリンクする場合には、このフラグは何の意味も持ちません。
- -noautolink
- .cma ファイルをリンクするとき、ライブラリが潜在的に持つ -cclib や -ccopt といったオプション (ライブラリを作成するときに指定されたもの) を無視します。ライブラリが間違った C ライブラリや C オプションの指定をしているときに使用できます。この場合、リンクの際コマンドラインで -noautolink を指定して、正しい C ライブラリやオプションを指定し直してください。
- -nolabels
- 型の中でオプションではないラベルを無視します。適用にラベルは使用できません。パラメータの順序が絶対になります。
- -o exec-file
- リンカが出力するファイルの名前を指定します。デフォルトの出力名は Unix の伝統に則り a.out です。-a が指定されている場合は生成されるライブラリ名の指定になります。-output-obj が指定されている場合は生成されるファイル名の指定になります。
- -output-obj
- リンカは実行ファイルではなく C オブジェクトファイルを生成します。C のプログラムから呼べるように、Caml のコードを C のライブラリとしてラップするのに使います。18 章のセクション 18.7.5 を見てください。出力されるオブジェクトファイルの名前はデフォルトで camlprog.o です。-o オプションで変更可能です。
- -p
実行されたときにプロファイル情報を書き出すコードを追加したプログラムを生成します。このプロファイル情報は解析プログラム gprof で調べることができます (プロファイリングについて詳しくは 17 章を見てください) 。-p オプションはコンパイル時にもリンク時にも与えてください。-p オプションなしでコンパイルされたオブジェクトファイルをリンクすることもできますが、プロファイリングが不正確になってしまいます。
Unix:
プロファイルについて詳しくは gprof(1) の Unix マニュアルを見てください。
gprof を完全にサポートしているのは一部のプラットフォームだけです (今のところ Intel x86/Linux と Alpha/Digital Unix) 。その他のプラットフォームでは -p オプションを付けてもプロファイルは不正確なものになります (コール・グラフ情報はなく、時間のプロファイルだけです) 。
Windows:
Windows では -p オプションは動きません。
- -pack
- コマンドラインで与えられた .cmx オブジェクトファイルをまとめて、オブジェクトファイル (.cmx/.o) とそのコンパイル済みインターフェイス (.cmi) を生成します。元のオブジェクトは出力された .cmx ファイルのサブモジュールとなります。出力される .cmx ファイルの名前は -o オプションで必ず与えてください。 例えば、
ocamlopt -pack -o P.cmx A.cmx B.cmx C.cmx
はP.cmxとP.o、P.cmiを生成します。それらは 3 つのサブモジュール A 、B 、C を持ち、それぞれオブジェクトファイル A.cmx 、B.cmx 、C.cmx の内容に対応します。他のプログラムからは、P.A 、P.B 、P.C などとしてこれらの内容を参照することができます。
結合される.cmxオブジェクトファイルは、適切な-for-packオプションとともにコンパイルされる必要があります。上記の例では、A.cmx、B.cmx、C.cmxはocamlopt -for-pack Pでコンパイルされなければなりません。
複数レベルのpackingは、-packと-for-pack組み合わせて実現されます。.例は以下の通りです。
ocamlopt -for-pack P.Q -c A.ml
ocamlopt -pack -o Q.cmx -for-pack P A.cmx
ocamlopt -for-pack P -c B.ml
ocamlopt -pack -o P.cmx Q.cmx B.cmx
結果のP.cmxオブジェクトファイルはP.Q、P.Q.A、P.Bをサブモジュールとして含みます。
- -pp command
- コンパイラは与えられたコマンド command をプリプロセッサとしてすべてのソースファイルに適用します。command の出力は一時ファイルにリダイレクトされてからコンパイルされます。コンパイルでエラーが発生しなかったら一時ファイルは削除されます。ソースファイルのベース名と拡張子 .ppi (インターフェイス .mli の場合) もしくは .ppo (実装 .ml の場合) から構成される名前が一時ファイルの名前になります。
- -principal
- 型チェックの際 information path をチェックします。すべての型が principal way で導出されているか確認します。-principal モードでコンパイルの通るプログラムは必ずデフォルトモード (equivalent type) でもコンパイルが通ります (ただしバイナリの signature は異なります) 。
- -rectypes
- 型チェックの際、任意の再帰的な型を許します。デフォルトでは、オブジェクト型を経由する再帰による再帰的な型しかサポートされません。
- -S
- コンパイルの際生成されるアセンブリコードを保存します。ソースファイル x.ml のアセンブリコードは x.s というファイルに保存されます。
- -thread
- 24 章で解説される threads ライブラリと組み合わせてマルチスレッドプログラムをコンパイル、リンクします。このオプションを付けると、特別なスレッドセーフな標準ライブラリを選択するようになります。
- -unsafe
- 配列や文字列アクセスのバウンダリチェックをオフにします (v.(i) や s.[i]) 。-unsafe でコンパイルされたプログラムはいくらか速くなりますが安全ではありません。プログラムが配列や文字列のバウンド外をアクセスした場合何が起こるかわかりません。
- -v
- コンパイラのバージョンと標準ライブラリディレクトリを表示して終了します。
- -verbose
- 外部コマンドを実行する前に、そのコマンドを表示します。特にC コンパイラやリンカの起動を表示します。
- -version
- コンパイラのバージョンを短い表記 (3.06 みたいに) 表示して終了します。
- -w warning-list
- Enable or disable warnings according to the argument
warning-list. The argument is a string of one or several characters, with
the following meaning for each character:
- A/a
- すべての警告を有効/無効にする
- C/c
- コメントかどうか疑わしいものに対する警告を有効/無効にする
- D/d
- 推奨されていない (deprecated) 機能に対する警告を有効/無効にする
- E/e
- 脆弱なパターンに対する警告を有効/無効にする。(バリアントにコンストラクタが追加されても、成立するmatch式)
- F/f
- 関数の部分適用に対する警告を有効/無効にする (つまり f x; expr のようなコードで、適用 f x が関数型を持つようなとき).
- L/l
- 適用でラベルが省略されていることに対する警告を有効/無効にする
- M/m
- メソッドのオーバーライドに対する警告を有効/無効にする
- P/p
- 部分マッチ (パターンマッチでどれにもマッチしない場合があるもの) に対する警告を有効/無効にする
- S/s
- 文が unit 型以外を持つことに対する警告を有効/無効にする (例えば expr1; expr2 のようなコードで、expr1 が unit 以外の型を持つようなとき)
U/u|使用されない (冗長な) マッチケースに対するに対する警告を有効/無効にする
- V/v
- 隠れたインスタンス変数に対する警告を有効/無効にする
- Y/y
- letやasキーワードで束縛され、_で始まっていない変数の未使用に対する警告を有効/無効にする。
- Z/z
- _で始まっていない変数の未使用に対する警告を有効/無効にする
- X/x
- その他の警告すべてを有効/無効にする
デフォルトでは -w Aelyz になります (脆弱なパターン、ラベル以外、未使用の変数以外はすべて有効) 。
- -warn-error warning-list
- 引数 warning-list で示される警告をエラーとして扱います。コンパイラはそのような警告が発生したらすぐにエラーとして停止して処理をやめます。warning-list の意味は -w オプションと同じで、大文字だと該当する警告をエラーとし、小文字だとエラーとしません。デフォルトでは -warn-error a になります (どの警告もエラーとしては扱わない) 。
- -where
- 標準ライブラリの位置を表示して終了します。
IA32アーキテクチャオプション
IA32コードジェネレータ(Intel Pentium, AMD Athlon)は以下の追加オプションをサポートします。
- -ffast-math
- 三角関数と指数関数をライブラリルーチンではなくIA32命令を用いて計算します。
影響を受ける関数は、atan, atan2, cos, log, log10, sin, sqrt,tanです。
生成されたコードはより高速ですが、サポートする引数の範囲と結果の精度が落ちます。
特に、cos, sin, tanの範囲は[-2^64,2^64]まで落ちます。
Sparcアーキテクチャオプション
Sparcコードジェネレータは以下の追加オプションをサポートします。
- -march=v8
- SPARC version 8のコードを生成します
- -march=v9
- SPARC version 9のコードを生成します
デフォルトでは全てのSPARCプロセッサで動作するSPARC version 7のコードを生成します。
Common errors
エラーメッセージはほとんど ocamlc と同じです。セクション 8.4 を見てください。
Running executables produced by ocamlopt
ocamloptで生成された実行ファイルは、ネイティブで静的リンクされており、直接起動が可能です。ocamlrunのバイトコードの実行システムに依存していません。
ocamloptで生成された実行ファイルは、実行時に以下の環境変数を読みます。
- OCAMLRUNPARAM
- ocamlrun(10.2参照)と同様の使い方です。ただし、Iが無視される点(OSのスタックサイズが変わりに用いられます)と、bが無視される点(catchされない例外が発生された場合も、スタックとレースが表示されません)が異なります。
- CAMLRUNPARAM
- OCAMLRUNPARAMが見つからない場合、CAMLRUNPARAMが代わりに使われます。CAMLRUNPARAMも見つからない場合、デフォルト値が用いられます。
Compatibility with the bytecode compiler
このセクションではバイトコードコンパイラとネイティブコードコンパイラの非互換性を解説します。以下に述べる点を除いては、これらのコンパイラが生成するコードは同じように動作するはずです。
- シグナルはプログラムがヒープに割り当てを行っていないと検出されません。つまり、コードの一部が割り当てられていないときにシグナルが配送されてきても、次のヒープ割り当てまでハンドラは呼ばれません。
- 深すぎる再帰によって引き起こされることが多いスタックオーバーフローは、以下の方法のいずれかで補足されます。どれが用いられるかはプラットフォームに依存します。-- バイトコードコンパイラと同様に、Stack_overflow例外が投げられる。(IA32/Linux, AMD64/Linux, PowerPC/MacOSX, MS Windows 32-bit ports).
- "segmentation fault"シグナルによってプログラムが終了する。 (そのほかのUnix systemsシステム)
- 何も起こらずに終了する。(MS Windows 64 bits).
- IA32プロセッサ(Intel Pentium, AMD Athlon, etc, in 32-bit mode)において、浮動少数計算の結果が、倍精度に丸められず、拡張精度で保持されることがあります。なので、浮動少数の結果が、バイトコードとネイティブコードで異なります。一般的に、ネイティブコードで得られる結果は、"より正確"です。(丸め誤差さ精度落ちの影響を受けることが少ない)
- Alphaプロセッサにおいて、無限や非正規数を扱う浮動小数演算が、"floating-point exception"シグナルで終了することがあります