ghc

ghc

#author(“2018-11-23T05:53:08+00:00”,“game”,“game”)

# コンパイラ構成論ソース読み会 〜ghc〜

# ghcインストール前に入れる

1
 $ git clone --recursive git://git.haskell.org/ghc.git
  • /usr/local/以下にインストールしても構わない場合はcloneしてきたディレクトリ上に移動して作業する

  • install先を指定する場合は以下の処理を行ってから作業する

    • ディレクトリ作成、移動
1
2
$ mkdir ghc_build
$ cd ghc_build
* 

* cabalをインストールする
1
 $ brew install cabal-install
* happyとAlexをインストールする
1
2
 $ cabal install happy
 $ cabal install Alex
* happyとAlexにシンボリックリンクを貼る
1
2
3
 $ cd /usr/local/bin
 $ ln -s ~/.cabal/bin/happy happy
 $ ln -s ~/.cabal/bin/alex alex

# インストール

1
2
 https://ghc.haskell.org/trac/ghc/wiki/Building/GettingTheSources
 % git clone --recursive git://git.haskell.org/ghc.git
  • bootを叩く時にm4などが必要になる
1
2
3
4
 % brew install m4
 % brew install autoconf
 % brew install  automake
 
* 残念ながらpathが通らないのでbrew install時に出たパスをzshrcにいれる
  • git で落としてくる
1
 $ git clone --recursive git://git.haskell.org/ghc.git
  • buildファイルが無いのでbuild.mk.sampleをリネームして用意する

  • /usr/local/以下にインストールしても構わない場合はcloneしてきたディレクトリ上に移動して作業する

1
 $ cd ghc
  • install先を指定する場合は以下の処理を行ってから作業する
    • install先ディレクトリ作成、移動
1
2
$ mkdir ghc_build
$ cd ghc_build
* ソースへのリンク作成
1
2
$ lndir ../ghc
$ ln -s ../ghc/.git
  • bootスクリプトの実行
1
2
 $ ./ boot
 以前はPerlだったらしいがPythonに変わってた
  • configureの実行
1
$ ./configure
* install先をしてする場合は以下のようにprefixを指定する
1
 $ ./configure --prefix=$PWD
* configure時

configureにときにhappyが無いと言われるケースが存在する. happyはhaskellのパーサジェネレータで,Haskellのstack(The Haskell Tool Stack)かcabalで導入する.

stackの場合 ghcも入る

1
2
 $ brew install stack
  $ stack  install happy

cabal

1
2
3
 $ brew install cabal-install    
 $ cabal update
 $ cabal install happy     
  • build.mk の作成
    • mk以下にbuild.mk.sampleというファイルがあるのでそれをコピーして編集
1
2
 $ cp mk/build.mk.sample mk/build.mk
 $ vim mk/build.mk

12行目辺りの以下の部分を#を消して有効に

1
#BuildFlavour = perf

80行目付近の以下の部分を

1
2
3
4
5
ifeq "$(BuildFlavour)" "perf"
# perf matches the default settings, repeated here for comparison: 
SRC_HC_OPTS     = -O -H64m
GhcStage1HcOpts = -O -fasm 
GhcStage2HcOpts = -O2 -fasm 

このように編集する

1
2
3
4
5
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
1
今はこうする?

GhcStage1HcOpts = -O0 $(GhcFAsm) -fprof-auto -DDEBUG GhcStage2HcOpts = -O0 $(GhcFAsm) -fprof-auto -DDEBUG

  • makeとmake installする
    • top levelのmakeが下のmakeを呼ぶときに -j が引き継がれるようにする。
1
2
 $ 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ファイルを消す。
1
$ rm **/*.hi-boot
* ghc-pkg: ghc no longer supports single-file style package databases
    * ghc-pkg 7.9ではファイル形式のデータベースはサポートしていないのでinitし直す必要がある。
    * 古いghcを消した時に発生した。
1
2
$ rm libraries/botstrapping.conf
$ ghc-pkg init libraries/bootstrapping.conf

# 1日目

# どこのghcが呼ばれているのかを調べる

  • 新しくインストールしたghcを、適当なディレクトリに移動する
1
 $ vim ghc
  • 最後の行のexecをechoに変更する
1
 $ ./ghc ../append.hs

これで、ghcを実行する時、どのファイルが呼ばれるかがわかる

# lldbで追っかけようとした

1
 $ lldb -- /Users/one/workspace... append.hs

これでは、mainがないというエラーが出る話が

# アセンブラを生成する

  • append.hsを編集し、先頭にModuleを追加する。

    • .oを生成
1
 $ ghc_build/bin/ghc -c append.hs
* .s(アセンブラコード)を生成
1
 $ ghc_build/bin/ghc -S append.hs

# ghc_build/compiler/main/HscMain.hsをghciで動かす

  • ghcがあるディレクトリへ移動
1
 $ cd ghc_build/compiler
  • Hscmain.hsを、ghciで動かすために様々なオプションを追加する
1
2
3
4
5
 -cpp : Cのプリプロセッサを通す
 -I   : #includeファイルを探すディレクトリを、通常のCでの方法で指定する
 -S   : .s(アセンブラコード)を生成
 -i   : 必要なモジュールのディレクトリを指定する
 -D   : 値を定義する
  • オプションを探す場合
1
2
 $ ghc --help
 $ man ghc

# Main.hsを動かす

  • FingerPrint.hsが重複していて、エラーが出た

    • FingerPrint.hsを読み込まないように、
    • GLASGOW_HASKELL < 707なif文にかかる処理を取った
      • これをとるとmakeが通らなくなったので必要な処理だったようだ
      • buildされるghcのバージョンではなくbuildに使用したghcのバージョン?
1
 $ 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を作る

1
2
let s = SysTools.initSysTools ( Just "/Users/one/workspace/ghc_build/lib/ghc-7.9.20141204" )
ss <- s
  • SettingsからDynFlagsが作れる (defaultDynFlags.Settings DynFlag.hs)
1
let df = DynFlags.defaultDynFlags s
  • DynFlagsからHscEnvが作れる (newHscEnv HscMain.hs)
1
HscMain.newHscEnv df
* ライブラリが見つからないと怒られる

# 2日目

# Haskell Compilerについて勉強する

スライド(http://www.scs.stanford.edu/11au-cs240h/notes/ghc-slides.html#%281%29)を 読んで、Haskell Compilerについて勉強する。

# GCについて勉強

スライド(http://www.slideshare.net/dec9ue/rts-gc)を読んで、 Gerbage Collectionについて勉強する。

# Main.hsを動かす(2)

  • .hiと.oがあると、ghciのbreak pointがかけられない。
1
$ -fno-code を適用すると .o が作成されなくなる
  • 動かない。Makeをやりなおし。

# デバッグオプションがついてない?(問題編)

  • config.mk.inの作成
1
GhcDebugged=YES
  • configureを実行すると、GhcDebuggedがNOになる。
1
GhcDebugged=NO

# 3日目

# デバッグオプションがついてない?(解決編)

  • 設定を書き換えたい分だけは、新しいファイル(build.mk)に書き込む。
  • build.mkの作成
1
GhcDebugged=YES

# lldbで読もう

  • lldbでcプログラムを読むように、ghcの実行ファイルを読む。

  • nmコマンドを使って、symboltableから、調査対象の関数名とプログラムアドレスを抜き出す。

  • プログラムアドレスにbreak pointをあてて、そこからstep実行をして、動作を細かく調査しようとした。

  • lldbで読んだ結果

    • ライブラリの読み込みやインダイレクトジャンプばかりをやっていて、追いにくそう..

# parser/Lexer.x を読む。

定義を確認し、alexにかけることで**.hsに変換  alex Parser.x 変換したParser.hsを読む。

# Lexer.hsをghciにいれてみる。

1
$ ../../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を読む

Parser.y を happyで *.hsに変換

1
happy Parser.y
  • Parser.hsで予約語の確認など(tokenとかも)

  • 数字の定義をするのが大変らしい

  • happyThenとは? -> ammsの定義を確認

    • aaって何? -> aa = annotation
    • amms = リストに対してannotationをマップして付けるもの

# ParseResultとは?

* parser/Lexer.hsにあるらしい
* runParser発見!

# parser を実行したい

  • parser/Parser.y
  • happy parser/Parser.y
    • で parser/Parser.hs が手に入る
  • parser は runParser で実行できそう? ソースにコメントで書かれてた
1
2
3
4
5
6
7
    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 とは?

  • parser/HscMain.hsにcaseで使われてる。

# Desugar を追う。

1
2
Hskellからcoreにするときにdesugarするので、Desugarを追う。
-> desugar/Desugar.hsに書かれてる。

# dsLExprとは

  • deSugar/DsExpr.hsに書いてある。
  • coreExpr を発見
    • coreの計算を行うところらしい。
      • oreSyn/CoreSyn.hsに書いてある。
  • coreSyn/CoreSyn.hsでcoreの定義がされている。

# 他に読むものは?

  • typechaek/TcUnify.hsを見たら推論木がわかりそう。
  • typecheck/TcType.hsを流し読み。

# 2018

# Haskell入門

  • ソフトウェア工学でも行ったbinary treeの例題

  • Tree.hsを作る

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
   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でコンパイルできない

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
   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
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy