13
第 12 第 RHG 第第第 2009/3/14 第第 第第

第 12 回 RHG の逆襲

Embed Size (px)

DESCRIPTION

第 12 回 RHG の逆襲. 2009/3/14 澤田 淳二. 今日のテーマ. ブロック周りの実装 メソッドへのブロックの渡し方 yield 時の処理 変数アクセス next, redo, break. サンプルソース. (1) C でのブロック呼び出し. (2) Ruby でのブロック呼び出し. def foo yield(1) yield(2) yield(3) yield(4) yield(5) end x = 1 foo {|i| next if i == 2 break if i == 3 - PowerPoint PPT Presentation

Citation preview

Page 1: 第 12 回  RHG の逆襲

第 12 回 RHG の逆襲

2009/3/14

澤田 淳二

Page 2: 第 12 回  RHG の逆襲

今日のテーマ ブロック周りの実装

メソッドへのブロックの渡し方 yield 時の処理 変数アクセス next, redo, break

Page 3: 第 12 回  RHG の逆襲

サンプルソース

def foo yield(1) yield(2) yield(3) yield(4) yield(5)end

x = 1foo {|i| next if i == 2 break if i == 3 redo if i == 6 p [x, i]}

x = 1(1..5).each do |i| (1..5).each do |j| next if i == 2 break if i == 3 redo if i == 6 p [x, i, j] endend

(1) C でのブロック呼び出し (2) Ruby でのブロック呼び出し

ローカル変数

外のブロック変数

自分のブロック変数

Page 4: 第 12 回  RHG の逆襲

YARV コードへの変換結果

で YARV コードが出力されますruby --dump=insn スクリプト

Page 5: 第 12 回  RHG の逆襲

ruby のトレース実行 Emacs の場合

make するとき最適化フラグは切っておくとよい M-x gdb で gdb 起動

• set args でコマンドライン• break でブレークポイント(ファイル:行数 or 関

数)• run で実行開始• step で 1 行実行(関数に入る)• next でも 1 行実行(関数には入らない)• finish で関数抜けだし• bt でバックトレース

Page 6: 第 12 回  RHG の逆襲

メソッドへのブロックの渡し方( 1 ) コンパイル時

iseq_compile_each() の NODE_ITER および NODE_CALL 参照

NODE_ITER• ブロックの NODE ツリーをコンパイルし compil

e_data->current_block に設定 NODE_CALL

• send 命令の引数として current_block を設定

Page 7: 第 12 回  RHG の逆襲

メソッドへのブロックの渡し方( 2 ) 実行時

ブロックの準備: caller_setup_args()• ブロックの rb_iseq_t をセットした rb_block_t を

準備• cfp の一部を rb_block_t 用の領域として使用

メソッドの呼び出し: vm_push_frame()• rb_block_t を specval に設定

Page 8: 第 12 回  RHG の逆襲

スタックは の向きに伸びる

図解

rb_control_frame_t

rb_block_t

iseqブロックのrb_iseq_t

実行中の cfp1 つ前の cf

p

lfpspecval

VALUE スタック

cfp スタック

Page 9: 第 12 回  RHG の逆襲

yield 時の処理 specval から rb_block_t 取り出し cfp を push

1 つ前の cfp の lfp(rb_block_t.lfp) を lfp に設定

1 つ前の cfp の dfp を specval に設定

Page 10: 第 12 回  RHG の逆襲

図解

外の each の cfp

<main> の cfp

外のブロックの cfp

中の each の cfp

中のブロックの cfp

j

specval(1 つ前の dfp)dfp

specval(rv_block_t)

idfp

specval(rb_block_t)

specval(1 つ前の dfp)

lfp, dfp

specval

x

サンプルソース (1) で中のブロック実行時のスタック状態

lfp

lfp

スタックは の向きに伸びる

Page 11: 第 12 回  RHG の逆襲

変数アクセス dfp を利用して変数の位置を特定 自ブロックの外にある変数も dfp をたど

ることでアクセス可能(たどるレベルはコンパイル時にわかる)

Page 12: 第 12 回  RHG の逆襲

next, redo next

処理:次のループに進む コンパイル結果: leave

redo 処理:ブロック実行をやり直す コンパイル結果: jump 0

ちなみに、 retry は使えなくなりました

Page 13: 第 12 回  RHG の逆襲

break

処理:ループ( yield したメソッド)を終了する

コンパイル結果: throw 2 break 時の 1 つ前の dfp とマッチする cfp

(メソッドを呼び出した cfp )を検索 マッチした cfp の pc から実行を再開 ブロック呼び出しが C 関数の場合、 JUMP_T

AG を利用して C のコールスタックを巻き戻し