Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Goのmapとheapを自作してみた / How to create your own map and heap in Go

DQNEO
February 25, 2019

Goのmapとheapを自作してみた / How to create your own map and heap in Go

Goコンパイラを自作するにあたって、mapとheapを自作してみたので、どうやって作ったのかを説明します。

DQNEO

February 25, 2019
Tweet

More Decks by DQNEO

Other Decks in Programming

Transcript

  1. GoのMapとHeapを自作してみ

    @DQNEO (どきゅねお)
    2019/2/25 mercari.go

    View full-size slide

  2. コンパイラ
    をゼロから自作してます。
    https://github.com/DQNEO/minigo

    View full-size slide

  3. が動くようになった!
    /
    https://twitter.com/DQNEO/status/1088422242772381697

    View full-size slide

  4. どうやって作ったのか

    View full-size slide



  5. GoのMap おさらい



    View full-size slide

  6. GoのMap
    Key: 任意の型 Value: 任意の型
    要素数:任意

    View full-size slide

  7. GoのMap
    任意の型の と
    任意の型の を
    任意の数だけ格納できるナニカ

    View full-size slide

  8. どこから手を付ければ

    View full-size slide

  9. 自作コンパイラ3原則
    小さくはじめる
    動けば正義
    遅くても気にしない

    View full-size slide

  10. 小さくはじめる

    View full-size slide


  11. とにかくハードルを下げる
    まずは 型は int のみ

    View full-size slide

  12. m = map [int]int {
    1: 2,
    3: 4,
    }
    とにかくハードルを下げる
    要素数も固定

    View full-size slide

  13. とにかくハードルを下げる
    1 2
    3 4
    メモリ上に数値が並んでるだけ

    View full-size slide

  14. これなら作れそう!

    View full-size slide

  15. m = map [int]int {
    1: 2,
    3: 4,
    }
    map[int]int
    1 2 3 4
    メモリ上に8byteずつ値を並べる

    View full-size slide

  16. x = m[3]
    map get
    1 2 3 4
    keyを16byteずつホップしながら探索
    マッチしたらその右隣の値を返す

    View full-size slide

  17. Keyの探索 (X86-64の例)
    emit("mov %%r13, %%rax") // i
    emit("imul $16, %%rax") // i * 16
    emit("mov %%r10, %%rcx") // head
    emit("add %%rax, %%rcx") // head + i * 16
    emit("mov (%%rcx), %%rdx") // eval key value
    16byteずつホップしながらkeyを探索

    View full-size slide

  18. マッチしたKey位置から
    Valueを取得
    emit("mov 8(%%rcx), %%r15") // get value of value
    emit("jmp %s", labelEnd)
    key値がマッチしたら、
    8bytes隣のvalue値を取り出して
    探索ループを脱出する

    View full-size slide

  19. map get
    実装の詳細はこちら
    https://github.com/DQNEO/minigo/commit/9f0cef573
    d772d67a0efc8fa88c78b185743ea40#diff-fa95711d
    3528c54a10a4bd5a3303bf8cR1443

    View full-size slide

  20. map set
    前述のようにKeyの探索を行う
    Keyが存在する?
    Yes -> その位置+8bytesの場所に値を書き込む。
    No -> データ領域の末尾にKey,Valueを追記。(後述)

    View full-size slide

  21. for range構文
    for k,v := range m {
    ...
    }

    View full-size slide

  22. for range構文
    for k,v := range m {
    ...
    }
    for i:=0, ik := mData[ i * 16]
    v := m[v]
    }
    構文木を書き換えて、普通のfor文に変換する

    View full-size slide

  23. を作る
    に追記するには動的なメモリ確保
    が必要
    を実装するのはチョットむずい
    疑似 でお茶を濁す

    View full-size slide

  24. 疑似
    静的グローバル
    配列をheapに見
    立て、そこからメ
    モリを必要なだけ
    切り取る

    View full-size slide

  25. 疑似 の利点
    メモリ開始アドレスが固定なので
    デバグしやすい

    View full-size slide

  26. これで への
    要素追加が可能に

    View full-size slide


  27. 型を汎用化したい

    View full-size slide

  28. 1 "hello"
    3 "world"
    valueのサイズが可変 → むずい

    View full-size slide

  29. 1 &s1
    3 &s2
    1 &i1
    3 &i2
    値のアドレスを格納する
    値自体はheapに置き、そのアドレスをmap領域に書
    き込む。
    valueのサイズは8bytesのままなので、今までの
    mapロジックをほぼそのまま使える

    View full-size slide

  30. mov (%rax), %rax
    Valueのデリファレンス
    値を取り出した直後に1度デリファレンスするだけ

    View full-size slide

  31. "hello" "world"
    "こんにち
    わ"
    "世界"
    keyの型も汎用にしたい

    View full-size slide

  32. &s1 &s2
    &s3 &s4
    keyも同様にアドレスを登録する

    View full-size slide

  33. これで動く が作れた

    View full-size slide

  34. の実装全体はこんな感じ
    https://github.com/DQNEO/minigo/compare/3
    654b7c283a01efd273a336188fd13ab10dbdf6
    e...3f6ce0773dd9bd7a233b2278f8911a5253f
    90999

    View full-size slide

  35. おまけ
    簡単そうに言っているが、
    動的アドレス計算を駆使するので、
    一歩間違えると Segmentation fault 地獄

    View full-size slide

  36. おかげで gdb が使えるようになりました
    おまけ

    View full-size slide

  37. おまけ
    このmapの仕組みを使えば、"interface"機能も実
    現できる。(できた)

    View full-size slide