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

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

 $ 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

インストール

  • 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日目

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

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

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

lldbで追っかけようとした

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

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

アセンブラを生成する

  • 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で動かす

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

Main.hsを動かす

  • 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日目

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がかけられない。
    $ -fno-code を適用すると .o が作成されなくなる
  • 動かない。Makeをやりなおし。

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

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

3日目

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

  • 設定を書き換えたい分だけは、新しいファイル(build.mk)に書き込む。
  • build.mkの作成
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にいれてみる。

$ ../../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に変換

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 で実行できそう? ソースにコメントで書かれてた
    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 を追う。

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を作る
   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

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2018-11-23 (金) 14:53:08 (1982d)