ここより先はハックする者の領域である。繰り返す。ここより先はハックする者の領域である。(警告したよ)
-ddump-
pass
過程<pass>
(頻繁に使われるものについては短縮形がある)の後にデバッグ情報を出力する。-v5
を使うことでこれら全てを一遍に得られる。(大量の出力になる)。あるいは、-v4
を使うことで大部分を得られる。-ddump-to-file
を渡すと、標準出力の邪魔をするのを防ぐことができる。特に重要なのは下記の通りである。
-ddump-parsed
:
パーサの出力
-ddump-rn
:
名前変更器(renamer)の出力
-ddump-tc
:
型検査器の出力
-ddump-splices
:
接合するTemplate Haskellの式と、その評価結果のHaskellコードを出力する。
-ddump-types
:
モジュールのトップレベルで定義されている全ての値について型シグネチャを出力する。この一覧は辞書順にソートされている。-dppr-debug
を使うと、インポートされたもの及びシステム定義のものの型シグネチャも出力する。これはコンパイラをデバッグするときに便利である。
-ddump-deriv
:
自動導出インスタンス
-ddump-ds
:
脱糖器の出力
-ddump-spec
:
特殊化過程の出力
-ddump-rules
:
このモジュールで指定された書き換え規則を全て出力する。7.21.6. 書き換え規則の中で何が起こるか制御するを見よ。
-ddump-rule-firings
:
このモジュールで発動した規則の名前を全て出力する
-ddump-rule-rewrites
:
このモジュールで発動した全ての規則に関して詳細な情報を出力する
-ddump-vect
:
ベクトル化器の出力を表示する。
-ddump-simpl
:
単純化器の出力(「コアからコアへ」の過程)
-ddump-inlinings
:
単純化器からのインライン化情報
-ddump-cpranal
:
CPR解析の出力
-ddump-stranal
:
正格性解析器の出力
-ddump-strsigs
:
正格性シグネチャ
-ddump-cse
:
CSE過程の出力
-ddump-worker-wraper
:
worker/wrapper分割の出力
-ddump-occur-anal
:
「出現解析」の出力
-ddump-prep
:
コアpreparation過程の出力
-ddump-stg
:
「STGからSTGへ」過程の出力
-ddump-flatC
:
平坦化された(flattened)抽象C
-ddump-cmm
:
C--のコードを表示する。
-ddump-opt-cmm
:
C--からC--への最適化過程の結果を表示する。
-ddump-asm
:
ネイティブコード生成器が出力するアセンブリ言語
-ddump-llvm
:
LLVMコード生成器が出力するLLVMコード
-ddump-bcos
:
バイトコードコンパイラの出力
-ddump-foreign
:
foreign exportのスタブを出力
-ddump-simpl-phases
:
単純化器の毎回の出力を見せる。-dverbose-core2core
でも足りないときに使う。
-ddump-simpl-iterations
:
単純化器の毎回の繰り返し毎の出力を見せる。(単純化器の一回の実行に対して繰り返し回数の最大値が決まっている。通常4)。これは-ddump-simpl-phases
よりさらに多くの情報を出力する。
-ddump-simpl-stats
変換の種類毎に、何回実行されたかの統計情報を出力する。-dppr-debug
を追加すればより詳細な情報が得られる。
-ddump-if-trace
インタフェースローダが自分のやっていることを*事細かに*報告するようにする。
-ddump-tc-trace
型検査器が自分のやっていることを*事細かに*報告するようにする。
-ddump-vt-trace
ベクトル化器が自分のやっていることを*事細かに*報告するようにする。
-ddump-rn-trace
名前変更器が自分のやっていることを*事細かに*報告するようにする。
-ddump-rn-stats
名前変更器がどんな種類の情報を収集しなければならなかったかについての要約を表示する。
-dverbose-core2core
,
-dverbose-stg2stg
中間の「コアからコアへ」および「STGからSTGへ」過程の出力をそれぞれ表示する。(たくさんの出力になるよ!)。なので、本当に自暴自棄になったときには、次のように打つ。
% ghc -noC -O -ddump-simpl -dverbose-core2core -dcore-lint Foo.hs
-dshow-passes
それぞれの過程を、発生するごとに表示する。
-ddump-core-stats
最適化パイプラインの最後に、コア言語のプログラムの大きさの要約を一行で表示する。
-dfaststring-stats
コンパイラによるfast stringの使用についての統計情報を表示する。
-dppr-debug
デバッグ出力にはいくつかの「様式」がある。例えば型を表示する場合、「利用者」様式(デフォルト)では、コンパイラが内部的に持っている型についての情報をできる限りHaskellソースファイルの構文に従って表す。「デバッグ」様式で型が表示されるときには、明示的なforallが付き、変数には固有IDが添付される。(このため、一見同じだが実は異なるものを見分けることができる)。このフラグは、デバッグ出力を、より多弁なデバッグ様式で行うようにする。
-dppr-user-length
エラーメッセージにおいて、式は決まった「深さ」までしか表示されず、それを超えた部分式は省略記号になる。このフラグはその深さを決めるものである。デフォルト値は5。
-dppr-colsNNN
デバッグ出力の幅を設定する。コードが過度に折り返されるときにはこれを使うと良い。例えば、-dppr-cols200
。
-dppr-case-as-let
選択肢が一つであるcase式を、正格なlet式であるかのように表示する。これは、大量のボックス外しを伴うコードを扱うときに助けになる。
-dno-debug-output
求められた以外のデバッグ出力を全て抑制する。GHCがDEBUG
オプション付きでビルドされているとき、開発者にとって興味のあるデバッグ情報を出力することがある。この余分な出力がテストフレームワークを混乱させ、嘘のテスト失敗を引き起すことがあるので、これを無効にするためにこのフラグがある。
-dsuppress-all
抑制可能なものを全て抑制する。ただし、出力を曖昧にすることが多いので、固有識別子だけは抑制しない。コードの全体的な構造を知りたいだけなら、ここから始めると良い。
-dsuppress-uniques
固有子の印字を抑制する。これによって、表示結果が曖昧になることがある(例えば、ある「x」の出現がどこで束縛されているのか不明瞭になる)が、コンパイラを二回実行したときに出力に無駄な違いが出ることを減らせるので、diffをまともに適用できるようになる。diffによってどこを見るべきか分かったら、-dsuppress-uniques
なしでもう一回試せばよい。
-dsuppress-idinfo
識別子の束縛地点に表示される、その識別子の拡張情報を抑制する。これには正格性情報とインライン化器のテンプレートが含まれる。インライン化器のテンプレートを省くことで、このフラグを使うとコア出力の大きさを半減させることができる。
-dsuppress-module-prefixes
Core出力の中でモジュール修飾接頭辞を表示するのを抑制する。Data.List.length
ならData.List
の部分である。
-dsuppress-type-signatures
型シグネチャの印字を抑制する。
-dsuppress-type-applications
型適用の印字を抑制する。
-dsuppress-coercions
型適用の印字を抑制する。
-dcore-lint
GHC中の重量級パス内正気度チェックをコア水準で有効にする。(これがチェックするのはGHCの正気であって、あなたのではない)
-dstg-lint
:
同じことをSTG水準で行う。(注意: 現在機能していない)
-dcmm-lint
:
同じことをC--水準で行う。
-ddump
系フラグ由来の)の読み方例を通して見ていくことにしよう。以下のコードに対して-ddump-ds
することを考える。
skip2 m = m : skip2 (m+2)
始める前に、名前について少々。GHC内部では、変数や型構築子などは「固有子(unique)」によって識別される。固有子は「文字」と「数値」(両方とも広く解釈する)をつなげた形をしている。「文字」部分はその固有子がどういう由来を持つのかを示す。例えば、_
は「組込みの型変数」を意味し、t
は「型検査器由来」、s
は「単純化器由来」という具合である。「数値」部分は「base-62」形式でとてもコンパクトに表示されるが、私(WDP)以外の皆はこれが嫌いなようだ。
もう一度言うが、あらゆるものは「固有子」を持っていて、これは通常デバッグ時になんらかの形で出力される。ではいってみよう…
Desugared: Main.skip2{-r1L6-} :: _forall_ a$_4 =>{{Num a$_4}} -> a$_4 -> [a$_4] --# 「r1L6」はMain.skip2の固有子 --# 「_4」は型変数(テンプレート)「a」の固有子 --# 「{{Num a$_4}}」は辞書引数 _NI_ --# 「_NI_」は「まだ(実質的に)情報がない」を意味する。これは後に --# GHC_PRAGMA情報になり、インタフェースファイルに行く。 Main.skip2{-r1L6-} = /\ _4 -> \ d.Num.t4Gt -> let { {- CoRec -} +.t4Hg :: _4 -> _4 -> _4 _NI_ +.t4Hg = (+{-r3JH-} _4) d.Num.t4Gt fromInt.t4GS :: Int{-2i-} -> _4 _NI_ fromInt.t4GS = (fromInt{-r3JX-} _4) d.Num.t4Gt --# クラスメソッド「+」(固有子: r3JH)は辞書「Num」(今や明示的にラ --# ムダで導入される引数である)から加算のコードを選択する。コアは --# 二階のラムダ計算なので、型適用と型ラムダ(/\)は明示的である。 --# そのため、「+」は、まず型(「_4」)に適用され、次に辞書に適用さ --# れている。この適用の結果が実際の加法関数であり、後で使われる。 --# (非標準の)クラスメソッド「fromInt」についても全く同じことを行 --# う。驚くに値しないが、型「Int」はコンパイラに結わえつけられて --# いる。 lit.t4Hb :: _4 _NI_ lit.t4Hb = let { ds.d4Qz :: Int{-2i-} _NI_ ds.d4Qz = I#! 2# } in fromInt.t4GS ds.d4Qz --# 「I# 2#」というのは単なるリテラルのInt「2」のことである。これ --# は、GHCが「data Int = I# Int#」と定義していることを反映してい --# る。ここで、Int#はプリミティブの非ボックス化型である。(非ボッ --# クス化型の関連情報についてはどこか別のところを見よ) --# 「I#」の後の「!」は、データ構築子「I#」の適用が*飽和*している --# (つまり、部分適用ではない)ことを示す。 skip2.t3Ja :: _4 -> [_4] _NI_ skip2.t3Ja = \ m.r1H4 -> let { ds.d4QQ :: [_4] _NI_ ds.d4QQ = let { ds.d4QY :: _4 _NI_ ds.d4QY = +.t4Hg m.r1H4 lit.t4Hb } in skip2.t3Ja ds.d4QY } in :! _4 m.r1H4 ds.d4QQ {- end CoRec -} } in skip2.t3Ja
(「これは一つの単純な関数型言語に過ぎない」はPeyton Jones Enterprises, plc.の非レジスタ化商標です。)