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

JVM言語の動き方・動かし方 /make-jvm-lang

Miyakawa Taku
February 27, 2019

JVM言語の動き方・動かし方 /make-jvm-lang

言語処理系一般の仕組みを腑分けして、JVM言語の動き方・動かし方を把握します。

• 言語処理系の構成
• コンピュータはどのようにプログラムを動かすのか
• JVMはどのようにプログラムを動かすのか
• JVM言語処理系の構成: Kinkを題材に

本資料をCC BY 4.0ライセンスにもとづいて提供します。
https://creativecommons.org/licenses/by/4.0/deed.ja

Miyakawa Taku

February 27, 2019
Tweet

More Decks by Miyakawa Taku

Other Decks in Programming

Transcript

  1. JVM言語の動き方・動かし方
    2019-02-27 JJUGナイトセミナー
    ハッシュタグ: #jjug
    宮川 拓

    View Slide

  2. ⚫ @miyakawa_taku
    ⚫ JJUG幹事です
    ⚫ Salesforceで働いてます
    ⚫ 奄美出身の力士を応援しています☆
    ⚫ オレオレJVM言語Kinkを作っています
    http://doc.kink-lang.org/
    自己紹介
    #jjug
    2/127

    View Slide

  3. 背景
    Graal+Truffleは素敵みたい
    Graal+Truffleの素敵さを納得するため、
    言語処理系のかんどころを分かっときたい
    #jjug
    3/127

    View Slide

  4. 演目
    言語処理系の構成
    コンピュータはどのように
    プログラムを動かすのか
    JVMはどのように
    プログラムを動かすのか
    JVM言語処理系の構成:
    Kinkを題材に
    #jjug
    4/127

    View Slide

  5. 演目
    言語処理系の構成
    コンピュータはどのように
    プログラムを動かすのか
    JVMはどのように
    プログラムを動かすのか
    JVM言語処理系の構成:
    Kinkを題材に
    #jjug
    5/127

    View Slide

  6. そもそも
    「言語処理系」ってなに?
    #jjug
    6/127

    View Slide

  7. 本セッションにおける用語定義
    書かれた
    プログラム
    くりかえし 三回:
    念仏.唱える
    コンピュータ上での
    実行
    南無阿弥陀仏
    南無阿弥陀仏
    南無阿弥陀仏
    この間に必要なソフトウェア一式を
    「言語処理系」と呼ぶことにする
    #jjug
    7/127

    View Slide

  8. 言語処理系の二段階
    翻訳系 *1 実行系 *1
    書かれたプログラム
    実行に適した表現:
    機械語 or
    バイトコード or ...
    実行に適した表現

    コンピュータ上で
    実行
    #jjug
    8/127

    View Slide

  9. 翻訳系
    ここはザックリ行きます
    #jjug
    9/127

    View Slide

  10. 翻訳系
    書かれたプログラム
    実行に適した表現:
    機械語 or
    バイトコード or ...
    つまりコンパイラ
    #jjug
    10/127

    View Slide

  11. 翻訳系の中身
    書かれたプログラム
    トークン列
    抽象構文木 (AST)
    実行に適した表現
    字句解析
    構文解析
    シンボル解決, 型検査, 最適化,,,
    " く り か え し ", " 三 回 ", " : ", , ,
    (loop 3 (call (var "念仏") "唱える"))
    #jjug
    11/127

    View Slide

  12. つまり翻訳系は多段階のデータ変換処理
    プログラマの多くは、似たような処理を
    作った経験があるはず
    #jjug
    12/127

    View Slide

  13. データ投入バッチ
    タブ区切りテキスト
    トークン列
    抽象構文木 (AST)
    データベース
    字句解析
    構文解析
    マスタ突合, バリデーション,,,
    "中島敦", "悟浄歎異", 1942, 改行,,,
    [{"author": "中島敦",
    "title": "悟浄歎異",
    "year": 1942},,,]
    中島敦 悟浄歎異 1942
    深沢七郎 風流夢譚 1960
    ,,,
    やってることは翻訳系と同じ!
    #jjug
    13/127

    View Slide

  14. 翻訳系でやっていることは、
    とても馴染み深いデータ変換処理
    ただし、型検査や最適化などは、それぞれが
    一大トピック
    #jjug
    14/127

    View Slide

  15. 実行系
    こっちが今日のメイン
    #jjug
    15/127

    View Slide

  16. 実行系
    実行に適した表現

    コンピュータ上で
    実行
    #jjug
    16/127

    View Slide

  17. 誰が実行する?
    実行対象 実行の担い手
    機械語の
    プログラム
    CPUとOS
    つまりコンピュータそのもの
    機械語以外:
    バイトコード、
    木構造など
    実行系のプログラム:
    JVM, V8, YARVなど
    コンピュータの真似をする
    “Java Virtual Machine”!
    #jjug
    17/127

    View Slide

  18. ここから先は、
    ◼ コンピュータがどのように
    プログラムを動かすのか
    ◼ JVMやJVM言語処理系が、どのように
    コンピュータを真似するのか
    を見ていきます
    #jjug
    18/127

    View Slide

  19. 演目
    言語処理系の構成
    コンピュータはどのように
    プログラムを動かすのか
    JVMはどのように
    プログラムを動かすのか
    JVM言語処理系の構成:
    Kinkを題材に
    #jjug
    19/127

    View Slide

  20. サンプルプログラム
    N = M + 42
    ld x5, &M
    addi x6, x5, 42
    sd x6, &N
    書かれた
    プログラム
    RISC-Vの
    機械語
    プログラム x5, x6: レジスタ
    &M, &N: 変数の番地
    #jjug
    20/127

    View Slide

  21. OSの仕事: ローダ
    #jjug
    21/127

    View Slide

  22. OS(ローダ)の仕事 1/2
    プログラムが使う仮想アドレス空間を確保
    機械語命令列の領域
    グローバル変数の領域
    100番地
    200番地
    #jjug
    22/127

    View Slide

  23. OS(ローダ)の仕事 2/2
    プログラムをアドレス空間にロード *2
    #jjug
    ld x5, &M
    addi x6, x5, 42
    sd x6, &N
    ...
    M
    N
    ...
    100番地
    200番地
    204番地
    23/127

    View Slide

  24. CPUの仕事: 計算
    #jjug
    24/127

    View Slide

  25. CPU
    CPUの概念図 *3
    レ ジ ス タ 群
    演 算 装 置
    ( ALU )
    メ モ リ
    デ ー タ 用
    ポ ー ト
    命 令 用
    ポ ー ト
    Program
    Counter
    +4
    番 地
    命 令
    番 地
    入 力 演 算 結 果
    デ ー タ
    次 の 命 令 へ
    レ ジ ス タ : 計 算 に 使 う 数 値 を 置 い て お く 場 所
    Program Counter ( PC ) : 実 行 す る 命 令 の 番 地 を 表 す 特 殊 な レ ジ ス タ
    演 算 装 置 ( ALU ) : 加 算 器 と か 除 算 器 と か の 回 路 の か た ま り
    #jjug
    25/127

    View Slide

  26. CPU
    ld x5, &M
    レ ジ ス タ 群
    演 算 装 置
    ( ALU )
    メ モ リ
    デ ー タ 用
    ポ ー ト
    命 令 用
    ポ ー ト
    Program
    Counter
    +4
    1) 100
    6) x5
    7) +4
    3) &M=200 4) &M=200
    5) M=3
    1) 実行する命令の番地
    2) 命 令
    5) M =3 をロード
    #jjug
    2) 命令をロード
    3, 4) M の番地
    6) 3 をx5 レジスタに書き込み
    7) +4 して次の命令の番地へ
    26/127

    View Slide

  27. CPU
    addi x6, x5, 42
    レ ジ ス タ 群
    演 算 装 置
    ( ALU )
    メ モ リ
    デ ー タ 用
    ポ ー ト
    命 令 用
    ポ ー ト
    Program
    Counter
    +4
    1) 104
    3) x5
    8) +4
    5) 42
    1) 実行する命令の番地
    2) 命 令
    5) 即 値 をALU に入力
    4) x5=3 6) 和=45
    7) x6
    #jjug
    3, 4) x5 =3 をALU に入力
    2) 命令をロード 6, 7) 和をx6 レジスタに
    8) +4 して次の命令の番地へ
    27/127

    View Slide

  28. CPU
    sd x6, &N
    レ ジ ス タ 群
    演 算 装 置
    ( ALU )
    メ モ リ
    デ ー タ 用
    ポ ー ト
    命 令 用
    ポ ー ト
    Program
    Counter
    +4
    1) 108
    5) x6
    7) +4
    3) &N=204 4) &N=204
    6) x6=45
    1) 実行する命令の番地
    2) 命 令
    5, 6) x6 =45 をN に書き込み
    #jjug
    3, 4) N の番地
    2) 命令をロード 7) +4 して次の命令の番地へ
    28/127

    View Slide

  29. CPUの仕事: 手続き呼び出し
    #jjug
    29/127

    View Slide

  30. 手続き呼び出し
    高度なプログラムでは、手続きの呼び出しに
    よって処理があちこちにジャンプする
    foo {
    ...
    bar()
    ...
    }
    bar {
    ...
    return;
    }
    call
    return
    #jjug
    30/127

    View Slide

  31. 手続きbarを呼び出す
    jal x1, &bar
    jal命令は次の2つを実行する
    1. PC+4をx1に入れる
    ⚫ PC+4は、jal命令の次の命令の番地
    ⚫ あとでこの戻り番地に戻ってくる
    2. PCに&barを入れる
    ⚫ つまり、barの命令列にジャンプする
    #jjug
    31/127

    View Slide

  32. 手続きbarから戻る
    jalr x0, x1
    #jjug
    jalr命令は次の2つを実行する
    1. PC+4をx0に入れる
    ⚫ x0は、入れた値が捨てられる特殊なレジスタ
    ⚫ つまりここでは、PC+4は捨てられる
    2. PCにx1を入れる
    ⚫ x1には、呼び出し元で設定された
    戻り番地が入っている
    ⚫ つまり、呼び出し元にジャンプ=returnする
    32/127

    View Slide

  33. OSの仕事: スタックの確保
    #jjug
    33/127

    View Slide

  34. 呼び出し先でレジスタの値が変えられると、
    元の処理が続けられないので困る
    呼び出し前後で、レジスタの元の値を
    退避しておく領域が必要
    → これがスタック領域
    #jjug
    34/127

    View Slide

  35. スタックの確保
    スレッドXの
    スタック
    OSはスレッドごとにスタックを確保する
    #jjug
    スレッドYの
    スタック
    スレッドZの
    スタック
    35/127

    View Slide

  36. スタックの中身
    fooの
    フレーム
    barの
    フレーム
    callしたら呼び出しの作業領域として
    「フレーム」を確保する
    returnしたらフレームを捨てる
    #jjug
    スタックの中身のやりくりはプログラムの責任
    36/127

    View Slide

  37. OSの仕事: 動的メモリ確保
    #jjug
    37/127

    View Slide

  38. 動的メモリ確保
    OSは実行中のプログラムの要求に応じて、
    仮想アドレス空間を動的に割り当てる
    #jjug
    38/127

    View Slide

  39. 小まとめ:
    コンピュータはどのように
    プログラムを動かすのか
    #jjug
    39/127

    View Slide

  40. プログラムを動かすためにコンピュータは
    ◼ プログラムのロード
    ◼ 計算
    ◼ 手続き呼び出し&スタック管理
    ◼ 動的メモリ確保
    などを行います
    #jjug
    40/127

    View Slide

  41. 演目
    言語処理系の構成
    コンピュータはどのように
    プログラムを動かすのか
    JVMはどのように
    プログラムを動かすのか
    JVM言語処理系の構成:
    Kinkを題材に
    #jjug
    41/127

    View Slide

  42. Java処理系の二段階
    翻訳系: javac 実行系: JVM
    Javaソースコード
    Javaバイトコード
    Javaバイトコード

    コンピュータ上で
    実行
    #jjug
    42/127

    View Slide

  43. 実行系はコンピュータの真似をする
    でも、まったく同じことをする必要はない!
    ◼ 抽象度の高い枠組みを提供しても良い
    ◼ コンピュータの機能が使えるのであれば、
    そのまま使えば良い
    ◆ 加算器の回路をシミュレートするより、
    add命令を使う方が良い
    #jjug
    43/127

    View Slide

  44. コンピュータ, JVM,
    WebAssemblyの比較
    #jjug
    44/127

    View Slide

  45. WebAssembly
    各種言語をブラウザ上で動かすための実行系
    の仕様
    JVMと位置づけは似ているが、
    構成はかなり異なる
    #jjug
    45/127

    View Slide

  46. オブジェクトシステム
    コンピュータ
    WebAssembly
    JVM
    オブジェクトシステムは
    存在しない
    Javaクラス&インスタンス
    #jjug
    46/127

    View Slide

  47. メモリ管理
    コンピュータ
    WebAssembly
    JVM
    フラットなアドレス領域を
    割り当て
    GCは存在しない
    オブジェクトの生成を管理
    参照関係を追跡して、
    不要なオブジェクトをGC
    #jjug
    47/127

    View Slide

  48. 手続きの呼び出し
    コンピュータ
    WebAssembly
    JVM
    アドレスへのジャンプ
    クラスの仮想関数テーブルを
    ルックアップ&ジャンプ
    またはinvokedynamicで
    実行時に呼び出し方法を規定
    #jjug
    48/127

    View Slide

  49. スタック
    コンピュータ
    WebAssembly
    JVM
    他の呼び出しのフレームも
    読み書き可能
    現在の呼び出しの
    フレーム以外は
    読み書きできない
    #jjug
    49/127

    View Slide

  50. 計算
    コンピュータ
    WebAssembly
    JVM
    PC, ALU, レジスタ, ...
    仕様は項書換え系
    実装は様々
    バイトコードインタプリタ
    &JITコンパイラ
    計算にはさまざまな実現方法がある
    #jjug
    50/127

    View Slide

  51. 計算: 項書換え系
    #jjug
    51/127

    View Slide

  52. 項書換え系
    ルールに従って項(式の一部)を書き換える
    (1+2) * 3
    3 * 3
    9
    項書換え系ですべてのプログラムが表せる!
    → ラムダ計算、SKIコンビネータ計算
    #jjug
    52/127

    View Slide

  53. 項書換え系
    利点
    ◼ データと演算をひとまとめにするので
    理論的に扱いやすい
    ◼ 数学っぽくてかっこいい
    欠点
    ◼ 素直に実装すると遅い
    #jjug
    53/127

    View Slide

  54. 計算: Metacircular Interpreter
    #jjug
    54/127

    View Slide

  55. Meta-circular Interpreter
    ASTやそれに類する木構造を再帰的に
    手繰って計算を適用する
    「ASTを直接評価」という場合はこれのこと
    #jjug
    55/127

    View Slide

  56. Meta-circular Interpreter
    利点
    ◼ ホスト言語(実行系を記述する言語)の
    制御構造が使えるため、実装が楽
    欠点
    ◼ ホスト言語の制御構造に制約される
    #jjug
    56/127

    View Slide

  57. 計算:
    バイトコードインタプリタ
    #jjug
    57/127

    View Slide

  58. バイトコードインタプリタ
    命令列の実行をホスト言語上でエミュレート
    命令の抽象度は機械語よりも高い
    ◼ JVMの場合:
    ◆ new命令: インスタンスを生成
    ◆ getfield命令: オブジェクトから
    フィールドの値を取得
    #jjug
    58/127

    View Slide

  59. バイトコードインタプリタ
    実装のイメージ
    for (int pc = 0; pc < instructions_len; ++pc) {
    long insn = instructions[pc];
    switch (get_opcode(insn)) {
    case GETFIELD:
    obj *obj = datastack[--sp];
    sym field_sym = get_operand(insn);
    datastack[sp++] = obj->get_field(field_sym);
    break;
    case SETFIELD:
    ...
    }
    }
    #jjug
    59/127

    View Slide

  60. バイトコードインタプリタ
    利点
    ◼ Javaの場合、クラスファイルの内容が
    直接実行できる
    ◼ スタックを自前で管理する場合、
    ホスト言語の制御構造ではできないこと
    も実現できる
    欠点
    ◼ コンピュータよりは遅い
    #jjug
    60/127

    View Slide

  61. 計算?: JITコンパイラ
    #jjug
    61/127

    View Slide

  62. JITコンパイラ
    実行系に内蔵された翻訳系のプログラム
    JVM
    バイトコード
    インタプリタ
    Javaバイトコード

    コンピュータ上で
    実行
    JITコンパイラ *4
    Javaバイトコード
    機械語の命令列
    #jjug
    62/127

    View Slide

  63. JITコンパイラ
    実行系の実行時に
    ◼ 機械語の命令列を生成して
    ◼ コード領域を動的確保して
    ◼ コード領域に機械語命令列を書き込んで
    ◼ CPUに実行させる!
    #jjug
    63/127

    View Slide

  64. JITコンパイラ
    利点
    ◼ 生成されたコードの実行は速い
    欠点
    ◼ JITコンパイラの実行自体は重い
    → よく使われる所だけを対象とする
    #jjug
    64/127

    View Slide

  65. 小まとめ:
    JVMはどのように
    プログラムを動かすのか
    #jjug
    65/127

    View Slide

  66. JVMはコンピュータよりも抽象度の高い
    枠組みを提供している
    ◼ オブジェクトシステム
    ◼ GC
    ◼ 仮想関数テーブル経由の呼び出し
    ◼ スタックフレームの保護
    JVMの計算はバイトコードインタプリタと
    JITコンパイラの組み合わせで実現される
    #jjug
    66/127

    View Slide

  67. 演目
    言語処理系の構成
    コンピュータはどのように
    プログラムを動かすのか
    JVMはどのように
    プログラムを動かすのか
    JVM言語処理系の構成:
    Kinkを題材に
    #jjug
    67/127

    View Slide

  68. そもそも
    JVM言語処理系ってなに?
    #jjug
    68/127

    View Slide

  69. JVM言語処理系
    本セッションでは
    ◼ 実行系がJVM上で動く言語処理系、
    ◼ または実行系がJVMそのものである
    言語処理系、
    ◼ ただしJava以外のもの
    としておきます
    #jjug
    69/127

    View Slide

  70. #jjug
    JVM言語処理系
    Scala, Kotlin, Groovy, JRuby, Jython, Clojure,
    Kawa, Frege, Eta, BeanShell, Pnuts, ...
    and Kink!
    70/127

    View Slide

  71. なぜJVM上で
    処理系を作るの?
    #jjug
    71/127

    View Slide

  72. #jjug
    GC
    JVMにGCが任せられる
    マルチスレッドの処理系で実用的に動く
    GCを作ることは、とてもむずかしい
    ◼ CRubyやCPythonは、各スレッドを
    排他的に動かして問題を回避している
    (Global Interpreter Lock)
    ◼ Luaの処理系は単一スレッドで動く
    72/127

    View Slide

  73. #jjug
    移植性
    つくった処理系がWindowsでもLinuxでも
    macOSでも動くのは嬉しい
    73/127

    View Slide

  74. #jjug
    性能
    プログラムをJavaバイトコードに翻訳する
    場合、JITコンパイルのおかげで速い
    74/127

    View Slide

  75. #jjug
    Java API
    リッチなJava APIが使える
    リフレクションのおかげで、
    JVM言語の側からJava APIを操作することも
    簡単
    75/127

    View Slide

  76. JVM言語処理系の構成
    #jjug
    76/127

    View Slide

  77. 基本的な考え方
    JVMの性能面の利点を活かすため、
    JVMの仕組みをできればそのまま使いたい
    そのまま使えない場合は
    ◼ JVMの仕組みとのマッピングを頑張るか、
    ◆ invokedynamicはこれを楽にする命令
    ◼ それも難しい場合は自前の仕組みを作る
    #jjug
    77/127

    View Slide

  78. JVM言語処理系の構成の傾向
    Scala
    Kotlin
    Groovy
    JRuby
    Kink
    実行系が
    JVMそのもの
    実行系に
    自前の要素が多い
    #jjug
    78/127

    View Slide

  79. ここから先はKink処理系の実装の中で、
    JVMの仕組みのどの部分が
    ◼ 使えるか
    ◼ 使えないか
    ◼ 使える可能性があるか
    を見ていきます
    #jjug
    79/127

    View Slide

  80. Kinkとその処理系
    #jjug
    80/127

    View Slide

  81. Kinkってこんな言語
    :new_dog new_val(
    'bark' { 'Bow' }
    'howl' { 'Wow' }
    )
    }
    :Dog stdout.print_line(Dog.bark) # => Bow
    stdout.print_line(Dog.howl) # => Wow
    :Another_dog Another_dog:howl stdout.print_line(Another_dog.bark) # => Bow
    stdout.print_line(Another_dog.howl) # => Grrr
    #jjug
    Kink言語マニュアル: 特徴
    81/127

    View Slide

  82. Kinkの処理系
    kinkコマンド
    翻訳系 実行系
    Qcode
    Kinkソースコード
    Qcode:
    自前のバイトコード

    Javaで実装された
    インタプリタで実行
    ひとつのコマンドで翻訳系・実行系をまかなう
    RubyやPythonなどと同様
    #jjug
    82/127

    View Slide

  83. Kinkの処理系: 翻訳系 *5
    ソースコード
    トークン列
    AST
    Qcode
    Itree
    Wire
    最適化
    最適化
    木構造の中間表現
    フラットな中間表現
    #jjug
    83/127

    View Slide

  84. Kinkの処理系: 実行系
    オブジェクト
    システム
    メモリ管理
    計算
    スタック
    自前
    継承のないオブジェクトシステム
    JVMのGCに依存
    自前のバイトコードインタプリタ
    自前のスタック管理
    #jjug
    84/127

    View Slide

  85. Kink:
    オブジェクトシステムと
    メモリ管理
    #jjug
    85/127

    View Slide

  86. Kinkは継承のないオブジェクトシステムを
    採用しています
    最初のオブジェクト指向言語である
    Simula Iと同様!
    #jjug
    86/127

    View Slide

  87. クラスベースの継承

    データ X
    データ Y
    クラス
    親クラス
    メソッド a
    メソッド b
    メソッド b
    メソッド c
    instance-of
    is-a
    例:
    ⚫ Java
    ⚫ Smalltalk
    ⚫ Ruby
    ⚫ C++
    #jjug
    87/127

    View Slide

  88. プロトタイプベースの継承

    データ X
    データ Y
    プロト
    タイプ
    プロト
    の親
    メソッド a
    メソッド b
    メソッド b
    メソッド c
    delegate-to
    delegate-to
    例:
    ⚫ JavaScript
    ⚫ SELF
    ⚫ Io
    ※ Luaは変わり種
    #jjug
    88/127

    View Slide

  89. delegate-to関係、必要?
    半端な値としての「プロトタイプ」の
    存在が気持ち悪い
    言語仕様に現れるべきものじゃなくない?
    処理系の実装が裏で頑張ればよくない?
    #jjug
    89/127

    View Slide

  90. Kinkのオブジェクトシステム

    データ X
    データ Y
    メソッド a
    メソッド b
    メソッド c
    #jjug
    90/127

    View Slide

  91. #jjug
    値をつくる
    単にnew_val関数を呼び出して値を作る
    # 色
    new_val(
    'R' 64
    'G' 128
    'B' 255
    )
    91/127

    View Slide

  92. #jjug
    トレイト
    複数の値で共有するフィールドのかたまりを
    トレイトと呼ぶ(実体はただの配列)
    # 色のトレイト
    :Color_trait 'lightness' {[:C]
    (C._max + C._min) // 2
    }
    '_max' {[:C]
    [C.R C.G C.B].fold(0 COMPARE$max)
    }
    '_min' {[:C]
    [C.R C.G C.B].fold(255 COMPARE$min)
    }
    ]
    92/127

    View Slide

  93. #jjug
    トレイトを使う
    トレイトはnew_val関数の引数として
    展開して使う
    new_val(
    # 色のトレイトを展開
    ... Color_trait
    'R' 64
    'G' 128
    'B' 255
    )
    93/127

    View Slide

  94. 意味は下記と同じ
    #jjug
    new_val(
    # トレイトが展開された結果
    'lightness' { 省略 }
    '_max' { 省略 }
    '_min' { 省略 }
    'R' 64
    'G' 128
    'B' 255
    )
    このとおり実装すると無駄が多いので、
    そこは実行系が工夫する
    94/127

    View Slide

  95. どう実装する?
    Javaのクラスベースのオブジェクトシステム
    へのマッピングはしんどい
    → 自前実装
    #jjug
    95/127

    View Slide

  96. オブジェクトシステムの実装
    Kinkの値: Valクラス
    commonVarMapping
    traitVarMapping
    ownVarMapping
    全ての値に共通の
    フィールド群(不変):
    k1→v1, k2→v2,,,
    複数の値に共有される
    フィールド群(不変):
    k1→v1, k2→v2,,,
    値固有の
    フィールド群(可変):
    k1→v1, k2→v2,,,
    #jjug
    96/127

    View Slide

  97. メモリ管理
    Kinkの値: Valクラス
    commonVarMapping
    traitVarMapping
    ownVarMapping
    k1→v1, k2→v2,,,
    k1→v1, k2→v2,,,
    k1→v1, k2→v2,,,
    値同士の参照関係はJavaオブジェクトの参照関係に
    マッピングされている
    → メモリ管理はJavaのGCに丸投げできる
    #jjug
    97/127

    View Slide

  98. Kink:
    計算とスタック管理
    #jjug
    98/127

    View Slide

  99. Kinkは限定継続を使って
    例外やコルーチンを実現しています
    #jjug
    99/127

    View Slide

  100. 限定継続とは
    スタックを切り貼りする仕組み
    Kinkではreset関数とshift関数の組み合わせで
    限定継続を操作
    #jjug
    100/127

    View Slide

  101. reset & shift: 継続の保存
    reset: スタック上にタグを打つ
    tag
    resetブロック内の処理
    tag
    shift: タグまでの切片を関数(=継続)に保存
    tag
    tag
    kont =
    #jjug
    101/127

    View Slide

  102. 継続は呼び出せる
    kont(Val): 継続の呼び出し:
    継続に保存されたスタック切片を貼り付ける
    よその処理
    tag
    #jjug
    102/127

    View Slide

  103. スタックをどう実装する?
    JVMのスタックをそのまま使うのは難しい
    ◼ JVMにはスタックをプログラムから
    操作する方法がない
    ※ でも世の中には無茶する人がいる
    ※ Quasarライブラリは例外とバイト
    コード変換で無理矢理操作
    ※ Project Loomで状況が変わるかも
    → Kinkは自前のスタックを実装
    #jjug
    103/127

    View Slide

  104. 計算をどう実装する?
    KinkプログラムをJavaバイトコードに
    翻訳するのは苦労が多そう
    ◼ 自前のオブジェクトシステム
    ◆ putfield/getfield命令が使えない
    ◼ 自前の呼び出しスタック
    ◆ invoke*命令が使えない
    → 自前のバイトコードインタプリタ
    #jjug
    104/127

    View Slide

  105. Kink:
    性能
    #jjug
    105/127

    View Slide

  106. ベンチマーク: たらいまわし関数
    #jjug
    :tarai (X <= Y).if_else(
    { Y }
    { tarai(
    tarai(X - 1 Y Z)
    tarai(Y - 1 Z X)
    tarai(Z - 1 X Y)
    )
    }
    )
    }
    tarai(13 6 0)
    106/127

    View Slide

  107. #jjug
    tarai(13 6 0)
    3.8 秒
    7.7 秒
    12.0 秒
    30.3 秒
    43.5 秒
    Ruby
    2.5.1
    Python
    3.6.7
    GNU awk
    4.1.4
    GNU bc
    1.07.1
    Kink
    @ Xeon E5-2699 v4
    107/127

    View Slide

  108. プロファイリング
    #jjug
    紫色の部分がスタック操作
    バイトコードインタプリタと合わせて、4割~を占める
    → 自前実行系の限界?
    108/127

    View Slide

  109. 性能改善の余地
    計算をJVMにプッシュバックすれば
    性能改善が期待できる、かも
    ◼ JRuby方式:
    ◆ 自前バイトコードから
    JavaバイトコードへのJITコンパイル
    ◆ 最も実行される部分は、さらにJVMが
    機械語にJITコンパイルする
    ◼ Graal+Truffleに期待できる?
    #jjug
    109/127

    View Slide

  110. まとめ
    #jjug
    110/127

    View Slide

  111. JVM言語処理系の構成にはさまざまな
    バリエーションがある
    JVMの仕組みがそのまま使えると嬉しい
    ◼ GC: 自前で作るのは大変!
    ◼ JITコンパイラ: 計算が速くなる
    JVMの仕組みへのマッピングはときに大変
    #jjug
    111/127

    View Slide

  112. 演目
    言語処理系の構成
    コンピュータはどのように
    プログラムを動かすのか
    JVMはどのように
    プログラムを動かすのか
    JVM言語処理系の構成:
    Kinkを題材に
    #jjug
    112/127

    View Slide

  113. 参考文献
    #jjug
    113/127

    View Slide

  114. 中田育夫
    『コンパイラ: つくりながら学ぶ』, 2017
    ◼ コンパイラと実行系をつくる
    ◼ 易しくて良い本
    #jjug
    114/127

    View Slide

  115. Abelson, Sussman, Sussman
    『Structure and Interpretation of Computer
    Programs』, 1996
    ◼ いわゆる「SICP」
    ◼ Scheme言語の上で、Scheme言語の
    実行系を作ろう!という本
    ◼ 5章がバイトコードインタプリタ
    ◆ 自前のメモリ管理も扱われています
    #jjug
    115/127

    View Slide

  116. Patterson, Hennessy
    『Computer Organization and Design: RISC-V
    Edition』, 2017
    ◼ いわゆる「パタヘネ」
    ◆ 「ヘネパタ」は別書なので注意!
    ◼ CPUがプログラムをどうやって動かすか、
    という本
    #jjug
    116/127

    View Slide

  117. Pierce『型システム入門』, 訳書2013
    ◼ いわゆる「TAPL」
    ◼ 型システムの教科書
    ◼ 序盤の3章~7章が特にオススメ
    ◆ 型を導入する準備として、型のない
    項書換え系が解説されている
    ◆ 型なしラムダ計算はこれでバッチリ!
    #jjug
    117/127

    View Slide

  118. 中田育夫
    『コンパイラの構成と最適化』, 2版, 2009
    ◼ 主に最適化の本
    ◼ むずかしい
    #jjug
    118/127

    View Slide

  119. 浅井健一
    「shift/resetプログラミング入門」, 2011
    ◼ 限定継続を紹介する文書
    #jjug
    119/127

    View Slide

  120. Gasbichler, Sperber
    「Final Shift for call/cc: Direct
    Implementation of Shift and Reset」, 2002
    ◼ Scheme処理系に限定継続を実装したよ、
    という論文
    ◼ Kinkにおける限定継続の実装は、3節の
    「Direct Implementation」に該当する
    #jjug
    120/127

    View Slide

  121. Chambers, Ungar, Lee
    「An Efficient Implementation of SELF」,
    1991
    ◼ SELF言語の実行系をどうやって高速化
    したか
    ◼ ChromeのJSエンジンであるV8でも参考
    にされています: Fast Property Access
    #jjug
    121/127

    View Slide

  122. 脚注
    #jjug
    122/127

    View Slide

  123. *1 翻訳系、実行系
    次の分け方が普通(中田 (2017))
    ◼ 変換系 = Translator
    ◼ 翻訳系 = Compiler
    ◼ 通訳系 = Interpreter
    変換は翻訳の亜種/一部と割り切りました
    「通訳」は分かりづらい、「インタプリタ」
    はソースを直接動かすものと誤解されがち、
    ということで「実行系」としました
    #jjug
    123/127

    View Slide

  124. *2 プログラムをロード
    実際は、プログラムの実行ファイルを、
    プロセスの仮想アドレス空間にメモリマップ
    します
    ◼ @akachochin
    「Linuxのユーザプロセスのメモリマッ
    プについて」, 2017
    #jjug
    124/127

    View Slide

  125. *3 CPUの概念図
    Patterson, Hennessy (2017) のFigure 4.11を
    参考にしました
    #jjug
    125/127

    View Slide

  126. *4 JITコンパイラ
    Graalの場合、JITコンパイラの入力は
    Javaバイトコードに限りません
    #jjug
    126/127

    View Slide

  127. *5 Kinkの翻訳系
    ソース: FunHelper#compile
    #jjug
    127/127

    View Slide