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 Slide

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

    View Slide

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

    View Slide

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

    View Slide



  5. GoのMap おさらい



    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  10. 小さくはじめる

    View Slide


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

    View Slide

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

    View Slide

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

    View Slide

  14. これなら作れそう!

    View Slide

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

    View Slide

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

    View 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  23. を作る

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide


  28. 型を汎用化したい

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  34. これで動く が作れた

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide