の まとめ 2007/04/02 (Mon) / d;id:hzkr

Slides:



Advertisements
Similar presentations
アルゴリズムとデータ構造 第2回 線形リスト(復習).
Advertisements

2006/10/26 山下 諒蔵 佐藤 春旗 前田 俊行 大山 恵弘 佐藤 秀明 住井 英二郎
(Rubyistのための) 超音速:ML入門
Generic programming と STL
プログラミング演習II 2004年11月 30日(第6回) 理学部数学科・木村巌.
Rubyプログラムを高速に 実行するための処理系の開発
コンパイラ 2011年11月14日
ISD実習E 2009年6月29日 LISPシステム入門 (第5回) 関数ポインタ eval システム関数.
スレッドの同期と、スレッドの使用例 スレッドの同期 Lockオブジェクト: lockオブジェクトの生成
2008/03/01 D-BOF k.inaba はじめての initial D 2008/03/01 D-BOF k.inaba
情報理工学部 情報システム工学科 ラシキアゼミ3年 H 岡田 貴大
実行時のメモリ構造(1) Jasminの基礎とフレーム内動作
コンパイラ 第9回 コード生成 ― スタックマシン ―
システムプログラミング 第11回 シグナル 情報工学科  篠埜 功.
第2回:Javaの変数と型の宣言 プログラミングII 2007年10月2日.
侵入検知システム(IDS) 停止 IDS サーバへの不正アクセスが増加している
2006/10/19 山下 諒蔵 佐藤 春旗 前田 俊行 大山 恵弘 佐藤 秀明 住井英二郎
アルゴリズムとデータ構造 2011年6月13日
プログラミング論 II 電卓,逆ポーランド記法電卓
Stack & Queue & List 3.
  【事例演習6】  数式インタプリタ      解 説     “インタプリタの基本的な仕組み”.
①データ構造 ②アルゴリズム ③プログラム言語 ④マークアップ言語
プログラミング演習Ⅰ 課題2 10進数と2進数 2回目.
システムプログラミング 第11回 シグナル 情報工学科  篠埜 功.
ML 演習 第 7 回 新井淳也、中村宇佑、前田俊行 2011/05/31.
YARVの ソースを 読んでみた (コンパイラ前段編)
コンパイラの解析 (4) 例外処理.
プログラミング言語入門 手続き型言語としてのJava
第9章 例外処理,パッケージ 9.1 例外処理 9.2 ガーベッジコレクション.
独習JAVA 6.8 コンストラクタの修飾子 6.9 メソッドの修飾子 6.10 ObjectクラスとClassクラス 11月28日(金)
アルゴリズムとデータ構造1 2006年6月16日
プログラミング 4 記憶の割り付け.
プログラミング言語入門.
プログラミング入門2 第11回 情報工学科 篠埜 功.
岩村雅一 知能情報工学演習I 第10回(後半第4回) 岩村雅一
TA 高田正法 B10 CPUを作る 3日目 SPIMの改造 TA 高田正法
Webプロキシ HTTP1.0 ヒント CS-B3 ネットワークプログラミング  &情報科学科実験I.
Talkプログラムのヒント 1 CS-B3 ネットワークプログラミング  &情報科学科実験I.
コンパイラ資料 実行時環境.
プログラミング言語論 第四回 理工学部 情報システム工学科 新田直也.
アルゴリズムとデータ構造 2010年7月26日
C言語を用いたマシン非依存な JITコンパイラ作成フレームワーク
オブジェクト指向プログラミングと開発環境
岩村雅一 知能情報工学演習I 第12回(C言語第6回) 岩村雅一
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング言語論 第六回 理工学部 情報システム工学科 新田直也.
JAVAバイトコードにおける データ依存解析手法の提案と実装
第1章 いよいよプログラミング!! ~文章の表示 printf~
同期処理のモジュール化を 可能にする アスペクト指向言語
計算機プログラミングI 木曜日 1時限・5時限 担当: 増原英彦 第1回 2002年10月10日(木)
アルゴリズムとデータ構造 2012年6月11日
アルゴリズムとデータ構造1 2008年7月24日
アルゴリズムとデータ構造1 2009年6月15日
ネットワーク・プログラミング デバイスドライバと環境変数.
第5回 プログラミングⅡ 第5回
第6回放送授業.
ドキュメントジェネレータ 詳細仕様 長谷川啓
プログラムの一時停止時に 将来の実行情報を提供するデバッガ
コンパイラ 2012年10月11日
プログラミング 4 文字列.
コンパイラ 第12回 実行時環境 ― 変数と関数 ― 38号館4階N-411 内線5459
アルゴリズムとデータ構造 2010年6月17日
プログラミング演習I 2003年6月11日(第9回) 木村巌.
システムプログラミング 第11回 シグナル 情報工学科  篠埜 功.
プログラミング演習II 2003年12月10日(第7回) 木村巌.
情報処理Ⅱ 小テスト 2005年2月1日(火).
プログラミング入門2 第3回 条件分岐(2) 繰り返し文 篠埜 功.
1.2 言語処理の諸観点 (1)言語処理の利用分野
情報処理Ⅱ 第3回 2004年10月19日(火).
情報処理Ⅱ 2006年10月20日(金).
Presentation transcript:

http://d.hatena.ne.jp/hzkr/19000101 の まとめ 2007/04/02 (Mon) / d;id:hzkr YARVの ソースを 読んでみた (VM 編) http://d.hatena.ne.jp/hzkr/19000101 の まとめ 2007/04/02 (Mon) / d;id:hzkr

YARV とは プログラミング言語 Ruby の処理系 特徴 URI のひとつ ささだこういち さんによる実装 (Yet Another ではなくなった?) 特徴 Rubyコードを仮想マシン語にコンパイルして実行 速い! URI http://www.atdot.net/yarv/ http://svn.ruby-lang.org/repos/ruby/trunk/

参考資料 YARV Maniacs Ruby ソースコード完全解説 Ruby リファレンスマニュアル http://jp.rubyist.net/magazine/?0006-YarvManiacs Ruby ソースコード完全解説 http://i.loveruby.net/ja/rhg/ Ruby リファレンスマニュアル http://www.ruby-lang.org/ja/man/

流れ (mainからyarvまで) main @ main.c ruby_init @ eval.c スタート main @ main.c ruby_init @ eval.c 組み込みモジュールの初期化など ruby_options @ eval.c この辺りで構文解析。本家Rubyと共通。 (Rubyのコード文字列を、NODE型の木構造に変換) ruby_run @ eval.c ruby_exec @ eval.c ruby_exec_internal @ eval.c yarvcore_eval_parsed @ yarvcore.c

流れ (yarv評価器内) yarvcore_eval_parsed @ yarvcore.c th_compile_from_node @ yarvcore.c 構文木を、YARVマシン語列に変換(コンパイル) yarv_iseq_new_with_opt @ iseq.c iseq_compile @ compile.c iseq_compile_each @ compile.c 構文木→マシン語列の変換関数 iseq_setup @ compile.c 最適化などなど yarvcore_eval_iseq @ yarvcore.c マシン語列を、実行 ここを 読むよ

このスライドの、この後の流れ VMのデータ構造 VM スレッド スタック フレーム 実行開始! メインループ! 命令定義ファイル

VMのデータ構造 : VM struct rb_vm_struct @ yarvcore.h VMは「スレッドの集まり」 rb_thread_lock_t global_interpreter_lock; rb_thread_struct* main_thread; rb_thread_struct* running_thread; st_table* living_threads; …略… VMは「スレッドの集まり」 ある時点で稼働中のスレッドは常に1個 == running_thread == global_interpreter_lock をロックしてるスレッド http://www.atdot.net/~ko1/w3ml/w3ml.cgi/yarv-dev/msg/631 http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/30202

VMのデータ構造 : スレッド rb_thread_struct @ yarvcore.h スレッドは VALUE* stack; rb_control_frame_t* cfp; native_thread_data_t native_thread_data; …略… スレッドは 計算用「スタック」 現在の「制御フレーム」 あと、YARVスレッドはネイティブスレッドで実装されてるのでそのデータ

VMのデータ構造 : スタック VALUE* stack ただの配列 = ALLOC_N(VALUE, RUBY_VM_STACK_SIZE); ただの配列 YARVはスタックマシンなので、 ここに値をpushしたりpopしたりして計算 ちなみに RUBY_VM_STACK_SIZE は 128*1024 でした (2007/04/01現在)

VMのデータ構造 : 制御フレーム struct rb_control_frame_t @ yarvcore.h VMの今の状態を表す VALUE* pc; 命令ポインタ VALUE* sp; スタックポインタ rb_iseq_t* iseq; 現在の関数/ブロックの命令列 VALUE* lfp; ローカル変数テーブル などなどなどなど… VMの今の状態を表す 関数/ブロック呼び出しごとにスタック的に 制御フレームを積んでいく感じ

実行開始!

実行開始! メインスレッドの場合 Thread.new で作る新規スレッド 色々あるけど th_eval_body から実行開始 yarvcore_eval_iseq @ yarvcore.c rb_thread_eval @ vm.c th_eval_body @ vm.c Thread.new で作る新規スレッド thread_s_new @ thread.c thread_create_core @ thread.c native_thread_create @ thread_(pthread|win32).c thread_start_func_1 @ thread_(pthread|win32).c thread_start_func_2 @ thread.c th_invoke_proc invoke_block

th_eval_body (要約) th_eval がメインループ 例外発生時か Ruby実行終了時にreturn VALUE th_eval_body(rb_thread_t* th) {   if( … ) {     vm_loop_start:       th_eval(…);       if( th->state != 0 )         goto exception_handler;   }   else {       …     exception_handler: } 例外catchするハンドラを ここで地道に検索 ハンドラを見つけたら 制御フレーム巻き戻して goto vm_loop_start

return from th_eval 例外発生時 Rubyコード実行終了時 YARVの”throw”命令 YARVの”finish”命令 メソッド終了時 (YARVの”leave”命令) には、いちいち th_eval を抜けたりしない

メインループ!

th_eval 命令1個読んでswitch&実行,の無限ループ …するコードを #include VALUE th_eval( rb_thread_t* th, VALUE initial ) { INSN_DISPATCH(); #include “vm.inc” END_INSN_DISPATCH(); }

vm.inc マクロ展開するとだいたいこんな感じ 「命令定義ファイル(insns.def)」から Rubyスクリプトで生成される! (スレッデッドコード最適化OFFの場合) 「命令定義ファイル(insns.def)」から  Rubyスクリプトで生成される! while(1)   switch(*cfp->pc)   {   case YARVINSN_leave: …   case YARVINSN_finish: …   case YARVINSN_branchif: …   …                            // などなど…   }

命令定義ファイル

insns.def 各YARV命令の実装を専用記法で書いた物 命令の 名前 引数リスト スタックからPOPする変数名のリスト スタックにPUSHする変数名 実際に実行する処理(ここはC言語で書く)

insns.def 例 : getlocal 指定された番号のローカル変数の値を スタックに積む命令 DEFINE_INSN 指定された番号のローカル変数の値を  スタックに積む命令 ローカル変数にアクセスするときに使われてる命令 DEFINE_INSN getlocal                       ← 命令の名前 (lindex_t idx)                ← 命令の引数(ローカル変数番号) ()                                ← スタックからPOPする値(なし) (VALUE val)                  ← スタックにPUSHする値 {   val = *(GET_LFP() – idx);   ← 実装(制御フレームからローカル                                            変数領域を取得してそこの値をget) }

insns.def 例 : tostring スタックトップにある値をString化して スタックに置き直す命令 “#{…}” とかで使われてる命令 DEFINE_INSN tostring                      ← 命令の名前 ()                                ← 命令の引数(なし) (VALUE val)                   ← スタックからPOPする値 (VALUE val)                   ← スタックにPUSHする値 {   val = rb_obj_as_string(val);   ← 実装(オブジェクトの表現などは                                            従来のRubyと同じなので、                                            従来の実装と同じ関数でOk) }

insns.def 例 : jump 指定された距離だけpc(次に実行する命令のアドレス)を動かす命令 whileやifなどなどで使われてる命令 DEFINE_INSN jump                           ← 命令の名前 (OFFSET dst)                ← 命令の引数(ジャンプ距離) ()                                ← スタックからPOPする値(なし) ()                               ← スタックにPUSHする値(なし) {    RUBY_VM_CHECK_INTS();  ← 各種ジャンプ命令のタイミングで                                           割り込みチェック&スレッド切替してるみたい    JUMP(dst);                    ← 実装 (cfp->pc += dst) }

insns.def 例 : putobject 指定されたオブジェクトをスタックに積む 1 とか true とか即値を書いたときに使われる命令 C実装の部分が空でちょっとかっこいい DEFINE_INSN putobject                     ← 命令の名前 (VALUE val)                  ← 命令の引数(オブジェクト) ()                                 ← スタックからPOPする値(なし) (VALUE val)                  ← スタックにPUSHする値 { }

まとめ YARVの、VM実装 …の部分のコードを読んだ結果をまとめました 超ダイジェスト版なので、物足りない方はぜひぜひ http://svn.ruby-lang.org/repos/ruby/trunk/ を読みましょう!!