#author("2018-11-23T05:52:49+00:00","game","game") #author("2018-11-23T05:53:08+00:00","game","game") *コンパイラ構成論ソース読み会 〜ghc〜 [#i886aa16] **ghcインストール前に入れる [#h44f1f9a] $ git clone --recursive git://git.haskell.org/ghc.git -/usr/local/以下にインストールしても構わない場合はcloneしてきたディレクトリ上に移動して作業する -install先を指定する場合は以下の処理を行ってから作業する --ディレクトリ作成、移動 $ mkdir ghc_build $ cd ghc_build -- --cabalをインストールする $ brew install cabal-install --happyとAlexをインストールする $ cabal install happy $ cabal install Alex --happyとAlexにシンボリックリンクを貼る $ cd /usr/local/bin $ ln -s ~/.cabal/bin/happy happy $ ln -s ~/.cabal/bin/alex alex **インストール [#w4862c51] -公式ホームページ(https://ghc.haskell.org/trac/ghc/wiki/Building )を参考に - mac osはこれを見て環境設定 https://ghc.haskell.org/trac/ghc/wiki/Building/Preparation/MacOSX - debugはこれを見る https://ghc.haskell.org/trac/ghc/wiki/Debugging/Compiler - 詳しくはここ https://ghc.haskell.org/trac/ghc/wiki/Building/Using - Haskellコンパイラはセルフビルドする必要性がある為Stage0->Stage1->Stage2の順でビルドする - まずはGitリポジトリからGHCをcloneする https://ghc.haskell.org/trac/ghc/wiki/Building/GettingTheSources % git clone --recursive git://git.haskell.org/ghc.git - bootを叩く時にm4などが必要になる % brew install m4 % brew install autoconf % brew install automake -- 残念ながらpathが通らないのでbrew install時に出たパスをzshrcにいれる -git で落としてくる $ git clone --recursive git://git.haskell.org/ghc.git - buildファイルが無いのでbuild.mk.sampleをリネームして用意する -/usr/local/以下にインストールしても構わない場合はcloneしてきたディレクトリ上に移動して作業する $ cd ghc -install先を指定する場合は以下の処理を行ってから作業する --install先ディレクトリ作成、移動 $ mkdir ghc_build $ cd ghc_build --ソースへのリンク作成 $ lndir ../ghc $ ln -s ../ghc/.git -bootスクリプトの実行 $ ./ boot 以前はPerlだったらしいがPythonに変わってた -configureの実行 $ ./configure --install先をしてする場合は以下のようにprefixを指定する $ ./configure --prefix=$PWD --configure時 configureにときにhappyが無いと言われるケースが存在する. happyはhaskellのパーサジェネレータで,Haskellのstack(The Haskell Tool Stack)かcabalで導入する. stackの場合 ghcも入る $ brew install stack $ stack install happy cabal $ brew install cabal-install $ cabal update $ cabal install happy -build.mk の作成 --mk以下にbuild.mk.sampleというファイルがあるのでそれをコピーして編集 $ cp mk/build.mk.sample mk/build.mk $ vim mk/build.mk 12行目辺りの以下の部分を#を消して有効に #BuildFlavour = perf 80行目付近の以下の部分を ifeq "$(BuildFlavour)" "perf" # perf matches the default settings, repeated here for comparison: SRC_HC_OPTS = -O -H64m GhcStage1HcOpts = -O -fasm GhcStage2HcOpts = -O2 -fasm このように編集する ifeq "$(BuildFlavour)" "perf" # perf matches the default settings, repeated here for comparison: SRC_HC_OPTS = -O -H64m GhcStage1HcOpts = -O -fasm -DDEBUG GhcStage2HcOpts = -O2 -fasm -DDEBUG 今はこうする? GhcStage1HcOpts = -O0 $(GhcFAsm) -fprof-auto -DDEBUG GhcStage2HcOpts = -O0 $(GhcFAsm) -fprof-auto -DDEBUG -makeとmake installする --top levelのmakeが下のmakeを呼ぶときに -j が引き継がれるようにする。 $ make MAKE="make -j" -j $ make install prefixを指定した場所に、ghcができている。指定してなければ/usr/local/bin/。 -エラーと対処法 --Bad interface file: ***.hi-boot --Bad interface file: ***.hi ---異なるバージョンのghcでコンパイルされたバイナリが残っているのが原因なのでいちど.hi-boot,.hiファイルを消す。 $ rm **/*.hi-boot --ghc-pkg: ghc no longer supports single-file style package databases ---ghc-pkg 7.9ではファイル形式のデータベースはサポートしていないのでinitし直す必要がある。 ---古いghcを消した時に発生した。 $ rm libraries/botstrapping.conf $ ghc-pkg init libraries/bootstrapping.conf * 1日目 [#f8353671] ** どこのghcが呼ばれているのかを調べる [#fbf44b30] -新しくインストールしたghcを、適当なディレクトリに移動する $ vim ghc -最後の行のexecをechoに変更する $ ./ghc ../append.hs これで、ghcを実行する時、どのファイルが呼ばれるかがわかる ** lldbで追っかけようとした [#g08dd524] $ lldb -- /Users/one/workspace... append.hs これでは、mainがないというエラーが出る話が ** アセンブラを生成する [#s7837b80] -append.hsを編集し、先頭にModuleを追加する。 --.oを生成 $ ghc_build/bin/ghc -c append.hs --.s(アセンブラコード)を生成 $ ghc_build/bin/ghc -S append.hs ** ghc_build/compiler/main/HscMain.hsをghciで動かす [#m31d55a7] -ghcがあるディレクトリへ移動 $ cd ghc_build/compiler -Hscmain.hsを、ghciで動かすために様々なオプションを追加する -cpp : Cのプリプロセッサを通す -I : #includeファイルを探すディレクトリを、通常のCでの方法で指定する -S : .s(アセンブラコード)を生成 -i : 必要なモジュールのディレクトリを指定する -D : 値を定義する - オプションを探す場合 $ ghc --help $ man ghc **Main.hsを動かす [#g400a981] -FingerPrint.hsが重複していて、エラーが出た --FingerPrint.hsを読み込まないように、 --__GLASGOW_HASKELL__ < 707なif文にかかる処理を取った ---これをとるとmakeが通らなくなったので必要な処理だったようだ ---buildされるghcのバージョンではなくbuildに使用したghcのバージョン? $ ghci -fno-code -cpp -DSTAGE=2 -I. -I../ghc -I./stage2 -I./stage2/build -iutils:main:prelude:hsSyn:types:typecheck:deSugar:stage2/build:parser:iface:basicTypes:profiling:specialise:coreSyn:cmm:simplCore:codeGen:nativeGen:StgSyn:cbits:rename:simplStg:specialise:stranal:types:vectorise:llvmGen ../ghc/Main.hs -この方法でghciは通った -次は、ghc_build/compiler/main/HscMain.hsを動かしたい - HscEnv とCompileFileを呼び出せばいい - InitSysToolsをつかってSettingsを作る let s = SysTools.initSysTools ( Just "/Users/one/workspace/ghc_build/lib/ghc-7.9.20141204" ) ss <- s - SettingsからDynFlagsが作れる (defaultDynFlags.Settings DynFlag.hs) let df = DynFlags.defaultDynFlags s - DynFlagsからHscEnvが作れる (newHscEnv HscMain.hs) HscMain.newHscEnv df --ライブラリが見つからないと怒られる * 2日目 [#f8353671] ** Haskell Compilerについて勉強する [#l845347c] スライド(http://www.scs.stanford.edu/11au-cs240h/notes/ghc-slides.html#%281%29)を 読んで、Haskell Compilerについて勉強する。 ** GCについて勉強 [#yfce9ca2] スライド(http://www.slideshare.net/dec9ue/rts-gc)を読んで、 Gerbage Collectionについて勉強する。 ** Main.hsを動かす(2) [#p12e0d6c] - .hiと.oがあると、ghciのbreak pointがかけられない。 $ -fno-code を適用すると .o が作成されなくなる - 動かない。Makeをやりなおし。 ** デバッグオプションがついてない?(問題編) [#q6bd9eea] - config.mk.inの作成 GhcDebugged=YES - configureを実行すると、GhcDebuggedがNOになる。 GhcDebugged=NO * 3日目 [#c6233ecd] ** デバッグオプションがついてない?(解決編) [#k66b060e] - 設定を書き換えたい分だけは、新しいファイル(build.mk)に書き込む。 - build.mkの作成 GhcDebugged=YES ** lldbで読もう [#da52547a] - lldbでcプログラムを読むように、ghcの実行ファイルを読む。 - nmコマンドを使って、symboltableから、調査対象の関数名とプログラムアドレスを抜き出す。 - プログラムアドレスにbreak pointをあてて、そこからstep実行をして、動作を細かく調査しようとした。 - lldbで読んだ結果 -- ライブラリの読み込みやインダイレクトジャンプばかりをやっていて、追いにくそう.. ** parser/Lexer.x を読む。 [#ke163cab] 定義を確認し、alexにかけることで**.hsに変換 alex Parser.x 変換したParser.hsを読む。 ** Lexer.hsをghciにいれてみる。 [#n3117e98] $ ../../build/bin/ghci -fno-code -cpp -DSTAGE=2 -I. -I../ghc -I./stage2 -I./stage2/build -iutils:main:prelude:hsSyn:types:typecheck:deSugar:stage2/build:parser:iface:basicTypes:profiling:specialise:coreSyn:cmm:simplCore:codeGen:nativeGen:StgSyn:cbits:rename:simplStg:specialise:stranal:types:vectorise:llvmGen ./parser/Lexer.hs - Libraryが読み込めないからソースコードから読むことに。 - Lexer.hsには字句解析について書かれている。 - AlexInputが怪しいのでは? -- AlexInputが書かれているコードを探す -> Parser.yに書かれているらしい ** parser/Parser.hsを読む [#eeef9a03] Parser.y を happyで *.hsに変換 happy Parser.y - Parser.hsで予約語の確認など(tokenとかも) - 数字の定義をするのが大変らしい - happyThenとは? -> ammsの定義を確認 -- aaって何? -> aa = annotation -- amms = リストに対してannotationをマップして付けるもの ** ParseResultとは? [#t918af22] -- parser/Lexer.hsにあるらしい -- runParser発見! ** parser を実行したい [#l9ca87cd] - parser/Parser.y - happy parser/Parser.y -- で parser/Parser.hs が手に入る - parser は runParser で実行できそう? ソースにコメントで書かれてた runParser :: DynFlags -> String -> P a -> ParseResult a runParser flags str parser = unP parser parseState where filename = "\<interactive\>" location = mkRealSrcLoc (mkFastString filename) 1 1 buffer = stringToStringBuffer str parseState = mkPState flags buffer location in ** PState とは? [#ob6d27a9] - parser/HscMain.hsにcaseで使われてる。 ** Desugar を追う。 [#r6319f58] Hskellからcoreにするときにdesugarするので、Desugarを追う。 -> desugar/Desugar.hsに書かれてる。 ** dsLExprとは [#f8ac644c] - deSugar/DsExpr.hsに書いてある。 - coreExpr を発見 -- coreの計算を行うところらしい。 --- oreSyn/CoreSyn.hsに書いてある。 - coreSyn/CoreSyn.hsでcoreの定義がされている。 ** 他に読むものは? [#s1848bf0] - typechaek/TcUnify.hsを見たら推論木がわかりそう。 - typecheck/TcType.hsを流し読み。 * 2018 [#ob90acbb] ** Haskell入門 [#j28152ec] - ソフトウェア工学でも行ったbinary treeの例題 - Tree.hsを作る data Tree a v = Node a v (Tree a v) (Tree a v) | Leaf deriving (Show) insert :: (Ord a ) => Tree a v -> a -> v -> Tree a v insert Leaf k v = Node k v Leaf Leaf insert (Node k v left right) k1 v1 | k == k1 = Node k v1 left right | k > k1 = Node k v (insert left k1 v1 ) right | k < k1 = Node k v left ( insert right k1 v1) test1 = Node 1 0 Leaf Leaf test2 = insert test1 2 1 - ghci Node.hs - ghcでcoreの出力を見る!! - mainが無いとghcでコンパイルできない data Tree a v = Node a v (Tree a v) (Tree a v) | Leaf deriving (Show) insert :: (Ord a ) => Tree a v -> a -> v -> Tree a v insert Leaf k v = Node k v Leaf Leaf insert (Node k v left right) k1 v1 | k == k1 = Node k v1 left right | k > k1 = Node k v (insert left k1 v1 ) right | k < k1 = Node k v left ( insert right k1 v1) test1 = Node 1 0 Leaf Leaf test2 = insert test1 2 1 main = do return test2 -