From 843a78478f9061b02af9258f45fe36468652c45a Mon Sep 17 00:00:00 2001 From: shyouhei Date: Fri, 12 Jan 2018 08:38:09 +0000 Subject: delete tool/instruction.rb (2nd try) Previous commit changed insns.def format. Now is the time for its generators. In doing so I chose to modernize the system, not just patch. My attempt includes - extensive use of Onigumo regular expressions - split from one big file (instruction.rb) into separated MVC - partial view Also, let me take this opportunity to kill old unused features such as - stack caching - minsns / yasmdata which are never seriously used - yarvarch document generation (moved to doc/) - vast majority of unused arguments to insns2vm.rb This commit generates VM source codes that cleanly compile, and the generated binary passes tests. At least for me. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61784 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- Makefile.in | 2 +- common.mk | 16 - compile.c | 7 +- doc/yarvarch.en | 7 + doc/yarvarch.ja | 454 +++++++ template/insns.inc.tmpl | 24 - template/insns_info.inc.tmpl | 134 --- template/minsns.inc.tmpl | 14 - template/opt_sc.inc.tmpl | 35 - template/optinsn.inc.tmpl | 78 -- template/optunifs.inc.tmpl | 67 -- template/vm.inc.tmpl | 33 - template/vmtc.inc.tmpl | 21 - template/yarvarch.en | 7 - template/yarvarch.ja | 454 ------- template/yasmdata.rb.tmpl | 20 - tool/insns2vm.rb | 15 +- tool/instruction.rb | 1250 -------------------- tool/ruby_vm/controllers/application_controller.rb | 24 + tool/ruby_vm/helpers/c_escape.rb | 120 ++ tool/ruby_vm/helpers/dumper.rb | 108 ++ tool/ruby_vm/helpers/scanner.rb | 49 + tool/ruby_vm/loaders/insns_def.rb | 92 ++ tool/ruby_vm/loaders/opt_insn_unif_def.rb | 34 + tool/ruby_vm/loaders/opt_operand_def.rb | 57 + tool/ruby_vm/loaders/vm_opts_h.rb | 37 + tool/ruby_vm/models/attribute.rb | 44 + tool/ruby_vm/models/bare_instructions.rb | 162 +++ tool/ruby_vm/models/c_expr.rb | 41 + tool/ruby_vm/models/instructions.rb | 22 + tool/ruby_vm/models/instructions_unifications.rb | 43 + tool/ruby_vm/models/operands_unifications.rb | 137 +++ tool/ruby_vm/models/trace_instructions.rb | 71 ++ tool/ruby_vm/models/typemap.rb | 61 + tool/ruby_vm/scripts/insns2vm.rb | 88 ++ tool/ruby_vm/views/_attributes.erb | 34 + tool/ruby_vm/views/_c_expr.erb | 17 + tool/ruby_vm/views/_copyright.erb | 31 + tool/ruby_vm/views/_insn_entry.erb | 50 + tool/ruby_vm/views/_insn_len_info.erb | 23 + tool/ruby_vm/views/_insn_name_info.erb | 47 + tool/ruby_vm/views/_insn_operand_info.erb | 59 + tool/ruby_vm/views/_insn_stack_increase.erb | 53 + tool/ruby_vm/views/_insn_type_chars.erb | 12 + tool/ruby_vm/views/_notice.erb | 22 + tool/ruby_vm/views/_trace_instruction.erb | 16 + tool/ruby_vm/views/insns.inc.erb | 26 + tool/ruby_vm/views/insns_info.inc.erb | 19 + tool/ruby_vm/views/opt_sc.inc.erb | 40 + tool/ruby_vm/views/optinsn.inc.erb | 71 ++ tool/ruby_vm/views/optunifs.inc.erb | 21 + tool/ruby_vm/views/vm.inc.erb | 30 + tool/ruby_vm/views/vmtc.inc.erb | 21 + vm_exec.h | 3 + win32/Makefile.sub | 2 +- 55 files changed, 2256 insertions(+), 2169 deletions(-) create mode 100644 doc/yarvarch.en create mode 100644 doc/yarvarch.ja delete mode 100644 template/insns.inc.tmpl delete mode 100644 template/insns_info.inc.tmpl delete mode 100644 template/minsns.inc.tmpl delete mode 100644 template/opt_sc.inc.tmpl delete mode 100644 template/optinsn.inc.tmpl delete mode 100644 template/optunifs.inc.tmpl delete mode 100644 template/vm.inc.tmpl delete mode 100644 template/vmtc.inc.tmpl delete mode 100644 template/yarvarch.en delete mode 100644 template/yarvarch.ja delete mode 100644 template/yasmdata.rb.tmpl delete mode 100755 tool/instruction.rb create mode 100644 tool/ruby_vm/controllers/application_controller.rb create mode 100644 tool/ruby_vm/helpers/c_escape.rb create mode 100644 tool/ruby_vm/helpers/dumper.rb create mode 100644 tool/ruby_vm/helpers/scanner.rb create mode 100644 tool/ruby_vm/loaders/insns_def.rb create mode 100644 tool/ruby_vm/loaders/opt_insn_unif_def.rb create mode 100644 tool/ruby_vm/loaders/opt_operand_def.rb create mode 100644 tool/ruby_vm/loaders/vm_opts_h.rb create mode 100644 tool/ruby_vm/models/attribute.rb create mode 100644 tool/ruby_vm/models/bare_instructions.rb create mode 100644 tool/ruby_vm/models/c_expr.rb create mode 100644 tool/ruby_vm/models/instructions.rb create mode 100644 tool/ruby_vm/models/instructions_unifications.rb create mode 100644 tool/ruby_vm/models/operands_unifications.rb create mode 100644 tool/ruby_vm/models/trace_instructions.rb create mode 100644 tool/ruby_vm/models/typemap.rb create mode 100644 tool/ruby_vm/scripts/insns2vm.rb create mode 100644 tool/ruby_vm/views/_attributes.erb create mode 100644 tool/ruby_vm/views/_c_expr.erb create mode 100644 tool/ruby_vm/views/_copyright.erb create mode 100644 tool/ruby_vm/views/_insn_entry.erb create mode 100644 tool/ruby_vm/views/_insn_len_info.erb create mode 100644 tool/ruby_vm/views/_insn_name_info.erb create mode 100644 tool/ruby_vm/views/_insn_operand_info.erb create mode 100644 tool/ruby_vm/views/_insn_stack_increase.erb create mode 100644 tool/ruby_vm/views/_insn_type_chars.erb create mode 100644 tool/ruby_vm/views/_notice.erb create mode 100644 tool/ruby_vm/views/_trace_instruction.erb create mode 100644 tool/ruby_vm/views/insns.inc.erb create mode 100644 tool/ruby_vm/views/insns_info.inc.erb create mode 100644 tool/ruby_vm/views/opt_sc.inc.erb create mode 100644 tool/ruby_vm/views/optinsn.inc.erb create mode 100644 tool/ruby_vm/views/optunifs.inc.erb create mode 100644 tool/ruby_vm/views/vm.inc.erb create mode 100644 tool/ruby_vm/views/vmtc.inc.erb diff --git a/Makefile.in b/Makefile.in index 25075f5900..48dd90d32c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -515,7 +515,7 @@ INSNS = opt_sc.inc optinsn.inc optunifs.inc insns.inc insns_info.inc \ $(INSNS): $(srcdir)/insns.def vm_opts.h \ $(srcdir)/defs/opt_operand.def $(srcdir)/defs/opt_insn_unif.def \ - $(srcdir)/tool/instruction.rb $(srcdir)/tool/insns2vm.rb + $(srcdir)/tool/insns2vm.rb $(ECHO) generating $@ $(Q) $(BASERUBY) -Ku $(srcdir)/tool/insns2vm.rb $(INSNS2VMOPT) $@ diff --git a/common.mk b/common.mk index 9312949d8e..a064467991 100644 --- a/common.mk +++ b/common.mk @@ -872,22 +872,6 @@ $(OBJS): {$(VPATH)}config.h {$(VPATH)}missing.h INSNS2VMOPT = --srcdir="$(srcdir)" -{$(VPATH)}minsns.inc: $(srcdir)/template/minsns.inc.tmpl - -{$(VPATH)}opt_sc.inc: $(srcdir)/template/opt_sc.inc.tmpl - -{$(VPATH)}optinsn.inc: $(srcdir)/template/optinsn.inc.tmpl - -{$(VPATH)}optunifs.inc: $(srcdir)/template/optunifs.inc.tmpl - -{$(VPATH)}insns.inc: $(srcdir)/template/insns.inc.tmpl - -{$(VPATH)}insns_info.inc: $(srcdir)/template/insns_info.inc.tmpl - -{$(VPATH)}vmtc.inc: $(srcdir)/template/vmtc.inc.tmpl - -{$(VPATH)}vm.inc: $(srcdir)/template/vm.inc.tmpl - srcs_vpath = {$(VPATH)} common-srcs: $(srcs_vpath)parse.c $(srcs_vpath)lex.c $(srcs_vpath)enc/trans/newline.c $(srcs_vpath)id.c \ srcs-lib srcs-ext incs diff --git a/compile.c b/compile.c index 1e1d85ab3e..87a1bd8f6a 100644 --- a/compile.c +++ b/compile.c @@ -15,7 +15,6 @@ #include "encindex.h" #include -#define USE_INSN_STACK_INCREASE 1 #include "vm_core.h" #include "vm_debug.h" #include "iseq.h" @@ -7373,16 +7372,16 @@ dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr, const char * rb_insns_name(int i) { - return insn_name_info[i]; + return insn_name(i); } VALUE rb_insns_name_array(void) { - VALUE ary = rb_ary_new(); + VALUE ary = rb_ary_new_capa(VM_INSTRUCTION_SIZE); int i; for (i = 0; i < VM_INSTRUCTION_SIZE; i++) { - rb_ary_push(ary, rb_fstring_cstr(insn_name_info[i])); + rb_ary_push(ary, rb_fstring_cstr(insn_name(i))); } return rb_obj_freeze(ary); } diff --git a/doc/yarvarch.en b/doc/yarvarch.en new file mode 100644 index 0000000000..7a76e25b7e --- /dev/null +++ b/doc/yarvarch.en @@ -0,0 +1,7 @@ +#title YARV: Yet another RubyVM - Software Architecture + +maybe writing. + +* YARV instruction set + +<%= d %> diff --git a/doc/yarvarch.ja b/doc/yarvarch.ja new file mode 100644 index 0000000000..2739ec6b14 --- /dev/null +++ b/doc/yarvarch.ja @@ -0,0 +1,454 @@ +#title YARVアーキテクチャ +#set author 日本 Ruby の会 ささだこういち + + +- 2005-03-03(Thu) 00:31:12 +0900 いろいろと書き直し + +---- + +* これは? + +[[YARV: Yet Another RubyVM|http://www.atdot.net/yarv]] の 設計メモです。 + + +YARV は、Ruby プログラムのための次の機能を提供します。 + +- Compiler +- VM Generator +- VM (Virtual Machine) +- Assembler +- Dis-Assembler +- (experimental) JIT Compiler +- (experimental) AOT Compiler + + +現在の YARV は Ruby インタプリタの拡張ライブラリとして実装しています。こ +れにより、Ruby インタプリタの必要な機能(パーサ、オブジェクト管理、既存 +の拡張ライブラリ)などがほぼそのまま利用できます。 + +ただし、いくつかのパッチを Ruby インタプリタに当てなければなりません。 + +今後は、Ruby 本体のインタプリタ部分(eval.c)を置き換えることを目指して +開発を継続する予定です。 + + +* Compiler (compile.h, compile.c) + +コンパイラは、Ruby インタプリタのパーサによって生成された構文木(RNode +データによる木)を YARV 命令列に変換します。YARV 命令については後述しま +す。 + +とくに難しいことはしていませんが、スコープなどの開始時にローカル変数の初 +期化などを行い、あとは構文木を辿り変換していきます。 + +変換中は Ruby の Array オブジェクトに YARV 命令オブジェクト、およびオペ +ランドを格納していき、最後に実行できる形に変換します。コンパイラでは、コ +ンパイル中に生成するメモリ領域の管理が問題になることがありますが、YARV +の場合、Ruby インタプリタがすべて面倒をみてくれるのでこの部分は非常に楽 +に作ることができました(ガーベージコレクタによって自動的にメモリ管理をし +てくれるため)。 + +YARV 命令は、命令を示す識別子、オペランドなど、すべて 1 word (マシンで +表現できる自然な値。C 言語ではポインタのサイズ。Ruby インタプリタ用語で +は VALUE のサイズ)で表現されます。そのため、YARV 命令はいわゆる「バイト +コード」ではありません。そのため、YARV の説明などでは「命令列」という用 +語を使っています。 + +1 word であるため、メモリの利用効率は多少悪くなりますが、アクセス速度な +どを考慮すると、本方式が一番いいと考えております。たとえばオペランドをコ +ンスタントプールに格納し、インデックスのみをオペランドで示すことも可能で +すが、間接アクセスになってしまうので性能に影響が出るため、却下しました。 + + +* VM Generator (rb/insns2vm.rb, insns.def) + +rb/insns2vm.rb というスクリプトは、insns.def というファイルを読み込み、 +VM のために必要なファイルを生成します。具体的には、命令を実行する部分を +生成しますが、ほかにもコンパイルに必要な情報、最適化に必要な情報、やアセ +ンブラ、逆アセンブラに必要な情報を示すファイルも生成します。 + + +** 命令記述 + +insns.def には、各命令がどのような命令であるかを記述します。具体的には次 +の情報を記述します。 + +- 命令の名前 +- その命令のカテゴリ、コメント(英語、日本語) +- オペランドの名前 +- その命令実行前にスタックからポップする値 +- その命令実行後にスタックにプッシュする値 +- その命令のロジック(C 言語で記述) + +たとえば、スタックに self をおく putself という命令は次のように記述しま +す。 + +#code +/** + @c put + @e put self. + @j self を置く。 + */ +DEFINE_INSN +putself +() +() +(VALUE val) +{ + val = GET_SELF(); +} +#end + +この場合、オペランドと、スタックからポップする値は無いことになります。命 +令終了後、self をスタックトップに置きたいわけですが、それは val という、 +スタックにプッシュする値として宣言しておいた変数に代入しておくことで、こ +れを変換するとスタックトップに置く C プログラムが生成されます。 + +細かいフォーマットは insns.def の冒頭を参照してください。そんなに難しく +ないと思います。 + +insnhelper.h というファイルに、命令ロジックを記述するために必要なマクロ +が定義されています。また、VM の内部構造に関する定義は vm.h というファイ +ルにあります。 + + +* VM (Virtual Machine, vm.h, vm.c) + +VM は、実際にコンパイルした結果生成される YARV 命令列を実行します。まさ +に、この部分が YARV のキモになり、将来的には eval.c をこの VM で置き換え +たいと考えています。 + +現在の Ruby インタプリタで実行できるすべてのことが、この VM で実現できる +ように作っています(現段階ではまだ完全ではありませんが、そうなるべきです)。 + +VM は、単純なスタックマシンとして実装しています。スレッドひとつにスタッ +クひとつを保持します。スタックの領域はヒープから取得するので、柔軟な領域 +設定が可能です。 + + +** レジスタ + +VM は 5 つの仮想的なレジスタによって制御されます。 + +- PC (Program Counter) +- SP (Stack Pointer) +- CFP (Control Frame Pointer) +- LFP (Local Frame Pointer) +- DFP (Dynamic Frame Pointer) + +PC は現在実行中の命令列の位置を示します。SP はスタックトップの位置を示し +ます。CFP、LFP、DFP はそれぞれフレームの情報を示します。詳細は後述します。 + + +** スタックフレーム + +obsolete (update soon) + + +** フレームデザインについての補足 + +Lisp の処理系などをかんがえると、わざわざブロックローカルフレームとメソ +ッドローカルフレームのようなものを用意するのは奇異に見えるかもしれません。 +あるフレームを、入れ子構造にして、ローカル変数のアクセスはその入れ子を外 +側に辿れば必ずたどり着くことができるからです(つまり、lfp は必要ない)。 + +しかし、Ruby ではいくつか状況が違います。まず、メソッドローカルな情報が +あること、具体的にはブロックとself(callee からみると receiver)です。こ +の情報をそれぞれのフレームにもたせるのは無駄です。 + +また、Ruby2.0 からはブロックローカル変数はなくなります(ブロックローカル +引数は残るので、構造自体はあまり変わりません)。そのため、メソッドローカ +ル変数へのアクセスが頻発することが予想されます。 + +このとき、メソッドローカル変数へのアクセスのたびにフレーム(スコープ)の +リストをたどるのは無駄であると判断し、明示的にメソッドローカルスコープと +ブロックフレームを分離し、ブロックフレームからはメソッドローカルフレーム +が lfpレジスタによって容易にアクセスできるようにしました。 + + +** メソッド呼び出しについて + +メソッド呼び出しは、YARV 命令列で記述されたメソッドか、C で記述されたメ +ソッドかによってディスパッチ手法が変わります。 + +YARV 命令列であった場合、上述したスタックフレームを作成して命令を継続し +ます。とくに VM の関数を再帰呼び出すすることは行ないません。 + +C で記述されたメソッドだった場合、単純にその関数を呼び出します(ただし、 +バックトレースを正しく生成するためにメソッド呼び出しの情報を付加してから +行ないます)。 + +このため、VM 用スタックを別途用意したものの、プログラムによってはマシン +スタックを使い切ってしまう可能性があります(C -> Ruby -> C -> ... という +呼び出しが続いた場合)。これは、現在では避けられない仕様となっています。 + + +** 例外 + +例外は、Java の JVM と同様に例外テーブルを用意することで実現します。例外 +が発生したら、当該フレームを、例外テーブルを検査します。そこで、例外が発 +生したときの PC の値に合致するエントリがあった場合、そのエントリに従って +動作します。もしエントリが見つからなかった場合、スタックを撒き戻してまた +同様にそのスコープの例外テーブルを検査します。 + +また、break、return(ブロック中)、retry なども同様の仕組みで実現します。 + +*** 例外テーブル + +例外テーブルエントリは具体的には次の情報が格納されています。 + +- 対象とする PC の範囲 +- 対象とする例外の種類 +- もし対象となったときにジャンプする先(種類による) +- もし対象となったときに起動するブロックの iseq + + +*** rescue + +rescue 節はブロックとして実現しています。$! の値を唯一の引数として持ちま +す。 + +#code +begin +rescue A +rescue B +rescue C +end +#end + +は、次のような Ruby スクリプトに変換されます。 + +#code +{|err| + case err + when A === err + when B === err + when C === err + else + raise # yarv の命令では throw + end +} +#end + + +*** ensure + +正常系(例外が発生しなかった場合)と異常系(例外が発生したときなど)の2 +種類の命令列が生成されます。正常系では、ただの連続したコード領域としてコ +ンパイルされます。また、異常系ではブロックとして実装します。最後は必ず +throw 命令で締めることになります。 + + +*** break, return(ブロック中)、retry + +break 文、ブロック中の return 文、retry 文は throw 命令としてコンパイル +されます。どこまで戻るかは、break をフックする例外テーブルのエントリが判 +断します。 + + +** 定数の検索 + +定数という名前なのに、Ruby ではコンパイル時に決定しません。というか、い +つまでも再定義可能になっています。 + +定数アクセスのためのRuby記述は次のようになります。 + +#code +Ruby表現: +expr::ID::...::ID +#end + +これは、yarv命令セットでは次のようになります。 + +#code +(expr) +getconstant ID +... +getconstant ID +#end + + +*** 定数検索パス + +もし expr が nil だった場合、定数検索パスに従って定数を検索します。この +挙動は今後 Ruby 2.0 に向けて変更される場合があります。 + ++ クラス、モジュールの動的ネスト関係(プログラムの字面上)をルートまで辿る ++ 継承関係をルート(Object)まで辿る + +このため、クラス、モジュールの動的ネスト関係を保存しなければなりません。 +このために、thread_object には klass_nest_stack というものを用意しました。 +これは、現在のネストの情報を保存します。 + +メソッド定義時、その現在のネスト情報をメソッド定義時に(dupして)加える +ことで、そのメソッドの実行時、そのネスト情報を参照することが可能になりま +す。 + +トップレベルでは、その情報はないことになります。 + +クラス/モジュール定義文実行時は、現在の情報そのものを参照することになり +ます。これは、クラススコープ突入時、その情報をクラス定義文にコピーします +(すでにコピーされていれば、これを行いません)。 + +これにより、動的なネスト情報を統一的に扱うことができます。 + + +** 最適化手法 + +YARV では高速化を目的としているので、さまざまな最適化手法を利用していま +す。詳細は割愛しますが、以下に述べる最適化などを行なっております。 + + +*** threaded code + +GCC の C 言語拡張である値としてのラベルを利用して direct threaded code +を実現しています。 + + +*** Peephole optimization + +いくつかの簡単な最適化をしています。 + + +*** inline method cache + +命令列の中にメソッド検索結果を埋め込みます。 + + +*** inline constant cache + +命令列の中に定数検索結果を埋め込みます。 + + +*** ブロックと Proc オブジェクトの分離 + +ブロック付きメソッド呼び出しが行なわれたときにはすぐにはブロックを Proc +オブジェクトとして生成しません。これにより、必要ない Proc オブジェクトの +生成を抑えています。 + +Proc メソッドは、実際に必要になった時点で作られ、そのときに環境(スコー +プ上に確保された変数など)をヒープに保存します。 + + +*** 特化命令 + +Fixnum 同士の加算などを正直に関数呼び出しによって行なうと、コストがかか +るので、これらのプリミティブな操作を行なうためのメソッド呼び出しは専用命 +令を用意しました。 + + +*** 命令融合 + +複数の命令を 1 命令に変換します。融合命令は opt_insn_unif.def の記述によ +り自動的に生成されます。 + + +*** オペランド融合 + +複数のオペランドを含めた命令を生成します。融合命令は opt_operand.def の +記述によって自動的に生成されます。 + + +*** stack caching + +スタックトップを仮想レジスタに保持するようにします。現在は 2 個の仮想レ +ジスタを想定し、5状態のスタックキャッシングを行ないます。スタックキャッ +シングする命令は自動的に生成されます。 + + +*** JIT Compile + +機械語を切り貼りします。非常に実験的なコードものしか作っておりません。ほ +とんどのプログラムは動きません。 + + +*** AOT Compile + +YARV 命令列を C 言語に変換します。まだ十分な最適化を行なえておりませんが、 +それなりに動きます。rb/aotc.rb がコンパイラです。 + + +* Assembler (rb/yasm.rb) + +YARV 命令列のアセンブラを用意しました。使い方は rb/yasm.rb を参照してく +ださい(まだ、例示してある生成手法のすべてをサポートしているわけではあり +ません)。 + + +* Dis-Assembler (disasm.c) + +YARV 命令列を示すオブジェクト YARVCore::InstructionSequence には disasm +メソッドがあります。これは、命令列を逆アセンブルした文字列を返します。 + + +* YARV 命令セット + +<%= d %> + +* その他 + +** テスト + +test/test_* がテストケースです。一応、ミスなく動くはずです。逆にいうと、 +このテストに記述されている例ではきちんと動作するということです。 + + +** ベンチマーク + +benchmark/bm_* にベンチマークプログラムがおいてあります。 + + +** 今後の予定 + +まだまだやらなければいけないこと、未実装部分がたくさんありますんでやって +いかなければなりません。一番大きな目標は eval.c を置き換えることでしょう +か。 + + +*** Verifier + +YARV 命令列は、ミスがあっても動かしてしまうため危険である可能性がありま +す。そのため、スタックの利用状態をきちんと事前に検証するようなベリファイ +アを用意しなければならないと考えています。 + + +*** Compiled File の構想 + +Ruby プログラムをこの命令セットにシリアライズしたデータ構造をファイルに +出力できるようにしたいと考えています。これを利用して一度コンパイルした命 +令列をファイルに保存しておけば、次回ロード時にはコンパイルの手間、コスト +を省くことができます。 + + +**** 全体構成 + +次のようなファイル構成を考えていますが、まだ未定です。 + +#code +u4 : 4 byte unsigned storage +u2 : 2 byte unsigned storage +u1 : 1 byte unsigned storage + +every storages are little endian :-) + +CompiledFile{ + u4 magic; + + u2 major; + u2 minor; + + u4 character_code; + + u4 constants_pool_count; + ConstantEntry constants_pool[constants_pool_count]; + + u4 block_count; + blockEntry blocks[block_count]; + + u4 method_count; + MethodEntry methods[method_count]; +} +#end + +Java classfile のパクリ。 + diff --git a/template/insns.inc.tmpl b/template/insns.inc.tmpl deleted file mode 100644 index 112732dce5..0000000000 --- a/template/insns.inc.tmpl +++ /dev/null @@ -1,24 +0,0 @@ -/** -*-c-*- - This file contains YARV instructions list. - - ---- - This file is auto generated by insns2vm.rb - DO NOT TOUCH! - - If you want to fix something, you must edit 'template/insns.inc.tmpl' - or tool/insns2vm.rb - */ - - -/* BIN : Basic Instruction Name */ -#define BIN(n) YARVINSN_##n - -enum ruby_vminsn_type { -% @insns.each do |insn| - BIN(<%=insn.name%>), -% end - VM_INSTRUCTION_SIZE -}; - -#define ASSERT_VM_INSTRUCTION_SIZE(array) \ - STATIC_ASSERT(numberof_##array, numberof(array) == VM_INSTRUCTION_SIZE) diff --git a/template/insns_info.inc.tmpl b/template/insns_info.inc.tmpl deleted file mode 100644 index 546368d984..0000000000 --- a/template/insns_info.inc.tmpl +++ /dev/null @@ -1,134 +0,0 @@ -/** -*-c-*- - This file contains instruction information for yarv instruction sequence. - - ---- - This file is auto generated by insns2vm.rb - DO NOT TOUCH! - - If you want to fix something, you must edit 'template/insns_info.inc.tmpl' - or tool/insns2vm.rb - */ - -% TYPE_CHARS.each do |t, c| -#define <%=t%> '<%=c%>' -% end - -extern const struct rb_vm_insn_name_info rb_vm_insn_name_info_base; -extern const unsigned short rb_vm_insn_name_info_offset[]; -extern const char rb_vm_insn_operand_info[][8]; -extern const unsigned short rb_vm_insn_stack_push_num_info[]; -#define insn_name_info (const char *)&rb_vm_insn_name_info_base+rb_vm_insn_name_info_offset -#define insn_operand_info rb_vm_insn_operand_info -#define rb_vm_insn_stack_push_num_info insn_stack_push_num_info - -#ifdef RUBY_VM_INSNS_INFO -const unsigned short rb_vm_insn_name_info_offset[] = { -% insn_name_length = @insns.inject(0) do |ofs, insn| - <%= ofs %>, -% ofs + insn.name.size + 1 -% end -}; - -ASSERT_VM_INSTRUCTION_SIZE(rb_vm_insn_name_info_offset); - -% n = 100 -const struct rb_vm_insn_name_info { -% @insns.each_with_index do |insn, i| -% if (i % n) == 0 - struct { -% end - char L<%=i%n%>[<%= insn.name.size+1 %>]; -% if (i % n) == n - 1 or i == @insns.size - 1 - } S<%=i / n%>; -% end -% end -} rb_vm_insn_name_info_base = { -% @insns.each_with_index do |insn, i| -% if (i % n) == 0 - { -% end - "<%= insn.name %>", -% if (i % n) == n - 1 or i == @insns.size - 1 - }, -% end -% end -}; - -const char rb_vm_insn_operand_info[][8] = { -% @insns.each do |insn| - "\<%= (insn.opes.size+1).to_s(8) %>""<% - insn.opes.each {|type, _| - %><%=TYPE_CHARS.fetch(op2typesig(type))%><% - }%>", -% end -}; - -ASSERT_VM_INSTRUCTION_SIZE(rb_vm_insn_operand_info); - -#ifdef USE_INSN_RET_NUM -const unsigned short rb_vm_insn_stack_push_num_info[] = { -% @insns.each do |insn| - <%= insn.rets.size %>, -% end -}; - -ASSERT_VM_INSTRUCTION_SIZE(rb_vm_insn_stack_push_num_info); -#endif -#endif - -#ifdef USE_INSN_STACK_INCREASE -static int -insn_stack_increase(int depth, int insn, VALUE *opes) -{ - switch (insn) { -% @insns.each do |insn| - case BIN(<%= insn.name %>): { - <%= insn.sp_increase_c_expr %> - } -% end - default: - rb_bug("insn_sp_increase: unreachable"); - } - return 0; -} -#endif - -/* some utilities */ - -static int -insn_len(VALUE insn) -{ - return (unsigned char)insn_operand_info[(int)insn][0]; -} - -static const char * -insn_name(VALUE insn) -{ - return insn_name_info[(int)insn]; -} - -static const char * -insn_op_types(VALUE insn) -{ - return insn_operand_info[(int)insn]+1; -} - -static int -insn_op_type(VALUE insn, long pos) -{ - int len = insn_len(insn) - 1; - if (pos < len) { - return insn_operand_info[(int)insn][pos+1]; - } - else{ - return 0; - } -} - -#ifdef USE_INSN_RET_NUM -static int -insn_ret_num(VALUE insn) -{ - return insn_stack_push_num_info[(int)insn]; -} -#endif diff --git a/template/minsns.inc.tmpl b/template/minsns.inc.tmpl deleted file mode 100644 index f32b28cb7f..0000000000 --- a/template/minsns.inc.tmpl +++ /dev/null @@ -1,14 +0,0 @@ -/** -*-c-*- - This file contains YARV instructions list, to define YARVCore::Instructions. - - ---- - This file is auto generated by insns2vm.rb - DO NOT TOUCH! - - If you want to fix something, you must edit 'template/minsns.inc.tmpl' - or tool/insns2vm.rb - */ - -% @insns.each_with_index do |insn, i| - rb_define_const(mYarvInsns, "I<%=insn.name%>", INT2FIX(<%=i%>)); -% end diff --git a/template/opt_sc.inc.tmpl b/template/opt_sc.inc.tmpl deleted file mode 100644 index 4c85f96c0f..0000000000 --- a/template/opt_sc.inc.tmpl +++ /dev/null @@ -1,35 +0,0 @@ -/* -*-c-*- *********************************************************/ -/*******************************************************************/ -/*******************************************************************/ -/** - This file is for threaded code. - - ---- - This file is auto generated by insns2vm.rb - DO NOT TOUCH! - - If you want to fix something, you must edit 'template/opt_sc.inc.tmpl' - or tool/insns2vm.rb - */ - -#define SC_STATE_SIZE 6 - -#define SCS_XX 1 -#define SCS_AX 2 -#define SCS_BX 3 -#define SCS_AB 4 -#define SCS_BA 5 - -#define SC_ERROR 0xffffffff - -static const VALUE sc_insn_info[][SC_STATE_SIZE] = { -<%= sc_insn_info %> -}; - -ASSERT_VM_INSTRUCTION_SIZE(sc_insn_info); - -static const VALUE sc_insn_next[] = { -<%= sc_insn_next %> -}; - -ASSERT_VM_INSTRUCTION_SIZE(sc_insn_next); diff --git a/template/optinsn.inc.tmpl b/template/optinsn.inc.tmpl deleted file mode 100644 index b1fba6dea3..0000000000 --- a/template/optinsn.inc.tmpl +++ /dev/null @@ -1,78 +0,0 @@ -/* -*-c-*- *********************************************************/ -/*******************************************************************/ -/*******************************************************************/ -/** - This file is for threaded code. - - ---- - This file is auto generated by insns2vm.rb - DO NOT TOUCH! - - If you want to fix something, you must edit 'template/optinsn.inc.tmpl' - or tool/insns2vm.rb - */ - -static INSN * -insn_operands_unification(INSN *insnobj) -{ -#ifdef OPT_OPERANDS_UNIFICATION - /* optimize rule */ - switch(insnobj->insn_id){ -% opt_insns_map.each do |originsn, optinsns| - case BIN(<%=originsn.name%>): -% optinsns.each {|opti| - if ( -% opti.defopes.each_with_index {|opinfo, i| -% next if opinfo[1] == '*' - insnobj->operands[<%=i%>] == <%=val_as_type(opinfo)%> && -% } - 1) { -% idx = 0 -% opti.defopes.each_with_index {|opinfo, n| -% if opinfo[1] == '*' -% if idx != n - insnobj->operands[<%=idx%>] = insnobj->operands[<%=n%>]; -% end -% idx += 1 -% end -% } - insnobj->insn_id = BIN(<%=opti.name%>); - insnobj->operand_size = <%=idx%>; - break; - } -% } - break; -% end - - default: - /* do nothing */; - break; - } -#endif - return insnobj; -} - -int -rb_insn_unified_local_var_level(VALUE insn) -{ -#ifdef OPT_OPERANDS_UNIFICATION - /* optimize rule */ - switch (insn) { -% opt_insns_map.each do |originsn, optinsns| -% optinsns.each {|opti| - case BIN(<%=opti.name%>): -% opti.defopes.each {|opinfo| -% next if opinfo[1] == '*' - return <%=opinfo[1]%>; -% break -% } -% } -% end - - default: - /* do nothing */; - break; - } -#endif - return -1; -} diff --git a/template/optunifs.inc.tmpl b/template/optunifs.inc.tmpl deleted file mode 100644 index 3ac5872346..0000000000 --- a/template/optunifs.inc.tmpl +++ /dev/null @@ -1,67 +0,0 @@ -/* -*-c-*- *********************************************************/ -/*******************************************************************/ -/*******************************************************************/ -/** - This file is for threaded code. - - ---- - This file is auto generated by insns2vm.rb - DO NOT TOUCH! - - If you want to fix something, you must edit 'template/optunifs.inc.tmpl' - or tool/insns2vm.rb - */ - -/* - static const int UNIFIED_insn_name_1[] = {id, size, ...}; - static const int UNIFIED_insn_name_2[] = {id, size, ...}; - ... - - static const int *const UNIFIED_insn_name[] = {size, - UNIFIED_insn_name_1, - UNIFIED_insn_name_2, ...}; - ... - - static const int *const *const unified_insns_data[] = { - UNIFIED_insn_nameA, - UNIFIED_insn_nameB, ...}; - */ - -% unif_insns_data = @insns.find_all {|insn| !insn.is_sc}.map do |insn| -% size = insn.unifs.size -% if size > 0 -% name = "UNIFIED_#{insn.name}" -% insn.unifs.sort_by{|unif| -unif[1].size}.each_with_index do |(uni_insn, uni_insns), i| -% uni_insns = uni_insns[1..-1] -static const int <%=name%>_<%=i%>[] = { - BIN(<%=uni_insn.name%>), <%=uni_insns.size + 2%>, - <% uni_insns.map{|e| -%> -BIN(<%=e.name%>),<% -%> -% } - -}; -% end - -static const int *const <%=name%>[] = {(int *)<%=size+1%>, -% size.times do |e| - <%=name%>_<%=e%>, -% end -}; -% name -% end -% end - -static const int *const *const unified_insns_data[] = {<%#-%> -% unif_insns_data.each_with_index do |insn, i| -% if (i%8).zero? - - <% -%> -% end - <%=insn || "0"%>,<%#-%> -% end - -}; - -#undef GET_INSN_NAME - -ASSERT_VM_INSTRUCTION_SIZE(unified_insns_data); diff --git a/template/vm.inc.tmpl b/template/vm.inc.tmpl deleted file mode 100644 index 14b6ba3f10..0000000000 --- a/template/vm.inc.tmpl +++ /dev/null @@ -1,33 +0,0 @@ -/* -*-c-*- *********************************************************/ -/*******************************************************************/ -/*******************************************************************/ -/** - This file is VM main loop. - - ---- - This file is auto generated by insns2vm.rb - DO NOT TOUCH! - - If you want to fix something, you must edit 'insns.def' - */ - - -% line = _erbout.count("\n") + 1 -% @insns.each do |insn| -<% -line += 1 -make_insn_def(insn).split(/(__CURRENT_LINE__|__CURRENT_FILE__)/).each {|e| - %><%= - case e - when '__CURRENT_LINE__' - line.to_s - when '__CURRENT_FILE__' - "vm.inc" - else - line += e.count("\n") - e - end - %><% -} -%> -% end diff --git a/template/vmtc.inc.tmpl b/template/vmtc.inc.tmpl deleted file mode 100644 index 3c313113a5..0000000000 --- a/template/vmtc.inc.tmpl +++ /dev/null @@ -1,21 +0,0 @@ -/* -*-c-*- *********************************************************/ -/*******************************************************************/ -/*******************************************************************/ -/** - This file is for threaded code. - - ---- - This file is auto generated by insns2vm.rb - DO NOT TOUCH! - - If you want to fix something, you must edit 'template/vmtc.inc.tmpl' - or insns2vm.rb - */ - -static const void *const insns_address_table[] = { -% @insns.each do |insn| - LABEL_PTR(<%=insn.name%>), -% end -}; - -ASSERT_VM_INSTRUCTION_SIZE(insns_address_table); diff --git a/template/yarvarch.en b/template/yarvarch.en deleted file mode 100644 index 7a76e25b7e..0000000000 --- a/template/yarvarch.en +++ /dev/null @@ -1,7 +0,0 @@ -#title YARV: Yet another RubyVM - Software Architecture - -maybe writing. - -* YARV instruction set - -<%= d %> diff --git a/template/yarvarch.ja b/template/yarvarch.ja deleted file mode 100644 index 2739ec6b14..0000000000 --- a/template/yarvarch.ja +++ /dev/null @@ -1,454 +0,0 @@ -#title YARVアーキテクチャ -#set author 日本 Ruby の会 ささだこういち - - -- 2005-03-03(Thu) 00:31:12 +0900 いろいろと書き直し - ----- - -* これは? - -[[YARV: Yet Another RubyVM|http://www.atdot.net/yarv]] の 設計メモです。 - - -YARV は、Ruby プログラムのための次の機能を提供します。 - -- Compiler -- VM Generator -- VM (Virtual Machine) -- Assembler -- Dis-Assembler -- (experimental) JIT Compiler -- (experimental) AOT Compiler - - -現在の YARV は Ruby インタプリタの拡張ライブラリとして実装しています。こ -れにより、Ruby インタプリタの必要な機能(パーサ、オブジェクト管理、既存 -の拡張ライブラリ)などがほぼそのまま利用できます。 - -ただし、いくつかのパッチを Ruby インタプリタに当てなければなりません。 - -今後は、Ruby 本体のインタプリタ部分(eval.c)を置き換えることを目指して -開発を継続する予定です。 - - -* Compiler (compile.h, compile.c) - -コンパイラは、Ruby インタプリタのパーサによって生成された構文木(RNode -データによる木)を YARV 命令列に変換します。YARV 命令については後述しま -す。 - -とくに難しいことはしていませんが、スコープなどの開始時にローカル変数の初 -期化などを行い、あとは構文木を辿り変換していきます。 - -変換中は Ruby の Array オブジェクトに YARV 命令オブジェクト、およびオペ -ランドを格納していき、最後に実行できる形に変換します。コンパイラでは、コ -ンパイル中に生成するメモリ領域の管理が問題になることがありますが、YARV -の場合、Ruby インタプリタがすべて面倒をみてくれるのでこの部分は非常に楽 -に作ることができました(ガーベージコレクタによって自動的にメモリ管理をし -てくれるため)。 - -YARV 命令は、命令を示す識別子、オペランドなど、すべて 1 word (マシンで -表現できる自然な値。C 言語ではポインタのサイズ。Ruby インタプリタ用語で -は VALUE のサイズ)で表現されます。そのため、YARV 命令はいわゆる「バイト -コード」ではありません。そのため、YARV の説明などでは「命令列」という用 -語を使っています。 - -1 word であるため、メモリの利用効率は多少悪くなりますが、アクセス速度な -どを考慮すると、本方式が一番いいと考えております。たとえばオペランドをコ -ンスタントプールに格納し、インデックスのみをオペランドで示すことも可能で -すが、間接アクセスになってしまうので性能に影響が出るため、却下しました。 - - -* VM Generator (rb/insns2vm.rb, insns.def) - -rb/insns2vm.rb というスクリプトは、insns.def というファイルを読み込み、 -VM のために必要なファイルを生成します。具体的には、命令を実行する部分を -生成しますが、ほかにもコンパイルに必要な情報、最適化に必要な情報、やアセ -ンブラ、逆アセンブラに必要な情報を示すファイルも生成します。 - - -** 命令記述 - -insns.def には、各命令がどのような命令であるかを記述します。具体的には次 -の情報を記述します。 - -- 命令の名前 -- その命令のカテゴリ、コメント(英語、日本語) -- オペランドの名前 -- その命令実行前にスタックからポップする値 -- その命令実行後にスタックにプッシュする値 -- その命令のロジック(C 言語で記述) - -たとえば、スタックに self をおく putself という命令は次のように記述しま -す。 - -#code -/** - @c put - @e put self. - @j self を置く。 - */ -DEFINE_INSN -putself -() -() -(VALUE val) -{ - val = GET_SELF(); -} -#end - -この場合、オペランドと、スタックからポップする値は無いことになります。命 -令終了後、self をスタックトップに置きたいわけですが、それは val という、 -スタックにプッシュする値として宣言しておいた変数に代入しておくことで、こ -れを変換するとスタックトップに置く C プログラムが生成されます。 - -細かいフォーマットは insns.def の冒頭を参照してください。そんなに難しく -ないと思います。 - -insnhelper.h というファイルに、命令ロジックを記述するために必要なマクロ -が定義されています。また、VM の内部構造に関する定義は vm.h というファイ -ルにあります。 - - -* VM (Virtual Machine, vm.h, vm.c) - -VM は、実際にコンパイルした結果生成される YARV 命令列を実行します。まさ -に、この部分が YARV のキモになり、将来的には eval.c をこの VM で置き換え -たいと考えています。 - -現在の Ruby インタプリタで実行できるすべてのことが、この VM で実現できる -ように作っています(現段階ではまだ完全ではありませんが、そうなるべきです)。 - -VM は、単純なスタックマシンとして実装しています。スレッドひとつにスタッ -クひとつを保持します。スタックの領域はヒープから取得するので、柔軟な領域 -設定が可能です。 - - -** レジスタ - -VM は 5 つの仮想的なレジスタによって制御されます。 - -- PC (Program Counter) -- SP (Stack Pointer) -- CFP (Control Frame Pointer) -- LFP (Local Frame Pointer) -- DFP (Dynamic Frame Pointer) - -PC は現在実行中の命令列の位置を示します。SP はスタックトップの位置を示し -ます。CFP、LFP、DFP はそれぞれフレームの情報を示します。詳細は後述します。 - - -** スタックフレーム - -obsolete (update soon) - - -** フレームデザインについての補足 - -Lisp の処理系などをかんがえると、わざわざブロックローカルフレームとメソ -ッドローカルフレームのようなものを用意するのは奇異に見えるかもしれません。 -あるフレームを、入れ子構造にして、ローカル変数のアクセスはその入れ子を外 -側に辿れば必ずたどり着くことができるからです(つまり、lfp は必要ない)。 - -しかし、Ruby ではいくつか状況が違います。まず、メソッドローカルな情報が -あること、具体的にはブロックとself(callee からみると receiver)です。こ -の情報をそれぞれのフレームにもたせるのは無駄です。 - -また、Ruby2.0 からはブロックローカル変数はなくなります(ブロックローカル -引数は残るので、構造自体はあまり変わりません)。そのため、メソッドローカ -ル変数へのアクセスが頻発することが予想されます。 - -このとき、メソッドローカル変数へのアクセスのたびにフレーム(スコープ)の -リストをたどるのは無駄であると判断し、明示的にメソッドローカルスコープと -ブロックフレームを分離し、ブロックフレームからはメソッドローカルフレーム -が lfpレジスタによって容易にアクセスできるようにしました。 - - -** メソッド呼び出しについて - -メソッド呼び出しは、YARV 命令列で記述されたメソッドか、C で記述されたメ -ソッドかによってディスパッチ手法が変わります。 - -YARV 命令列であった場合、上述したスタックフレームを作成して命令を継続し -ます。とくに VM の関数を再帰呼び出すすることは行ないません。 - -C で記述されたメソッドだった場合、単純にその関数を呼び出します(ただし、 -バックトレースを正しく生成するためにメソッド呼び出しの情報を付加してから -行ないます)。 - -このため、VM 用スタックを別途用意したものの、プログラムによってはマシン -スタックを使い切ってしまう可能性があります(C -> Ruby -> C -> ... という -呼び出しが続いた場合)。これは、現在では避けられない仕様となっています。 - - -** 例外 - -例外は、Java の JVM と同様に例外テーブルを用意することで実現します。例外 -が発生したら、当該フレームを、例外テーブルを検査します。そこで、例外が発 -生したときの PC の値に合致するエントリがあった場合、そのエントリに従って -動作します。もしエントリが見つからなかった場合、スタックを撒き戻してまた -同様にそのスコープの例外テーブルを検査します。 - -また、break、return(ブロック中)、retry なども同様の仕組みで実現します。 - -*** 例外テーブル - -例外テーブルエントリは具体的には次の情報が格納されています。 - -- 対象とする PC の範囲 -- 対象とする例外の種類 -- もし対象となったときにジャンプする先(種類による) -- もし対象となったときに起動するブロックの iseq - - -*** rescue - -rescue 節はブロックとして実現しています。$! の値を唯一の引数として持ちま -す。 - -#code -begin -rescue A -rescue B -rescue C -end -#end - -は、次のような Ruby スクリプトに変換されます。 - -#code -{|err| - case err - when A === err - when B === err - when C === err - else - raise # yarv の命令では throw - end -} -#end - - -*** ensure - -正常系(例外が発生しなかった場合)と異常系(例外が発生したときなど)の2 -種類の命令列が生成されます。正常系では、ただの連続したコード領域としてコ -ンパイルされます。また、異常系ではブロックとして実装します。最後は必ず -throw 命令で締めることになります。 - - -*** break, return(ブロック中)、retry - -break 文、ブロック中の return 文、retry 文は throw 命令としてコンパイル -されます。どこまで戻るかは、break をフックする例外テーブルのエントリが判 -断します。 - - -** 定数の検索 - -定数という名前なのに、Ruby ではコンパイル時に決定しません。というか、い -つまでも再定義可能になっています。 - -定数アクセスのためのRuby記述は次のようになります。 - -#code -Ruby表現: -expr::ID::...::ID -#end - -これは、yarv命令セットでは次のようになります。 - -#code -(expr) -getconstant ID -... -getconstant ID -#end - - -*** 定数検索パス - -もし expr が nil だった場合、定数検索パスに従って定数を検索します。この -挙動は今後 Ruby 2.0 に向けて変更される場合があります。 - -+ クラス、モジュールの動的ネスト関係(プログラムの字面上)をルートまで辿る -+ 継承関係をルート(Object)まで辿る - -このため、クラス、モジュールの動的ネスト関係を保存しなければなりません。 -このために、thread_object には klass_nest_stack というものを用意しました。 -これは、現在のネストの情報を保存します。 - -メソッド定義時、その現在のネスト情報をメソッド定義時に(dupして)加える -ことで、そのメソッドの実行時、そのネスト情報を参照することが可能になりま -す。 - -トップレベルでは、その情報はないことになります。 - -クラス/モジュール定義文実行時は、現在の情報そのものを参照することになり -ます。これは、クラススコープ突入時、その情報をクラス定義文にコピーします -(すでにコピーされていれば、これを行いません)。 - -これにより、動的なネスト情報を統一的に扱うことができます。 - - -** 最適化手法 - -YARV では高速化を目的としているので、さまざまな最適化手法を利用していま -す。詳細は割愛しますが、以下に述べる最適化などを行なっております。 - - -*** threaded code - -GCC の C 言語拡張である値としてのラベルを利用して direct threaded code -を実現しています。 - - -*** Peephole optimization - -いくつかの簡単な最適化をしています。 - - -*** inline method cache - -命令列の中にメソッド検索結果を埋め込みます。 - - -*** inline constant cache - -命令列の中に定数検索結果を埋め込みます。 - - -*** ブロックと Proc オブジェクトの分離 - -ブロック付きメソッド呼び出しが行なわれたときにはすぐにはブロックを Proc -オブジェクトとして生成しません。これにより、必要ない Proc オブジェクトの -生成を抑えています。 - -Proc メソッドは、実際に必要になった時点で作られ、そのときに環境(スコー -プ上に確保された変数など)をヒープに保存します。 - - -*** 特化命令 - -Fixnum 同士の加算などを正直に関数呼び出しによって行なうと、コストがかか -るので、これらのプリミティブな操作を行なうためのメソッド呼び出しは専用命 -令を用意しました。 - - -*** 命令融合 - -複数の命令を 1 命令に変換します。融合命令は opt_insn_unif.def の記述によ -り自動的に生成されます。 - - -*** オペランド融合 - -複数のオペランドを含めた命令を生成します。融合命令は opt_operand.def の -記述によって自動的に生成されます。 - - -*** stack caching - -スタックトップを仮想レジスタに保持するようにします。現在は 2 個の仮想レ -ジスタを想定し、5状態のスタックキャッシングを行ないます。スタックキャッ -シングする命令は自動的に生成されます。 - - -*** JIT Compile - -機械語を切り貼りします。非常に実験的なコードものしか作っておりません。ほ -とんどのプログラムは動きません。 - - -*** AOT Compile - -YARV 命令列を C 言語に変換します。まだ十分な最適化を行なえておりませんが、 -それなりに動きます。rb/aotc.rb がコンパイラです。 - - -* Assembler (rb/yasm.rb) - -YARV 命令列のアセンブラを用意しました。使い方は rb/yasm.rb を参照してく -ださい(まだ、例示してある生成手法のすべてをサポートしているわけではあり -ません)。 - - -* Dis-Assembler (disasm.c) - -YARV 命令列を示すオブジェクト YARVCore::InstructionSequence には disasm -メソッドがあります。これは、命令列を逆アセンブルした文字列を返します。 - - -* YARV 命令セット - -<%= d %> - -* その他 - -** テスト - -test/test_* がテストケースです。一応、ミスなく動くはずです。逆にいうと、 -このテストに記述されている例ではきちんと動作するということです。 - - -** ベンチマーク - -benchmark/bm_* にベンチマークプログラムがおいてあります。 - - -** 今後の予定 - -まだまだやらなければいけないこと、未実装部分がたくさんありますんでやって -いかなければなりません。一番大きな目標は eval.c を置き換えることでしょう -か。 - - -*** Verifier - -YARV 命令列は、ミスがあっても動かしてしまうため危険である可能性がありま -す。そのため、スタックの利用状態をきちんと事前に検証するようなベリファイ -アを用意しなければならないと考えています。 - - -*** Compiled File の構想 - -Ruby プログラムをこの命令セットにシリアライズしたデータ構造をファイルに -出力できるようにしたいと考えています。これを利用して一度コンパイルした命 -令列をファイルに保存しておけば、次回ロード時にはコンパイルの手間、コスト -を省くことができます。 - - -**** 全体構成 - -次のようなファイル構成を考えていますが、まだ未定です。 - -#code -u4 : 4 byte unsigned storage -u2 : 2 byte unsigned storage -u1 : 1 byte unsigned storage - -every storages are little endian :-) - -CompiledFile{ - u4 magic; - - u2 major; - u2 minor; - - u4 character_code; - - u4 constants_pool_count; - ConstantEntry constants_pool[constants_pool_count]; - - u4 block_count; - blockEntry blocks[block_count]; - - u4 method_count; - MethodEntry methods[method_count]; -} -#end - -Java classfile のパクリ。 - diff --git a/template/yasmdata.rb.tmpl b/template/yasmdata.rb.tmpl deleted file mode 100644 index a11df0a712..0000000000 --- a/template/yasmdata.rb.tmpl +++ /dev/null @@ -1,20 +0,0 @@ -# -*-ruby-*- -# - -class VM - class InstructionSequence - class Instruction - InsnID2NO = { -<%= insn_id2no %> - } - - def self.id2insn_no id - if InsnID2NO.has_key? id - InsnID2NO[id] - end - end - end - end -end - - diff --git a/tool/insns2vm.rb b/tool/insns2vm.rb index ecbbb52643..e6fe64d189 100755 --- a/tool/insns2vm.rb +++ b/tool/insns2vm.rb @@ -3,16 +3,11 @@ # This is used by Makefile.in to generate .inc files. # See Makefile.in for details. -require 'optparse' - -Version = %w$Revision: 11626 $[1..-1] - -require "#{File.join(File.dirname(__FILE__), 'instruction')}" +require_relative 'ruby_vm/scripts/insns2vm' if $0 == __FILE__ - opts = ARGV.options - maker = RubyVM::SourceCodeGenerator.def_options(opts) - files = opts.parse! - generator = maker.call - generator.generate(files) + router(ARGV).each do |(path, generator)| + str = generator.generate path + path.write str, mode: 'wb:utf-8' + end end diff --git a/tool/instruction.rb b/tool/instruction.rb deleted file mode 100755 index 21af9a5800..0000000000 --- a/tool/instruction.rb +++ /dev/null @@ -1,1250 +0,0 @@ -#!./miniruby -# -*- coding: us-ascii -*- -# -# This library is used by insns2vm.rb as part of autogenerating -# instruction files with .inc extensions like insns.inc and vm.inc. - -require 'erb' -$:.unshift(File.dirname(__FILE__)) -require 'vpath' - -class RubyVM - class Instruction - def initialize name, opes, pops, rets, comm, body, tvars, sp_inc, - orig = self, defopes = [], type = nil, - nsc = [], psc = [[], []] - - @name = name - @opes = opes # [[type, name], ...] - @pops = pops # [[type, name], ...] - @rets = rets # [[type, name], ...] - @comm = comm # {:c => category, :e => en desc, :j => ja desc} - @body = body # '...' - - @orig = orig - @defopes = defopes - @type = type - @tvars = tvars - - @nextsc = nsc - @pushsc = psc - @sc = [] - @unifs = [] - @optimized = [] - @is_sc = false - @sp_inc = sp_inc - @trace = trace - end - - def add_sc sci - @sc << sci - sci.set_sc - end - - attr_reader :name, :opes, :pops, :rets - attr_reader :body, :comm - attr_reader :nextsc, :pushsc - attr_reader :orig, :defopes, :type - attr_reader :sc - attr_reader :unifs, :optimized - attr_reader :is_sc - attr_reader :tvars - attr_reader :sp_inc - attr_accessor :trace - - def set_sc - @is_sc = true - end - - def add_unif insns - @unifs << insns - end - - def add_optimized insn - @optimized << insn - end - - def sp_increase_c_expr - if(pops.any?{|t, v| v == '...'} || - rets.any?{|t, v| v == '...'}) - # user definition - raise "no sp increase definition" if @sp_inc.nil? - ret = "int inc = 0;\n" - - @opes.each_with_index{|(t, v), i| - if (t == 'rb_num_t' && ((re = /\b#{v}\b/n) =~ @sp_inc)) || - (@defopes.any?{|t, val| re =~ val}) - ret << " int #{v} = FIX2INT(opes[#{i}]);\n" - elsif (t == 'CALL_INFO' && ((re = /\b#{v}\b/n) =~ @sp_inc)) - ret << " CALL_INFO #{v} = (CALL_INFO)(opes[#{i}]);\n" - end - } - - @defopes.each_with_index{|((t, var), val), i| - if t == 'rb_num_t' && val != '*' && /\b#{var}\b/ =~ @sp_inc - ret << " #{t} #{var} = #{val};\n" - end - } - - ret << " #{@sp_inc};\n" - ret << " return depth + inc;" - ret - else - "return depth + #{rets.size - pops.size};" - end - end - - def inspect - "#" - end - end - - class InstructionsLoader - def initialize opts = {} - @insns = [] - @insn_map = {} - - @vpath = opts[:VPATH] || File - @use_const = opts[:use_const] - @verbose = opts[:verbose] - @destdir = opts[:destdir] - - (@vm_opts = load_vm_opts).each {|k, v| - @vm_opts[k] = opts[k] if opts.key?(k) - } - - load_insns_def opts[:"insns.def"] || 'insns.def' - - load_opt_operand_def opts[:"opope.def"] || 'defs/opt_operand.def' - load_insn_unification_def opts[:"unif.def"] || 'defs/opt_insn_unif.def' - make_stackcaching_insns if vm_opt?('STACK_CACHING') - make_trace_insns - end - - attr_reader :vpath - attr_reader :destdir - - %w[use_const verbose].each do |attr| - attr_reader attr - alias_method "#{attr}?", attr - remove_method attr - end - - def [](s) - @insn_map[s.to_s] - end - - def each - @insns.each{|insn| - yield insn - } - end - - def size - @insns.size - end - - ### - private - - def vm_opt? name - @vm_opts[name] - end - - def load_vm_opts file = nil - file ||= 'vm_opts.h' - opts = {} - vpath.open(file) do |f| - f.grep(/^\#define\s+OPT_([A-Z_]+)\s+(\d+)/) do - opts[$1] = !$2.to_i.zero? - end - end - opts - end - - SKIP_COMMENT_PATTERN = Regexp.compile(Regexp.escape('/** ##skip')) - - include Enumerable - - def add_insn insn - @insns << insn - @insn_map[insn.name] = insn - end - - def make_insn name, opes, pops, rets, comm, body, sp_inc - add_insn Instruction.new(name, opes, pops, rets, comm, body, [], sp_inc) - end - - # str -> [[type, var], ...] - def parse_vars line - raise unless /\((.*?)\)/ =~ line - vars = $1.split(',') - vars.map!{|v| - if /\s*(\S+)\s+(\S+)\s*/ =~ v - type = $1 - var = $2 - elsif /\s*\.\.\.\s*/ =~ v - type = var = '...' - else - raise - end - [type, var] - } - vars - end - - def parse_comment comm - c = 'others' - j = '' - e = '' - comm.each_line{|line| - case line - when /@c (.+)/ - c = $1 - when /@e (.+)/ - e = $1 - when /@e\s*$/ - e = '' - when /@j (.+)$/ - j = $1 - when /@j\s*$/ - j = '' - end - } - { :c => c, - :e => e, - :j => j, - } - end - - def load_insns_def file - body = insn = opes = pops = rets = nil - comment = '' - - vpath.open(file) {|f| - f.instance_variable_set(:@line_no, 0) - class << f - def line_no - @line_no - end - def gets - @line_no += 1 - super - end - end - - while line = f.gets - line.chomp! - case line - - when SKIP_COMMENT_PATTERN - while line = f.gets.chomp - if /\s+\*\/$/ =~ line - break - end - end - - # collect instruction comment - when /^\/\*\*$/ - while line = f.gets - if /\s+\*\/\s*$/ =~ line - break - else - comment << line - end - end - - # start instruction body - when /^DEFINE_INSN$/ - insn = f.gets.chomp - opes = parse_vars(f.gets.chomp) - pops = parse_vars(f.gets.chomp).reverse - rets_str = f.gets.chomp - rets = parse_vars(rets_str).reverse - comment = parse_comment(comment) - insn_in = true - body = '' - - - when /^\/\/ attr rb_snum_t sp_inc = (.+)$/ - sp_inc = 'inc +=' + $1 - - when /^\{$/ - line_no = f.line_no - - # end instruction body - when /^\}/ - if insn_in - body.instance_variable_set(:@line_no, line_no) - body.instance_variable_set(:@file, f.path) - insn = make_insn(insn, opes, pops, rets, comment, body, sp_inc) - insn_in = false - comment = '' - end - - else - if insn_in - body << line + "\n" - end - end - end - } - end - - ## opt op - def load_opt_operand_def file - vpath.foreach(file) {|line| - line = line.gsub(/\#.*/, '').strip - next if line.length == 0 - break if /__END__/ =~ line - /(\S+)\s+(.+)/ =~ line - insn = $1 - opts = $2 - add_opt_operand insn, opts.split(/,/).map{|e| e.strip} - } if file - end - - def label_escape label - label.gsub(/\(/, '_O_'). - gsub(/\)/, '_C_'). - gsub(/\*/, '_WC_') - end - - def add_opt_operand insn_name, opts - insn = @insn_map[insn_name] - opes = insn.opes - - if opes.size != opts.size - raise "operand size mismatch for #{insn.name} (opes: #{opes.size}, opts: #{opts.size})" - end - - ninsn = insn.name + '_OP_' + opts.map{|e| label_escape(e)}.join('_') - nopes = [] - defv = [] - - opts.each_with_index{|e, i| - if e == '*' - nopes << opes[i] - end - defv << [opes[i], e] - } - - make_insn_operand_optimized(insn, ninsn, nopes, defv) - end - - def make_insn_operand_optimized orig_insn, name, opes, defopes - comm = orig_insn.comm.dup - comm[:c] = 'optimize' - add_insn insn = Instruction.new( - name, opes, orig_insn.pops, orig_insn.rets, comm, - orig_insn.body, orig_insn.tvars, orig_insn.sp_inc, - orig_insn, defopes) - orig_insn.add_optimized insn - end - - ## insn unif - def load_insn_unification_def file - vpath.foreach(file) {|line| - line = line.gsub(/\#.*/, '').strip - next if line.length == 0 - break if /__END__/ =~ line - make_unified_insns line.split.map{|e| - raise "unknown insn: #{e}" unless @insn_map[e] - @insn_map[e] - } - } if file - end - - def all_combination sets - ret = sets.shift.map{|e| [e]} - - sets.each{|set| - prev = ret - ret = [] - prev.each{|ary| - set.each{|e| - eary = ary.dup - eary << e - ret << eary - } - } - } - ret - end - - def make_unified_insns insns - if vm_opt?('UNIFY_ALL_COMBINATION') - insn_sets = insns.map{|insn| - [insn] + insn.optimized - } - - all_combination(insn_sets).each{|insns_set| - make_unified_insn_each insns_set - } - else - make_unified_insn_each insns - end - end - - def mk_private_val vals, i, redef - vals.dup.map{|v| - # v[0] : type - # v[1] : var name - - v = v.dup - if v[0] != '...' - redef[v[1]] = v[0] - v[1] = "#{v[1]}_#{i}" - end - v - } - end - - def mk_private_val2 vals, i, redef - vals.dup.map{|v| - # v[0][0] : type - # v[0][1] : var name - # v[1] : default val - - pv = v.dup - v = pv[0] = pv[0].dup - if v[0] != '...' - redef[v[1]] = v[0] - v[1] = "#{v[1]}_#{i}" - end - pv - } - end - - def make_unified_insn_each insns - names = [] - opes = [] - pops = [] - rets = [] - comm = { - :c => 'optimize', - :e => 'unified insn', - :j => 'unified insn', - } - body = '' - passed = [] - tvars = [] - defopes = [] - sp_inc = '' - - insns.each_with_index{|insn, i| - names << insn.name - redef_vars = {} - - e_opes = mk_private_val(insn.opes, i, redef_vars) - e_pops = mk_private_val(insn.pops, i, redef_vars) - e_rets = mk_private_val(insn.rets, i, redef_vars) - # ToDo: fix it - e_defs = mk_private_val2(insn.defopes, i, redef_vars) - - passed_vars = [] - while pvar = e_pops.pop - rvar = rets.pop - if rvar - raise "unsupported unif insn: #{insns.inspect}" if rvar[0] == '...' - passed_vars << [pvar, rvar] - tvars << rvar - else - e_pops.push pvar - break - end - end - - opes.concat e_opes - pops.concat e_pops - rets.concat e_rets - defopes.concat e_defs - sp_inc << "#{insn.sp_inc}" - - body << "{ /* unif: #{i} */\n" + - passed_vars.map{|rpvars| - pv = rpvars[0] - rv = rpvars[1] - "#define #{pv[1]} #{rv[1]}" - }.join("\n") + - "\n" + - redef_vars.map{|v, type| - "#{type} #{v} = #{v}_#{i};" - }.join("\n") + "\n" - if line = insn.body.instance_variable_get(:@line_no) - file = insn.body.instance_variable_get(:@file) - body << "#line #{line+1} \"#{file}\"\n" - body << insn.body - body << "\n#line __CURRENT_LINE__ \"__CURRENT_FILE__\"\n" - else - body << insn.body - end - body << redef_vars.keys.map{|v| - "#{v}_#{i} = #{v};" - }.join("\n") + - "\n" + - passed_vars.map{|rpvars| - "#undef #{rpvars[0][1]}" - }.join("\n") + - "\n}\n" - } - - tvars_ary = [] - tvars.each{|tvar| - unless opes.any?{|var| - var[1] == tvar[1] - } || defopes.any?{|pvar| - pvar[0][1] == tvar[1] - } - tvars_ary << tvar - end - } - add_insn insn = Instruction.new("UNIFIED_" + names.join('_'), - opes, pops, rets.reverse, comm, body, - tvars_ary, sp_inc) - insn.defopes.replace defopes - insns[0].add_unif [insn, insns] - end - - ## sc - SPECIAL_INSN_FOR_SC_AFTER = { - /\Asend/ => [:a], - /\Aend/ => [:a], - /\Ayield/ => [:a], - /\Aclassdef/ => [:a], - /\Amoduledef/ => [:a], - } - FROM_SC = [[], [:a], [:b], [:a, :b], [:b, :a]] - - def make_stackcaching_insns - pops = rets = nil - - @insns.dup.each{|insn| - opops = insn.pops - orets = insn.rets - oopes = insn.opes - ocomm = insn.comm - oname = insn.name - - after = SPECIAL_INSN_FOR_SC_AFTER.find {|k, v| k =~ oname} - - insns = [] - FROM_SC.each{|from| - name, pops, rets, pushs1, pushs2, nextsc = - *calc_stack(insn, from, after, opops, orets) - - make_insn_sc(insn, name, oopes, pops, rets, [pushs1, pushs2], nextsc) - } - } - end - - def make_trace_insns - @insns.dup.each{|insn| - body = <<-EOS - vm_trace(ec, GET_CFP(), GET_PC()); - DISPATCH_ORIGINAL_INSN(#{insn.name}); - EOS - - trace_insn = Instruction.new(name = "trace_#{insn.name}", - insn.opes, insn.pops, insn.rets, insn.comm, - body, insn.tvars, insn.sp_inc) - trace_insn.trace = true - add_insn trace_insn - } - end - - def make_insn_sc orig_insn, name, opes, pops, rets, pushs, nextsc - comm = orig_insn.comm.dup - comm[:c] = 'optimize(sc)' - - scinsn = Instruction.new( - name, opes, pops, rets, comm, - orig_insn.body, orig_insn.tvars, orig_insn.sp_inc, - orig_insn, orig_insn.defopes, :sc, nextsc, pushs) - - add_insn scinsn - orig_insn.add_sc scinsn - end - - def self.complement_name st - "#{st[0] ? st[0] : 'x'}#{st[1] ? st[1] : 'x'}" - end - - def add_stack_value st - len = st.length - if len == 0 - st[0] = :a - [nil, :a] - elsif len == 1 - if st[0] == :a - st[1] = :b - else - st[1] = :a - end - [nil, st[1]] - else - st[0], st[1] = st[1], st[0] - [st[1], st[1]] - end - end - - def calc_stack insn, ofrom, oafter, opops, orets - from = ofrom.dup - pops = opops.dup - rets = orets.dup - rest_scr = ofrom.dup - - pushs_before = [] - pushs= [] - - pops.each_with_index{|e, i| - if e[0] == '...' - pushs_before = from - from = [] - end - r = from.pop - break unless r - pops[i] = pops[i].dup << r - } - - if oafter - from = oafter - from.each_with_index{|r, i| - rets[i] = rets[i].dup << r if rets[i] - } - else - rets = rets.reverse - rets.each_with_index{|e, i| - break if e[0] == '...' - pushed, r = add_stack_value from - rets[i] = rets[i].dup << r - if pushed - if rest_scr.pop - pushs << pushed - end - - if i - 2 >= 0 - rets[i-2].pop - end - end - } - end - - if false #|| insn.name =~ /test3/ - p ofrom - p pops - p rets - p pushs_before - p pushs - p from - exit - end - - ret = ["#{insn.name}_SC_#{InstructionsLoader.complement_name(ofrom)}_#{complement_name(from)}", - pops, rets, pushs_before, pushs, from] - end - end - - class SourceCodeGenerator - def initialize insns - @insns = insns - end - - attr_reader :insns - - def generate - raise "should not reach here" - end - - def vpath - @insns.vpath - end - - def verbose? - @insns.verbose? - end - - def use_const? - @insns.use_const? - end - - def template(name) - ERB.new(vpath.read("template/#{name}"), nil, '%-') - end - - def build_string - @lines = [] - yield - @lines.join("\n") - end - - EMPTY_STRING = ''.freeze - - def commit str = EMPTY_STRING - @lines << str - end - - def comment str - @lines << str if verbose? - end - - def output_path(fn) - d = @insns.destdir - fn = File.join(d, fn) if d - fn - end - end - - ################################################################### - # vm.inc - class VmBodyGenerator < SourceCodeGenerator - # vm.inc - def generate - template('vm.inc.tmpl').result(binding) - end - - def generate_from_insnname insnname - make_insn_def @insns[insnname.to_s] - end - - ####### - private - - def make_header_prepare_stack insn - comment " /* prepare stack status */" - - push_ba = insn.pushsc - raise "unsupport" if push_ba[0].size > 0 && push_ba[1].size > 0 - - n = 0 - push_ba.each {|pushs| n += pushs.length} - commit " CHECK_VM_STACK_OVERFLOW_FOR_INSN(VM_REG_CFP, #{n});" if n > 0 - push_ba.each{|pushs| - pushs.each{|r| - commit " PUSH(SCREG(#{r}));" - } - } - end - - def make_header_operands insn - comment " /* declare and get from iseq */" - - vars = insn.opes - n = 0 - ops = [] - - vars.each_with_index{|(type, var), i| - if type == '...' - break - end - - # skip make operands when body has no reference to this operand - # TODO: really needed? - re = /\b#{var}\b/n - if re =~ insn.body or re =~ insn.sp_inc or insn.rets.any?{|t, v| re =~ v} or re =~ 'ic' or re =~ 'ci' or re =~ 'cc' - ops << " #{type} #{var} = (#{type})GET_OPERAND(#{i+1});" - end - - n += 1 - } - @opn = n - - # reverse or not? - # ops.join - commit ops.reverse - end - - def make_header_default_operands insn - vars = insn.defopes - - vars.each{|e| - next if e[1] == '*' - if use_const? - commit " const #{e[0][0]} #{e[0][1]} = #{e[1]};" - else - commit " #define #{e[0][1]} #{e[1]}" - end - } - end - - def make_footer_default_operands insn - comment " /* declare and initialize default opes */" - if use_const? - commit - else - vars = insn.defopes - - vars.each{|e| - next if e[1] == '*' - commit "#undef #{e[0][1]}" - } - end - end - - def make_header_stack_pops insn - comment " /* declare and pop from stack */" - - n = 0 - pops = [] - vars = insn.pops - vars.each_with_index{|iter, i| - type, var, r = *iter - if type == '...' - break - end - if r - pops << " #{type} #{var} = SCREG(#{r});" - else - pops << " #{type} #{var} = TOPN(#{n});" - n += 1 - end - } - @popn = n - - # reverse or not? - commit pops.reverse - end - - def make_header_temporary_vars insn - comment " /* declare temporary vars */" - - insn.tvars.each{|var| - commit " #{var[0]} #{var[1]};" - } - end - - def make_header_stack_val insn - comment "/* declare stack push val */" - - vars = insn.opes + insn.pops + insn.defopes.map{|e| e[0]} - - insn.rets.each{|var| - if vars.all?{|e| e[1] != var[1]} && var[1] != '...' - commit " #{var[0]} #{var[1]};" - end - } - end - - def make_header_analysis insn - commit " COLLECT_USAGE_INSN(BIN(#{insn.name}));" - insn.opes.each_with_index{|op, i| - commit " COLLECT_USAGE_OPERAND(BIN(#{insn.name}), #{i}, #{op[1]});" - } - end - - def make_header_pc insn - commit " ADD_PC(1+#{@opn});" - commit " PREFETCH(GET_PC());" - end - - def make_header_popn insn - comment " /* management */" - commit " POPN(#{@popn});" if @popn > 0 - end - - def make_header_debug insn - comment " /* for debug */" - commit " DEBUG_ENTER_INSN(\"#{insn.name}\");" - end - - def make_header_defines insn - commit " #define CURRENT_INSN_#{insn.name} 1" - commit " #define INSN_IS_SC() #{insn.sc ? 0 : 1}" - commit " #define INSN_LABEL(lab) LABEL_#{insn.name}_##lab" - commit " #define LABEL_IS_SC(lab) LABEL_##lab##_###{insn.sc.size == 0 ? 't' : 'f'}" - end - - def each_footer_stack_val insn - insn.rets.reverse_each{|v| - break if v[1] == '...' - yield v - } - end - - def make_footer_stack_val insn - comment " /* push stack val */" - - n = 0 - each_footer_stack_val(insn){|v| - n += 1 unless v[2] - } - commit " CHECK_VM_STACK_OVERFLOW_FOR_INSN(VM_REG_CFP, #{n});" if n > 0 - each_footer_stack_val(insn){|v| - if v[2] - commit " SCREG(#{v[2]}) = #{v[1]};" - else - commit " PUSH(#{v[1]});" - end - } - end - - def make_footer_undefs insn - commit "#undef CURRENT_INSN_#{insn.name}" - commit "#undef INSN_IS_SC" - commit "#undef INSN_LABEL" - commit "#undef LABEL_IS_SC" - end - - def make_header insn - label = insn.trace ? '' : "START_OF_ORIGINAL_INSN(#{insn.name});" - commit "INSN_ENTRY(#{insn.name}){#{label}" - make_header_prepare_stack insn - commit "{" - unless insn.trace - make_header_stack_val insn - make_header_default_operands insn - make_header_operands insn - make_header_stack_pops insn - make_header_temporary_vars insn - # - make_header_debug insn - make_header_pc insn - make_header_popn insn - make_header_defines insn - make_header_analysis insn - end - commit "{" - end - - def make_footer insn - unless insn.trace - make_footer_stack_val insn - make_footer_default_operands insn - make_footer_undefs insn - end - commit " END_INSN(#{insn.name});}}}" - end - - def make_insn_def insn - build_string do - make_header insn - if line = insn.body.instance_variable_get(:@line_no) - file = insn.body.instance_variable_get(:@file) - commit "#line #{line+1} \"#{file}\"" - commit insn.body - commit '#line __CURRENT_LINE__ "__CURRENT_FILE__"' - else - commit insn.body - end - make_footer(insn) - end - end - end - - ################################################################### - # vmtc.inc - class VmTCIncGenerator < SourceCodeGenerator - def generate - template('vmtc.inc.tmpl').result(binding) - end - end - - ################################################################### - # insns_info.inc - class InsnsInfoIncGenerator < SourceCodeGenerator - def generate - template('insns_info.inc.tmpl').result(binding) - end - - ### - private - - def op2typesig op - case op - when /^OFFSET/ - "TS_OFFSET" - when /^rb_num_t/ - "TS_NUM" - when /^lindex_t/ - "TS_LINDEX" - when /^VALUE/ - "TS_VALUE" - when /^ID/ - "TS_ID" - when /GENTRY/ - "TS_GENTRY" - when /^IC/ - "TS_IC" - when /^CALL_INFO/ - "TS_CALLINFO" - when /^CALL_CACHE/ - "TS_CALLCACHE" - when /^\.\.\./ - "TS_VARIABLE" - when /^CDHASH/ - "TS_CDHASH" - when /^ISEQ/ - "TS_ISEQ" - when /rb_insn_func_t/ - "TS_FUNCPTR" - else - raise "unknown op type: #{op}" - end - end - - TYPE_CHARS = { - 'TS_OFFSET' => 'O', - 'TS_NUM' => 'N', - 'TS_LINDEX' => 'L', - 'TS_VALUE' => 'V', - 'TS_ID' => 'I', - 'TS_GENTRY' => 'G', - 'TS_IC' => 'K', - 'TS_CALLINFO' => 'C', - 'TS_CALLCACHE' => 'E', - 'TS_CDHASH' => 'H', - 'TS_ISEQ' => 'S', - 'TS_VARIABLE' => '.', - 'TS_FUNCPTR' => 'F', - } - - def max_length(array) - max = 0 - array.each do |i| - if (n = i.length) > max - max = n - end - end - max - end - end - - ################################################################### - # insns.inc - class InsnsIncGenerator < SourceCodeGenerator - def generate - template('insns.inc.tmpl').result(binding) - end - end - - ################################################################### - # minsns.inc - class MInsnsIncGenerator < SourceCodeGenerator - def generate - template('minsns.inc.tmpl').result(binding) - end - end - - ################################################################### - # optinsn.inc - class OptInsnIncGenerator < SourceCodeGenerator - def generate - optinsn_inc - end - - ### - private - - def val_as_type op - type = op[0][0] - val = op[1] - - case type - when /^long/, /^rb_num_t/, /^lindex_t/ - "INT2FIX(#{val})" - when /^VALUE/ - val - when /^ID/ - "INT2FIX(#{val})" - when /^ISEQ/, /^rb_insn_func_t/ - val - when /GENTRY/ - raise - when /^\.\.\./ - raise - else - raise "type: #{type}" - end - end - - # optinsn.inc - def optinsn_inc - opt_insns_map = Hash.new{[]} - - @insns.each{|insn| - next if insn.defopes.size == 0 - next if insn.type == :sc - next if /^UNIFIED/ =~ insn.name.to_s - - originsn = insn.orig - opt_insns_map[originsn] <<= insn - } - - opt_insns_map.each_value do |optinsns| - sorted = optinsns.sort_by {|opti| - opti.defopes.find_all{|e| e[1] == '*'}.size - } - optinsns.replace(sorted) - end - - template('optinsn.inc.tmpl').result(binding) - end - end - - ################################################################### - # optunifs.inc - class OptUnifsIncGenerator < SourceCodeGenerator - def generate - template('optunifs.inc.tmpl').result(binding) - end - end - - ################################################################### - # opt_sc.inc - class OptSCIncGenerator < SourceCodeGenerator - def generate - sc_insn_info = [] - @insns.each{|insn| - insns = insn.sc - if insns.size > 0 - insns = ['SC_ERROR'] + insns.map{|e| " BIN(#{e.name})"} - else - insns = Array.new(6){'SC_ERROR'} - end - sc_insn_info << " {\n#{insns.join(",\n")}}" - } - sc_insn_info = sc_insn_info.join(",\n") - - sc_insn_next = @insns.map{|insn| - " SCS_#{InstructionsLoader.complement_name(insn.nextsc).upcase}" + - (verbose? ? " /* #{insn.name} */" : '') - }.join(",\n") - template('opt_sc.inc.tmpl').result(binding) - end - end - - ################################################################### - # yasmdata.rb - class YASMDataRbGenerator < SourceCodeGenerator - def generate - insn_id2no = '' - @insns.each_with_index{|insn, i| - insn_id2no << " :#{insn.name} => #{i},\n" - } - template('yasmdata.rb').result(binding) - end - end - - ################################################################### - # yarvarch.* - class YARVDocGenerator < SourceCodeGenerator - def generate - - end - - def desc lang - d = '' - i = 0 - cat = nil - @insns.each{|insn| - seq = insn.opes.map{|t,v| v}.join(' ') - before = insn.pops.reverse.map{|t,v| v}.join(' ') - after = insn.rets.reverse.map{|t,v| v}.join(' ') - - if cat != insn.comm[:c] - d << "** #{insn.comm[:c]}\n\n" - cat = insn.comm[:c] - end - - d << "*** #{insn.name}\n" - d << "\n" - d << insn.comm[lang] << "\n\n" - d << ":instruction sequence: 0x%02x " % i << seq << "\n" - d << ":stack: #{before} => #{after}\n\n" - i+=1 - } - d - end - - def desc_ja - d = desc :j - template('yarvarch.ja').result(binding) - end - - def desc_en - d = desc :e - template('yarvarch.en').result(binding) - end - end - - class SourceCodeGenerator - Files = { # codes - 'vm.inc' => VmBodyGenerator, - 'vmtc.inc' => VmTCIncGenerator, - 'insns.inc' => InsnsIncGenerator, - 'insns_info.inc' => InsnsInfoIncGenerator, - # 'minsns.inc' => MInsnsIncGenerator, - 'optinsn.inc' => OptInsnIncGenerator, - 'optunifs.inc' => OptUnifsIncGenerator, - 'opt_sc.inc' => OptSCIncGenerator, - 'yasmdata.rb' => YASMDataRbGenerator, - } - - def generate args = [] - args = Files.keys if args.empty? - args.each{|fn| - s = Files[fn].new(@insns).generate - open(output_path(fn), 'w') {|f| f.puts(s)} - } - end - - def self.def_options(opt) - opts = { - :"insns.def" => 'insns.def', - :"opope.def" => 'defs/opt_operand.def', - :"unif.def" => 'defs/opt_insn_unif.def', - } - - opt.on("-Dname", /\AOPT_(\w+)\z/, "enable VM option") {|s, v| - opts[v] = true - } - opt.on("--enable=name[,name...]", Array, - "enable VM options (without OPT_ prefix)") {|*a| - a.each {|v| opts[v] = true} - } - opt.on("-Uname", /\AOPT_(\w+)\z/, "disable VM option") {|s, v| - opts[v] = false - } - opt.on("--disable=name[,name...]", Array, - "disable VM options (without OPT_ prefix)") {|*a| - a.each {|v| opts[v] = false} - } - opt.on("-i", "--insnsdef=FILE", "--instructions-def", - "instructions definition file") {|n| - opts[:insns_def] = n - } - opt.on("-o", "--opt-operanddef=FILE", "--opt-operand-def", - "vm option: operand definition file") {|n| - opts[:opope_def] = n - } - opt.on("-u", "--opt-insnunifdef=FILE", "--opt-insn-unif-def", - "vm option: instruction unification file") {|n| - opts[:unif_def] = n - } - opt.on("-C", "--[no-]use-const", - "use consts for default operands instead of macros") {|v| - opts[:use_const] = v - } - opt.on("-d", "--destdir", "--output-directory=DIR", - "make output file underneath DIR") {|v| - opts[:destdir] = v - } - opt.on("-V", "--[no-]verbose") {|v| - opts[:verbose] = v - } - - vpath = VPath.new - vpath.def_options(opt) - - proc { - opts[:VPATH] = vpath - build opts - } - end - - def self.build opts, vpath = ['./'] - opts[:VPATH] ||= VPath.new(*vpath) - self.new InstructionsLoader.new(opts) - end - end -end diff --git a/tool/ruby_vm/controllers/application_controller.rb b/tool/ruby_vm/controllers/application_controller.rb new file mode 100644 index 0000000000..bb86873d25 --- /dev/null +++ b/tool/ruby_vm/controllers/application_controller.rb @@ -0,0 +1,24 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +require_relative '../helpers/dumper' +require_relative '../models/instructions' +require_relative '../models/typemap' +require_relative '../loaders/vm_opts_h' + +class ApplicationController + def generate i + path = Pathname.new i + dumper = RubyVM::Dumper.new i + return [path, dumper] + end +end diff --git a/tool/ruby_vm/helpers/c_escape.rb b/tool/ruby_vm/helpers/c_escape.rb new file mode 100644 index 0000000000..088df1562e --- /dev/null +++ b/tool/ruby_vm/helpers/c_escape.rb @@ -0,0 +1,120 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +require 'securerandom' + +module RubyVM::CEscape + module_function + + # generate comment, with escaps. + def commentify str + return "/* #{str.strip.b.gsub '*/', '*\\/'} */" + end + + # Mimic gensym of CL. + def gensym prefix = 'gensym_' + return as_tr_cpp "#{prefix}#{SecureRandom.uuid}" + end + + # Mimic AS_TR_CPP() of autoconf. + def as_tr_cpp name + q = name.b + q.gsub! %r/[^a-zA-Z0-9_]/m, '_' + q.gsub! %r/_+/, '_' + return q + end + + # Section 6.10.4 of ISO/IEC 9899:1999 specifies that the file name used for + # #line directive shall be a "character string literal". So this is needed. + # + # I'm not sure how many chars are allowed here, though. The standard + # specifies 4095 chars at most, after string concatenation (section 5.2.4.1). + # But it is easy to have a path that is longer than that. + # + # Here we ignore the standard. Just create single string literal of any + # needed length. + def rstring2cstr str + # I believe this is the fastest implementation done in pure-ruby. + # Constants cached, gsub skips block evaluation, string literal optimized. + buf = str.b + buf.gsub! %r/./n, RString2CStr + return %'"#{buf}"' + end + + RString2CStr = { + "\x00"=> "\\0", "\x01"=> "\\x1", "\x02"=> "\\x2", "\x03"=> "\\x3", + "\x04"=> "\\x4", "\x05"=> "\\x5", "\x06"=> "\\x6", "\a"=> "\\a", + "\b"=> "\\b", "\t"=> "\\t", "\n"=> "\\n", "\v"=> "\\v", + "\f"=> "\\f", "\r"=> "\\r", "\x0E"=> "\\xe", "\x0F"=> "\\xf", + "\x10"=>"\\x10", "\x11"=>"\\x11", "\x12"=>"\\x12", "\x13"=>"\\x13", + "\x14"=>"\\x14", "\x15"=>"\\x15", "\x16"=>"\\x16", "\x17"=>"\\x17", + "\x18"=>"\\x18", "\x19"=>"\\x19", "\x1A"=>"\\x1a", "\e"=>"\\x1b", + "\x1C"=>"\\x1c", "\x1D"=>"\\x1d", "\x1E"=>"\\x1e", "\x1F"=>"\\x1f", + " "=> " ", "!"=> "!", "\""=> "\\\"", "#"=> "#", + "$"=> "$", "%"=> "%", "&"=> "&", "'"=> "\\'", + "("=> "(", ")"=> ")", "*"=> "*", "+"=> "+", + ","=> ",", "-"=> "-", "."=> ".", "/"=> "/", + "0"=> "0", "1"=> "1", "2"=> "2", "3"=> "3", + "4"=> "4", "5"=> "5", "6"=> "6", "7"=> "7", + "8"=> "8", "9"=> "9", ":"=> ":", ";"=> ";", + "<"=> "<", "="=> "=", ">"=> ">", "?"=> "?", + "@"=> "@", "A"=> "A", "B"=> "B", "C"=> "C", + "D"=> "D", "E"=> "E", "F"=> "F", "G"=> "G", + "H"=> "H", "I"=> "I", "J"=> "J", "K"=> "K", + "L"=> "L", "M"=> "M", "N"=> "N", "O"=> "O", + "P"=> "P", "Q"=> "Q", "R"=> "R", "S"=> "S", + "T"=> "T", "U"=> "U", "V"=> "V", "W"=> "W", + "X"=> "X", "Y"=> "Y", "Z"=> "Z", "["=> "[", + "\\"=> "\\\\", "]"=> "]", "^"=> "^", "_"=> "_", + "`"=> "`", "a"=> "a", "b"=> "b", "c"=> "c", + "d"=> "d", "e"=> "e", "f"=> "f", "g"=> "g", + "h"=> "h", "i"=> "i", "j"=> "j", "k"=> "k", + "l"=> "l", "m"=> "m", "n"=> "n", "o"=> "o", + "p"=> "p", "q"=> "q", "r"=> "r", "s"=> "s", + "t"=> "t", "u"=> "u", "v"=> "v", "w"=> "w", + "x"=> "x", "y"=> "y", "z"=> "z", "{"=> "{", + "|"=> "|", "}"=> "}", "~"=> "~", "\x7F"=>"\\x7f", + "\x80"=>"\\x80", "\x81"=>"\\x81", "\x82"=>"\\x82", "\x83"=>"\\x83", + "\x84"=>"\\x84", "\x85"=>"\\x85", "\x86"=>"\\x86", "\x87"=>"\\x87", + "\x88"=>"\\x88", "\x89"=>"\\x89", "\x8A"=>"\\x8a", "\x8B"=>"\\x8b", + "\x8C"=>"\\x8c", "\x8D"=>"\\x8d", "\x8E"=>"\\x8e", "\x8F"=>"\\x8f", + "\x90"=>"\\x90", "\x91"=>"\\x91", "\x92"=>"\\x92", "\x93"=>"\\x93", + "\x94"=>"\\x94", "\x95"=>"\\x95", "\x96"=>"\\x96", "\x97"=>"\\x97", + "\x98"=>"\\x98", "\x99"=>"\\x99", "\x9A"=>"\\x9a", "\x9B"=>"\\x9b", + "\x9C"=>"\\x9c", "\x9D"=>"\\x9d", "\x9E"=>"\\x9e", "\x9F"=>"\\x9f", + "\xA0"=>"\\xa0", "\xA1"=>"\\xa1", "\xA2"=>"\\xa2", "\xA3"=>"\\xa3", + "\xA4"=>"\\xa4", "\xA5"=>"\\xa5", "\xA6"=>"\\xa6", "\xA7"=>"\\xa7", + "\xA8"=>"\\xa8", "\xA9"=>"\\xa9", "\xAA"=>"\\xaa", "\xAB"=>"\\xab", + "\xAC"=>"\\xac", "\xAD"=>"\\xad", "\xAE"=>"\\xae", "\xAF"=>"\\xaf", + "\xB0"=>"\\xb0", "\xB1"=>"\\xb1", "\xB2"=>"\\xb2", "\xB3"=>"\\xb3", + "\xB4"=>"\\xb4", "\xB5"=>"\\xb5", "\xB6"=>"\\xb6", "\xB7"=>"\\xb7", + "\xB8"=>"\\xb8", "\xB9"=>"\\xb9", "\xBA"=>"\\xba", "\xBB"=>"\\xbb", + "\xBC"=>"\\xbc", "\xBD"=>"\\xbd", "\xBE"=>"\\xbe", "\xBF"=>"\\xbf", + "\xC0"=>"\\xc0", "\xC1"=>"\\xc1", "\xC2"=>"\\xc2", "\xC3"=>"\\xc3", + "\xC4"=>"\\xc4", "\xC5"=>"\\xc5", "\xC6"=>"\\xc6", "\xC7"=>"\\xc7", + "\xC8"=>"\\xc8", "\xC9"=>"\\xc9", "\xCA"=>"\\xca", "\xCB"=>"\\xcb", + "\xCC"=>"\\xcc", "\xCD"=>"\\xcd", "\xCE"=>"\\xce", "\xCF"=>"\\xcf", + "\xD0"=>"\\xd0", "\xD1"=>"\\xd1", "\xD2"=>"\\xd2", "\xD3"=>"\\xd3", + "\xD4"=>"\\xd4", "\xD5"=>"\\xd5", "\xD6"=>"\\xd6", "\xD7"=>"\\xd7", + "\xD8"=>"\\xd8", "\xD9"=>"\\xd9", "\xDA"=>"\\xda", "\xDB"=>"\\xdb", + "\xDC"=>"\\xdc", "\xDD"=>"\\xdd", "\xDE"=>"\\xde", "\xDF"=>"\\xdf", + "\xE0"=>"\\xe0", "\xE1"=>"\\xe1", "\xE2"=>"\\xe2", "\xE3"=>"\\xe3", + "\xE4"=>"\\xe4", "\xE5"=>"\\xe5", "\xE6"=>"\\xe6", "\xE7"=>"\\xe7", + "\xE8"=>"\\xe8", "\xE9"=>"\\xe9", "\xEA"=>"\\xea", "\xEB"=>"\\xeb", + "\xEC"=>"\\xec", "\xED"=>"\\xed", "\xEE"=>"\\xee", "\xEF"=>"\\xef", + "\xF0"=>"\\xf0", "\xF1"=>"\\xf1", "\xF2"=>"\\xf2", "\xF3"=>"\\xf3", + "\xF4"=>"\\xf4", "\xF5"=>"\\xf5", "\xF6"=>"\\xf6", "\xF7"=>"\\xf7", + "\xF8"=>"\\xf8", "\xF9"=>"\\xf9", "\xFA"=>"\\xfa", "\xFB"=>"\\xfb", + "\xFC"=>"\\xfc", "\xFD"=>"\\xfd", "\xFE"=>"\\xfe", "\xFF"=>"\\xff", + }.freeze + private_constant :RString2CStr +end diff --git a/tool/ruby_vm/helpers/dumper.rb b/tool/ruby_vm/helpers/dumper.rb new file mode 100644 index 0000000000..8f14d90d1f --- /dev/null +++ b/tool/ruby_vm/helpers/dumper.rb @@ -0,0 +1,108 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +require 'pathname' +require 'erb' +require_relative 'c_escape' + +class RubyVM::Dumper + include RubyVM::CEscape + + # I learned this handy "super-private" maneuver from @a_matsuda + # cf: https://github.com/rails/rails/pull/27363/files + using Module.new { + refine RubyVM::Dumper do + private + + def new_binding + # This `eval 'binding'` does not return the current binding + # but creates one on top of it. + return eval 'binding' + end + + def new_erb spec + path = Pathname.new __dir__ + path += '../views' + path += spec + src = path.read mode: 'rt:utf-8:utf-8' + rescue Errno::ENOENT + raise "don't know how to generate #{path}" + else + erb = ERB.new src, nil, '%-' + erb.filename = path.realpath.to_path + return erb + end + + def finderb spec + return @erb.fetch spec do |k| + erb = new_erb k + @erb[k] = erb + end + end + + def replace_pragma_line str, lineno + if str == "#pragma RubyVM reset source\n" then + return "#line #{lineno + 2} #{@file}\n" + else + return str + end + end + + public + + def do_render source, locals + erb = finderb source + bnd = @empty.dup + locals.each_pair do |k, v| + bnd.local_variable_set k, v + end + return erb.result bnd + end + + def replace_pragma str + return str \ + . each_line \ + . with_index \ + . map {|i, j| replace_pragma_line i, j } \ + . join + end + end + } + + def initialize path + @erb = {} + @empty = new_binding + dst = Pathname.new Dir.getwd + dst += path + @file = cstr dst.realdirpath.to_path + end + + def render partial, locals: {} + return do_render "_#{partial}.erb", locals + end + + def generate template + str = do_render "#{template}.erb", {} + return replace_pragma str + end + + private + + # view helpers + + alias cstr rstring2cstr + alias comm commentify + + def render_c_expr expr + render 'c_expr', locals: { expr: expr, } + end +end diff --git a/tool/ruby_vm/helpers/scanner.rb b/tool/ruby_vm/helpers/scanner.rb new file mode 100644 index 0000000000..3dce6ffdfe --- /dev/null +++ b/tool/ruby_vm/helpers/scanner.rb @@ -0,0 +1,49 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +require 'pathname' +require 'strscan' + +class RubyVM::Scanner + attr_reader :__FILE__ + attr_reader :__LINE__ + + def initialize path + src = Pathname.new __dir__ + src += path + @__LINE__ = 1 + @__FILE__ = src.realpath.to_path + str = src.read mode: 'rt:utf-8:utf-8' + @scanner = StringScanner.new str + end + + def eos? + @scanner.eos? + end + + def scan re + ret = @__LINE__ + match = @scanner.scan re + return unless match + @__LINE__ += match.count "\n" + return ret + end + + def scan! re + scan re or raise sprintf "parse error at %s:%d near:\n %s...", \ + @__FILE__, @__LINE__, @scanner.peek(32) + end + + def [] key + return @scanner[key] + end +end diff --git a/tool/ruby_vm/loaders/insns_def.rb b/tool/ruby_vm/loaders/insns_def.rb new file mode 100644 index 0000000000..58748c3ca6 --- /dev/null +++ b/tool/ruby_vm/loaders/insns_def.rb @@ -0,0 +1,92 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +require_relative '../helpers/scanner' + +json = [] +scanner = RubyVM::Scanner.new '../../../insns.def' +path = scanner.__FILE__ +grammar = %r' + (? /[*] [^*]* [*]+ (?: [^*/] [^*]* [*]+ )* / ){0} + (? typedef | extern | static | auto | register | + struct | union | enum ){0} + (? (?: \g | [^{}]+ )* ){0} + (? \{ \g* ^ \g $ \g* \} ){0} + (? \g | \s ){0} + (? [_a-zA-Z] [0-9_a-zA-Z]* ){0} + (? (?: \g \g+ )* \g ){0} + (? \g \g+ \g | \.\.\. ){0} + (? (?# empty ) | + void | + \g (?: \g* , \g* \g \g* )* ){0} + (? \g* // \s* attr \g+ + (? \g ) \g+ + (? \g ) \g* + = \g* + (? .+?; ) \g* ){0} + (? DEFINE_INSN \g+ + (? \g ) \g* + [(] \g* (? \g ) \g* [)] \g* + [(] \g* (? \g ) \g* [)] \g* + [(] \g* (? \g ) \g* [)] \g* ){0} +'x + +until scanner.eos? do + next if scanner.scan(/#{grammar}\g+/o) + split = -> (v) { + case v when /\Avoid\z/ then + [] + else + v.split(/, */) + end + } + + l1 = scanner.scan!(/#{grammar}\g/o) + name = scanner["insn:name"] + ope = split.(scanner["insn:opes"]) + pop = split.(scanner["insn:pops"]) + ret = split.(scanner["insn:rets"]) + + attrs = [] + while l2 = scanner.scan(/#{grammar}\g/o) do + attrs << { + location: [path, l2], + name: scanner["pragma:name"], + type: scanner["pragma:type"], + expr: scanner["pragma:expr"], + } + end + + l3 = scanner.scan!(/#{grammar}\g/o) + json << { + name: name, + location: [path, l1], + signature: { + name: name, + ope: ope, + pop: pop, + ret: ret, + }, + attributes: attrs, + expr: { + location: [path, l3], + expr: scanner["block"], + }, + } +end + +RubyVM::InsnsDef = json + +if __FILE__ == $0 then + require 'json' + JSON.dump RubyVM::InsnsDef, STDOUT +end diff --git a/tool/ruby_vm/loaders/opt_insn_unif_def.rb b/tool/ruby_vm/loaders/opt_insn_unif_def.rb new file mode 100644 index 0000000000..a5af409e71 --- /dev/null +++ b/tool/ruby_vm/loaders/opt_insn_unif_def.rb @@ -0,0 +1,34 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +require_relative '../helpers/scanner' + +json = [] +scanner = RubyVM::Scanner.new '../../../defs/opt_insn_unif.def' +path = scanner.__FILE__ +until scanner.eos? do + next if scanner.scan(/ ^ (?: \#.* )? \n /x) + break if scanner.scan(/ ^ __END__ $ /x) + + pos = scanner.scan!(/(? (?: [\ \t]* \w+ )+ ) \n /mx) + json << { + location: [path, pos], + signature: scanner["series"].strip.split + } +end + +RubyVM::OptInsnUnifDef = json + +if __FILE__ == $0 then + require 'json' + JSON.dump RubyVM::OptInsnUnifDef, STDOUT +end diff --git a/tool/ruby_vm/loaders/opt_operand_def.rb b/tool/ruby_vm/loaders/opt_operand_def.rb new file mode 100644 index 0000000000..5c94b4bced --- /dev/null +++ b/tool/ruby_vm/loaders/opt_operand_def.rb @@ -0,0 +1,57 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +require_relative '../helpers/scanner' + +json = [] +scanner = RubyVM::Scanner.new '../../../defs/opt_operand.def' +path = scanner.__FILE__ +grammar = %r/ + (? \# .+? \n ){0} + (? \g | \s ){0} + (? \w+ ){0} + (? \( (?: \g | [^()]+)* \) ){0} + (? (?: \g | [^(),\ \n] )+ ){0} + (? \g ){0} + (? \g ){0} + (? , \g* \g ){0} + (? \g \g* ){0} + (? \g \g+ \g \n ){0} +/mx + +until scanner.eos? do + break if scanner.scan(/ ^ __END__ $ /x) + next if scanner.scan(/#{grammar} \g+ /ox) + + line = scanner.scan!(/#{grammar} \g /mox) + insn = scanner["insn"] + args = scanner["args"] + ary = [] + until args.strip.empty? do + tmp = StringScanner.new args + tmp.scan(/#{grammar} \g /mox) + ary << tmp["arg"] + args = tmp["remain"] + break unless args + end + json << { + location: [path, line], + signature: [insn, ary] + } +end + +RubyVM::OptOperandDef = json + +if __FILE__ == $0 then + require 'json' + JSON.dump RubyVM::OptOperandDef, STDOUT +end diff --git a/tool/ruby_vm/loaders/vm_opts_h.rb b/tool/ruby_vm/loaders/vm_opts_h.rb new file mode 100644 index 0000000000..f898fb36a4 --- /dev/null +++ b/tool/ruby_vm/loaders/vm_opts_h.rb @@ -0,0 +1,37 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +require_relative '../helpers/scanner' + +json = {} +scanner = RubyVM::Scanner.new '../../../vm_opts.h' +grammar = %r/ + (? \u0020 ){0} + (? \w+ ){0} + (? 0|1 ){0} + (? \#define \g+ OPT_\g \g+ \g \g*\n ) +/mx + +until scanner.eos? do + if scanner.scan grammar then + json[scanner['key']] = ! scanner['value'].to_i.zero? # not nonzero? + else + scanner.scan(/.*\n/) + end +end + +RubyVM::VmOptsH = json + +if __FILE__ == $0 then + require 'json' + JSON.dump RubyVM::VmOptsH, STDOUT +end diff --git a/tool/ruby_vm/models/attribute.rb b/tool/ruby_vm/models/attribute.rb new file mode 100644 index 0000000000..0b6d6e09b2 --- /dev/null +++ b/tool/ruby_vm/models/attribute.rb @@ -0,0 +1,44 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +require_relative 'c_expr' + +class RubyVM::Attribute + include RubyVM::CEscape + attr_reader :insn, :key, :type, :expr + + def initialize insn:, name:, type:, location:, expr: + @insn = insn + @key = name + @expr = RubyVM::CExpr.new location: location, expr: expr + @type = type + end + + def name + as_tr_cpp "attr #{@key} @ #{@insn.name}" + end + + def pretty_name + "attr #{type} #{key} @ #{insn.pretty_name}" + end + + def declaration + argv = @insn.opes.map {|o| o[:decl] }.join(', ') + sprintf '%s %s(%s)', @type, name, argv + end + + def definition + argv = @insn.opes.map {|o| "MAYBE_UNUSED(#{o[:decl]})" }.join(",\n ") + argv = "\n #{argv}\n" if @insn.opes.size > 1 + sprintf "%s\n%s(%s)", @type, name, argv + end +end diff --git a/tool/ruby_vm/models/bare_instructions.rb b/tool/ruby_vm/models/bare_instructions.rb new file mode 100644 index 0000000000..c3a96b8f08 --- /dev/null +++ b/tool/ruby_vm/models/bare_instructions.rb @@ -0,0 +1,162 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +require_relative '../loaders/insns_def' +require_relative 'c_expr' +require_relative 'typemap' +require_relative 'attribute' + +class RubyVM::BareInstructions + attr_reader :template, :name, :opes, :pops, :rets, :decls, :expr + + def initialize template:, name:, location:, signature:, attributes:, expr: + @template = template + @name = name + @loc = location + @sig = signature + @expr = RubyVM::CExpr.new expr + @opes = typesplit @sig[:ope] + @pops = typesplit @sig[:pop].reject {|i| i == '...' } + @rets = typesplit @sig[:ret].reject {|i| i == '...' } + @attrs = attributes.map {|i| + RubyVM::Attribute.new insn: self, **i + }.each_with_object({}) {|a, h| + h[a.key] = a + } + @attrs_orig = @attrs.dup + end + + def pretty_name + n = @sig[:name] + o = @sig[:ope].map{|i| i[/\S+$/] }.join ', ' + p = @sig[:pop].map{|i| i[/\S+$/] }.join ', ' + r = @sig[:ret].map{|i| i[/\S+$/] }.join ', ' + return sprintf "%s(%s)(%s)(%s)", n, o, p, r + end + + def bin + return "BIN(#{name})" + end + + def call_attribute name + return sprintf 'CALL_ATTRIBUTE(%s)', [ + name, @name, @opes.map {|i| i[:name] } + ].flatten.compact.join(', ') + end + + def sp_inc + return @attrs.fetch "sp_inc" do |k| + return generate_attribute k, 'rb_snum_t', rets.size - pops.size + end + end + + def has_attribute? k + @attrs_orig.has_key? k + end + + def attributes + # need to generate predefined attribute defaults + sp_inc + # other_attribute + # ... + return @attrs.values + end + + def width + return 1 + opes.size + end + + def declarations + return @variables \ + . values \ + . group_by {|h| h[:type] } \ + . map {|t, v| [t, v.map {|i| i[:name] }.sort ] } \ + . map {|t, v| sprintf("%s %s", t, v.join(', ')) } \ + . sort + end + + def preamble + # preamble makes sense for operand unifications + return [] + end + + def sc? + # sc stands for stack caching. + return false + end + + def cast_to_VALUE var, expr = var[:name] + RubyVM::Typemap.typecast_to_VALUE var[:type], expr + end + + def cast_from_VALUE var, expr = var[:name] + RubyVM::Typemap.typecast_from_VALUE var[:type], expr + end + + def operands_info + opes.map {|o| + c, _ = RubyVM::Typemap.fetch o[:type] + next c + }.join + end + + def pushs_frame? + opes.any? {|o| /CALL_INFO/ =~ o[:type] } + end + + def inspect + sprintf "#<%s@%s:%d>", @name, @loc[0], @loc[1] + end + + private + + def generate_attribute k, t, v + attr = RubyVM::Attribute.new \ + insn: self, \ + name: k, \ + type: t, \ + location: [], \ + expr: v.to_s + ';' + return @attrs[k] = attr + end + + def typesplit a + @variables ||= {} + a.map do |decl| + md = %r' + (? /[*] [^*]* [*]+ (?: [^*/] [^*]* [*]+ )* / ){0} + (? \g | \s ){0} + (? [_a-zA-Z] [0-9_a-zA-Z]* ){0} + (? (?: \g \g+ )* \g ){0} + (? \g ){0} + \G \g* \g \g+ \g + 'x.match(decl) + @variables[md['var']] ||= { + decl: decl, + type: md['type'], + name: md['var'], + } + end + end + + @instances = RubyVM::InsnsDef.map {|h| new template: h, **h } + + def self.fetch name + @instances.find do |insn| + insn.name == name + end or raise IndexError, "instruction not found: #{name}" + end + + def self.to_a + @instances + end +end diff --git a/tool/ruby_vm/models/c_expr.rb b/tool/ruby_vm/models/c_expr.rb new file mode 100644 index 0000000000..b19dd8bb48 --- /dev/null +++ b/tool/ruby_vm/models/c_expr.rb @@ -0,0 +1,41 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +require_relative '../helpers/c_escape.rb' + +class RubyVM::CExpr + include RubyVM::CEscape + + attr_reader :__FILE__, :__LINE__, :expr + + def initialize location:, expr: + @__FILE__ = location[0] + @__LINE__ = location[1] + @expr = expr + end + + # blank, in sense of C program. + RE = %r'\A{\g*}\z|\A(?\s|/[*][^*]*[*]+([^*/][^*]*[*]+)*/)*\z' + if RUBY_VERSION > '2.4' then + def blank? + RE.match? @expr + end + else + def blank? + RE =~ @expr + end + end + + def inspect + sprintf "#<%s:%d %s>", @__FILE__, @__LINE__, @expr + end +end diff --git a/tool/ruby_vm/models/instructions.rb b/tool/ruby_vm/models/instructions.rb new file mode 100644 index 0000000000..5a04190a19 --- /dev/null +++ b/tool/ruby_vm/models/instructions.rb @@ -0,0 +1,22 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +require_relative 'bare_instructions' +require_relative 'operands_unifications' +require_relative 'instructions_unifications' + +RubyVM::Instructions = RubyVM::BareInstructions.to_a + \ + RubyVM::OperandsUnifications.to_a + \ + RubyVM::InstructionsUnifications.to_a + +require_relative 'trace_instructions' +RubyVM::Instructions.freeze diff --git a/tool/ruby_vm/models/instructions_unifications.rb b/tool/ruby_vm/models/instructions_unifications.rb new file mode 100644 index 0000000000..346cebd709 --- /dev/null +++ b/tool/ruby_vm/models/instructions_unifications.rb @@ -0,0 +1,43 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +require_relative '../helpers/c_escape' +require_relative '../loaders/opt_insn_unif_def' +require_relative 'bare_instructions' + +class RubyVM::InstructionsUnifications + include RubyVM::CEscape + + attr_reader :name + + def initialize location:, signature: + @location = location + @name = namegen signature + @series = signature.map do |i| + RubyVM::BareInstructions.fetch i # Misshit is fatal + end + end + + private + + def namegen signature + as_tr_cpp ['UNIFIED', *signature].join('_') + end + + @instances = RubyVM::OptInsnUnifDef.map do |h| + new(**h) + end + + def self.to_a + @instances + end +end diff --git a/tool/ruby_vm/models/operands_unifications.rb b/tool/ruby_vm/models/operands_unifications.rb new file mode 100644 index 0000000000..c0ae0ece45 --- /dev/null +++ b/tool/ruby_vm/models/operands_unifications.rb @@ -0,0 +1,137 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +require_relative '../helpers/c_escape' +require_relative '../loaders/opt_operand_def' +require_relative 'bare_instructions' + +class RubyVM::OperandsUnifications < RubyVM::BareInstructions + include RubyVM::CEscape + + attr_reader :preamble, :original, :spec + + def initialize location:, signature: + name = signature[0] + @original = RubyVM::BareInstructions.fetch name + template = @original.template + parts = compose location, signature, template[:signature] + json = template.dup + json[:location] = location + json[:signature] = parts[:signature] + json[:name] = parts[:name] + @preamble = parts[:preamble] + @spec = parts[:spec] + super template: template, **json + parts[:vars].each do |v| + @variables[v[:name]] ||= v + end + end + + def operand_shift_of var + before = @original.opes.find_index var + after = @opes.find_index var + raise "no #{var} for #{@name}" unless before and after + return before - after + end + + def condition ptr + # :FIXME: I'm not sure if this method should be in model? + exprs = @spec.each_with_index.map do |(var, val), i| + case val when '*' then + next nil + else + type = @original.opes[i][:type] + expr = RubyVM::Typemap.typecast_to_VALUE type, val + next "#{ptr}[#{i}] == #{expr}" + end + end + exprs.compact! + if exprs.size == 1 then + return exprs[0] + else + exprs.map! {|i| "(#{i})" } + return exprs.join ' && ' + end + end + + private + + def namegen signature + insn, argv = *signature + wcary = argv.map do |i| + case i when '*' then + 'WC' + else + i + end + end + as_tr_cpp [insn, *wcary].join(', ') + end + + def compose location, spec, template + name = namegen spec + *, argv = *spec + opes = @original.opes + if opes.size != argv.size + raise sprintf("operand size mismatch for %s (%s's: %d, given: %d)", + name, template[:name], opes.size, argv.size) + else + src = [] + mod = [] + spec = [] + vars = [] + argv.each_index do |i| + j = argv[i] + k = opes[i] + spec[i] = [k, j] + case j when '*' then + # operand is from iseq + mod << k[:decl] + else + # operand is inside C + vars << k + src << { + location: location, + expr: " #{k[:name]} = #{j};" + } + end + end + src.map! {|i| RubyVM::CExpr.new i } + return { + name: name, + signature: { + name: name, + ope: mod, + pop: template[:pop], + ret: template[:ret], + }, + preamble: src, + vars: vars, + spec: spec + } + end + end + + @instances = RubyVM::OptOperandDef.map do |h| + new(**h) + end + + def self.to_a + @instances + end + + def self.each_group + to_a.group_by(&:original).each_pair do |k, v| + yield k, v + end + end +end diff --git a/tool/ruby_vm/models/trace_instructions.rb b/tool/ruby_vm/models/trace_instructions.rb new file mode 100644 index 0000000000..a99a933ac7 --- /dev/null +++ b/tool/ruby_vm/models/trace_instructions.rb @@ -0,0 +1,71 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +require_relative '../helpers/c_escape' +require_relative 'bare_instructions' + +class RubyVM::TraceInstructions + include RubyVM::CEscape + + attr_reader :name + + def initialize orig: + @orig = orig + @name = as_tr_cpp "trace @ #{@orig.name}" + end + + def pretty_name + return sprintf "%s(...)(...)(...)", @name + end + + def jump_destination + return @orig.name + end + + def bin + return sprintf "BIN(%s)", @name + end + + def width + return @orig.width + end + + def operands_info + return @orig.operands_info + end + + def rets + return ['...'] + end + + def pops + return ['...'] + end + + def attributes + return [] + end + + def has_attribute? *; + return false + end + + private + + @instances = RubyVM::Instructions.map {|i| new orig: i } + + def self.to_a + @instances + end + + RubyVM::Instructions.push(*to_a) +end diff --git a/tool/ruby_vm/models/typemap.rb b/tool/ruby_vm/models/typemap.rb new file mode 100644 index 0000000000..d818ce9878 --- /dev/null +++ b/tool/ruby_vm/models/typemap.rb @@ -0,0 +1,61 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +RubyVM::Typemap = { + "..." => %w[. TS_VARIABLE], + "CALL_CACHE" => %w[E TS_CALLCACHE], + "CALL_INFO" => %w[C TS_CALLINFO], + "CDHASH" => %w[H TS_CDHASH], + "GENTRY" => %w[G TS_GENTRY], + "IC" => %w[K TS_IC], + "ID" => %w[I TS_ID], + "ISEQ" => %w[S TS_ISEQ], + "OFFSET" => %w[O TS_OFFSET], + "VALUE" => %w[V TS_VALUE], + "lindex_t" => %w[L TS_LINDEX], + "rb_insn_func_t" => %w[F TS_FUNCPTR], + "rb_num_t" => %w[N TS_NUM], +} + +# :FIXME: should this method be here? +class << RubyVM::Typemap + def typecast_from_VALUE type, val + # see also iseq_set_sequence() + case type + when '...' + raise "cast not possible: #{val}" + when 'VALUE' then + return val + when 'rb_num_t', 'lindex_t' then + return "NUM2LONG(#{val})" + when 'ID' then + return "SYM2ID(#{val})" + else + return "(#{type})(#{val})" + end + end + + def typecast_to_VALUE type, val + case type + when 'VALUE' then + return val + when 'ISEQ', 'rb_insn_func_t' then + return "(VALUE)(#{val})" + when 'rb_num_t', 'lindex_t' + "LONG2NUM(#{val})" + when 'ID' then + return "ID2SYM(#{val})" + else + raise ":FIXME: TBW for #{type}" + end + end +end diff --git a/tool/ruby_vm/scripts/insns2vm.rb b/tool/ruby_vm/scripts/insns2vm.rb new file mode 100644 index 0000000000..704a54acec --- /dev/null +++ b/tool/ruby_vm/scripts/insns2vm.rb @@ -0,0 +1,88 @@ +#! /your/favourite/path/to/ruby +# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*- +# -*- frozen_string_literal: true; -*- +# -*- warn_indent: true; -*- +# +# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +# +# This file is a part of the programming language Ruby. Permission is hereby +# granted, to either redistribute and/or modify this file, provided that the +# conditions mentioned in the file COPYING are met. Consult the file for +# details. + +require 'optparse' +require_relative '../controllers/application_controller.rb' + +def router argv + targets = generate_parser.parse argv + return targets.map do |i| + next ApplicationController.new.generate i + end +end + +def generate_parser + OptionParser.new do |this| + this.on "-I", "--srcdir=DIR", <<~'end' + Historically this option has been passed to the script. This is + supposedly because at the beginning the script was placed + outside of the ruby source tree. Decades passed since the merge + of YARV, now I can safely assume this feature is obsolescent. + Just ignore the passed value here. + end + + this.on "-L", "--vpath=SPEC", <<~'end' + Likewise, this option is no longer supported. + end + + this.on "--path-separator=SEP", /\A(?:\W\z|\.(\W).+)/, <<~'end' + Old script says this option is a "separator for vpath". I am + confident we no longer need this option. + end + + this.on "-Dname", "--enable=name[,name...]", Array, <<~'end' + This option used to override VM option that is defined in + vm_opts.h. Now it is officially unsupported because vm_opts.h to + remain mismatched with this option must break things. Just edit + vm_opts.h directly. + end + + this.on "-Uname", "--disable=name[,name...]", Array, <<~'end' + This option used to override VM option that is defined in + vm_opts.h. Now it is officially unsupported because vm_opts.h to + remain mismatched with this option must break things. Just edit + vm_opts.h directly. + end + + this.on "-i", "--insnsdef=FILE", "--instructions-def", <<~'end' + This option used to specify alternative path to insns.def. For + the same reason to ignore -I, we no longer support this. + end + + this.on "-o", "--opt-operanddef=FILE", "--opt-operand-def", <<~'end' + This option used to specify alternative path to opt_operand.def. + For the same reason to ignore -I, we no longer support this. + end + + this.on "-u", "--opt-insnunifdef=FILE", "--opt-insn-unif-def", <<~'end' + This option used to specify alternative path to + opt_insn_unif.def. For the same reason to ignore -I, we no + longer support this. + end + + this.on "-C", "--[no-]use-const", <<~'end' + We use const whenever possible now so this option is ignored. + The author believes that C compilers can constant-fold. + end + + this.on "-d", "--destdir", "--output-directory=DIR", <<~'begin' do |dir| + THIS IS THE ONLY OPTION THAT WORKS today. Change destination + directory from the current working directory to the given path. + begin + Dir.chdir dir + end + + this.on "-V", "--[no-]verbose", <<~'end' + Please let us ignore this and be modest. + end + end +end diff --git a/tool/ruby_vm/views/_attributes.erb b/tool/ruby_vm/views/_attributes.erb new file mode 100644 index 0000000000..258bf02906 --- /dev/null +++ b/tool/ruby_vm/views/_attributes.erb @@ -0,0 +1,34 @@ +%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%# +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# +typedef long OFFSET; +typedef unsigned long lindex_t; +typedef VALUE GENTRY; +typedef rb_iseq_t *ISEQ; + +#define CALL_ATTRIBUTE(name, insn, ...) attr_ ## name ## _ ## insn(__VA_ARGS__) + +% attrs = RubyVM::Instructions.map(&:attributes).flatten +% +% attrs.each do |a| +PUREFUNC(MAYBE_UNUSED(static <%= a.declaration %>)); +% end +% +% attrs.each do |a| + +/* <%= a.pretty_name %> */ +<%= a.definition %> +{ +% str = render_c_expr a.expr +% case str when /\A#/ then + return +<%= str -%> +% else + return <%= str -%> +% end +} +% end diff --git a/tool/ruby_vm/views/_c_expr.erb b/tool/ruby_vm/views/_c_expr.erb new file mode 100644 index 0000000000..cebe4d7a5d --- /dev/null +++ b/tool/ruby_vm/views/_c_expr.erb @@ -0,0 +1,17 @@ +%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%# +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +%; +% if expr.blank? +% # empty +% elsif ! expr.__LINE__ +<%= expr.expr %> +% else +#line <%= expr.__LINE__ %> <%=cstr expr.__FILE__ %> +<%= expr.expr %> +#pragma RubyVM reset source +% end diff --git a/tool/ruby_vm/views/_copyright.erb b/tool/ruby_vm/views/_copyright.erb new file mode 100644 index 0000000000..a449b0b735 --- /dev/null +++ b/tool/ruby_vm/views/_copyright.erb @@ -0,0 +1,31 @@ +%# -*- mode: c; style: ruby; coding: utf-8; indent-tabs-mode: nil -*- +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%; +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +%; +%; +%# Below is the licensing term for the generated output, not this erb file. +/* This is an auto-generated file and is a part of the programming language + * Ruby. The person who created a program to generate this file (``I'' + * hereafter) would like to refrain from defining licensing of this generated + * source code. + * + * This file consist of many small parts of codes copyrighted by each authors, + * not only the ``I'' person. Those original authors agree with some + * open-source license. I believe that the license we agree is the condition + * mentioned in the file COPYING. It states "4. You may modify and include + * the part of the software into any other software ...". But the problem is, + * the license never makes it clear if such modified parts still remain in the + * same license, or not. The fact that we agree with the source code's + * licensing terms do not automatically define that of generated ones. This is + * the reason why this file is under unclear situation. All that I know is + * that above provision guarantees this file to exist. + * + * Please let me hesitate to declare something about this nuanced contract. I + * am not in the position to take over other authors' license to merge into my + * one. Changing them to (say) GPLv3 is not doable by myself. Perhaps someday + * it might turn out to be okay to say this file is under a license. I wish the + * situation would become more clear in the future. */ diff --git a/tool/ruby_vm/views/_insn_entry.erb b/tool/ruby_vm/views/_insn_entry.erb new file mode 100644 index 0000000000..cebca8b8d0 --- /dev/null +++ b/tool/ruby_vm/views/_insn_entry.erb @@ -0,0 +1,50 @@ +%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%# +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +%; + +/* insn <%= insn.pretty_name %> */ +#define NAME_OF_CURRENT_INSN <%= insn.name %> +INSN_ENTRY(<%= insn.name %>) +{ +% unless insn.declarations.empty? + <%= insn.declarations.join(";\n ") %>; + +% end + START_OF_ORIGINAL_INSN(<%= insn.name %>); +% insn.preamble.each do |konst| +<%= render_c_expr konst -%> +% end +% +% insn.opes.each_with_index do |ope, i| + <%= ope[:name] %> = (<%= ope[:type] %>)GET_OPERAND(<%= i + 1 %>); +% end +% +% insn.pops.reverse_each.with_index.reverse_each do |pop, i| + <%= pop[:name] %> = <%= insn.cast_from_VALUE pop, "TOPN(#{i})"%>; +% end + DEBUG_ENTER_INSN(<%=cstr insn.name %>); + ADD_PC(<%= insn.width %>); + PREFETCH(GET_PC()); +% unless insn.pops.empty? + POPN(<%= insn.pops.size %>); +% end + COLLECT_USAGE_INSN(<%= insn.bin %>); +% insn.opes.each_with_index do |ope, i| + COLLECT_USAGE_OPERAND(<%= insn.bin %>, <%= i %>, <%= ope[:name] %>); +% end +<%= render_c_expr insn.expr -%> +% unless insn.rets.empty? + CHECK_VM_STACK_OVERFLOW_FOR_INSN(VM_REG_CFP, <%= insn.rets.size %>); +% insn.rets.each_with_index do |ret, i| + PUSH(<%= insn.cast_to_VALUE ret %>); +% end +% end +% + END_INSN(<%= insn.name %>); +} +#undef NAME_OF_CURRENT_INSN diff --git a/tool/ruby_vm/views/_insn_len_info.erb b/tool/ruby_vm/views/_insn_len_info.erb new file mode 100644 index 0000000000..abbdb2e92c --- /dev/null +++ b/tool/ruby_vm/views/_insn_len_info.erb @@ -0,0 +1,23 @@ +%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%# +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +CONSTFUNC(MAYBE_UNUSED(static int insn_len(VALUE insn))); +extern const char rb_vm_insn_len_info[]; + +#ifdef RUBY_VM_INSNS_INFO +const char rb_vm_insn_len_info[] = { +% RubyVM::Instructions.each_slice 25 do |a| + <%= a.map(&:width).join(', ') -%>, +% end +}; +#endif + +int +insn_len(VALUE i) +{ + return rb_vm_insn_len_info[i]; +} diff --git a/tool/ruby_vm/views/_insn_name_info.erb b/tool/ruby_vm/views/_insn_name_info.erb new file mode 100644 index 0000000000..79a48cd9cb --- /dev/null +++ b/tool/ruby_vm/views/_insn_name_info.erb @@ -0,0 +1,47 @@ +%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%# +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +% +% a = RubyVM::Instructions.map {|i| i.name } +% b = (0...a.size) +% c = a.inject([0]) {|r, i| r << (r[-1] + i.length + 1) } +% c.pop +% +CONSTFUNC(MAYBE_UNUSED(static const char *insn_name(VALUE insn))); +extern const char *rb_vm_insn_name_info; +extern const unsigned short rb_vm_insn_name_offset[]; + +#ifdef RUBY_VM_INSNS_INFO +const unsigned short rb_vm_insn_name_offset[] = { +% c.each_slice 12 do |d| + <%= d.map {|i| sprintf("%4d", i) }.join(', ') %>, +% end +}; +ASSERT_VM_INSTRUCTION_SIZE(rb_vm_insn_name_offset); + +PACKED_STRUCT(struct rb_vm_insn_name_info_tag { +% b.each_slice 3 do |d| + <%= d.map {|i| + sprintf("const char L%03d[%2d]", i, a[i].length + 1) + }.join('; ') %>; +% end +}); + +static const struct rb_vm_insn_name_info_tag rb_vm_insn_name_base = { +% a.each_slice 2 do |d| + <%= d.map {|i| sprintf("%-30s", cstr(i)) }.join(', ') %>, +% end +}; + +const char *rb_vm_insn_name_info = (const char *)&rb_vm_insn_name_base; +#endif + +const char * +insn_name(VALUE i) +{ + return &rb_vm_insn_name_info[rb_vm_insn_name_offset[i]]; +} diff --git a/tool/ruby_vm/views/_insn_operand_info.erb b/tool/ruby_vm/views/_insn_operand_info.erb new file mode 100644 index 0000000000..f6b6565d59 --- /dev/null +++ b/tool/ruby_vm/views/_insn_operand_info.erb @@ -0,0 +1,59 @@ +%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%# +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +% +% a = RubyVM::Instructions.map {|i| i.operands_info } +% b = (0...a.size) +% c = a.inject([0]) {|r, i| r << (r[-1] + i.length + 1) } +% c.pop +% +CONSTFUNC(MAYBE_UNUSED(static const char *insn_op_types(VALUE insn))); +CONSTFUNC(MAYBE_UNUSED(static int insn_op_type(VALUE insn, long pos))); +extern const char *rb_vm_insn_op_info; +extern const unsigned short rb_vm_insn_op_offset[]; + +#ifdef RUBY_VM_INSNS_INFO +const unsigned short rb_vm_insn_op_offset[] = { +% c.each_slice 14 do |d| + <%= d.map {|i| sprintf("%3d", i) }.join(', ') %>, +% end +}; +ASSERT_VM_INSTRUCTION_SIZE(rb_vm_insn_op_offset); + +PACKED_STRUCT(struct rb_vm_insn_op_info_tag { +% b.each_slice 3 do |d| + <%= d.map {|i| + sprintf("const char L%03d[%2d]", i, a[i].length + 1) + }.join('; ') %>; +% end +}); + +static const struct rb_vm_insn_op_info_tag rb_vm_insn_op_base = { +% a.each_slice 8 do |d| + <%= d.map {|i| sprintf("%-6s", cstr(i)) }.join(', ') %>, +% end +}; + +const char *rb_vm_insn_op_info = (const char *)&rb_vm_insn_op_base; +#endif + +const char * +insn_op_types(VALUE i) +{ + return &rb_vm_insn_op_info[rb_vm_insn_op_offset[i]]; +} + +int +insn_op_type(VALUE i, long j) +{ + if (j >= insn_len(i)) { + return 0; + } + else { + return insn_op_types(i)[j]; + } +} diff --git a/tool/ruby_vm/views/_insn_stack_increase.erb b/tool/ruby_vm/views/_insn_stack_increase.erb new file mode 100644 index 0000000000..566e06c95e --- /dev/null +++ b/tool/ruby_vm/views/_insn_stack_increase.erb @@ -0,0 +1,53 @@ +%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%# +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +%# +PUREFUNC(MAYBE_UNUSED(static int insn_stack_increase(int depth, int insn, const VALUE *opes))); +PUREFUNC(static rb_snum_t insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *opes)); + +rb_snum_t +insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *opes) +{ + static const signed char t[] = { +% RubyVM::Instructions.each_slice 8 do |a| + <%= a.map { |i| + if i.has_attribute?('sp_inc') + '-127' + else + sprintf("%4d", i.rets.size - i.pops.size) + end + }.join(', ') -%>, +% end + }; + char c = t[insn]; + + ASSERT_VM_INSTRUCTION_SIZE(t); + if (c != -127) { + return c; + } + else switch(insn) { + default: + UNREACHABLE; +% RubyVM::Instructions.each do |i| +% next unless i.has_attribute?('sp_inc') + case <%= i.bin %>: + return CALL_ATTRIBUTE(sp_inc, <%= i.name %><%= + i.opes.map.with_index do |v, j| + k = i.cast_from_VALUE v, "opes[#{j}]" + next ", #{k}" + end.join + %>); +% end + } +} + +int +insn_stack_increase(int depth, int insn, const VALUE *opes) +{ + enum ruby_vminsn_type itype = (enum ruby_vminsn_type)insn; + return depth + (int)insn_stack_increase_dispatch(itype, opes); +} diff --git a/tool/ruby_vm/views/_insn_type_chars.erb b/tool/ruby_vm/views/_insn_type_chars.erb new file mode 100644 index 0000000000..b3eff5624f --- /dev/null +++ b/tool/ruby_vm/views/_insn_type_chars.erb @@ -0,0 +1,12 @@ +%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%# +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +% +% map = RubyVM::Typemap.each_pair.map {|k, (c, t)| sprintf "%s = '%s'", t, c } +enum ruby_insn_type_chars { + <%= map.join(",\n ") %> +}; diff --git a/tool/ruby_vm/views/_notice.erb b/tool/ruby_vm/views/_notice.erb new file mode 100644 index 0000000000..8c6cdaf533 --- /dev/null +++ b/tool/ruby_vm/views/_notice.erb @@ -0,0 +1,22 @@ +%# -*- mode: c; style: ruby; coding: utf-8; indent-tabs-mode: nil -*- +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%; +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +%; +%; +/*******************************************************************/ +/*******************************************************************/ +/*******************************************************************/ +/** + This file <%= this_file %>. + + ---- + This file is auto generated by insns2vm.rb + DO NOT TOUCH! + + If you want to fix something, you must edit <%= cstr edit %> + or tool/insns2vm.rb + */ diff --git a/tool/ruby_vm/views/_trace_instruction.erb b/tool/ruby_vm/views/_trace_instruction.erb new file mode 100644 index 0000000000..30933a6f5a --- /dev/null +++ b/tool/ruby_vm/views/_trace_instruction.erb @@ -0,0 +1,16 @@ +%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%# +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +%; + +/* insn <%= insn.pretty_name %> */ +INSN_ENTRY(<%= insn.name %>) +{ + vm_trace(ec, GET_CFP(), GET_PC()); + DISPATCH_ORIGINAL_INSN(<%= insn.jump_destination %>); + END_INSN(<%= insn.name %>); +} diff --git a/tool/ruby_vm/views/insns.inc.erb b/tool/ruby_vm/views/insns.inc.erb new file mode 100644 index 0000000000..78dddd69d1 --- /dev/null +++ b/tool/ruby_vm/views/insns.inc.erb @@ -0,0 +1,26 @@ +/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */ + +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%# +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +<%= render 'copyright' %> +<%= render 'notice', locals: { + this_file: 'contains YARV instruction list', + edit: __FILE__, +} -%> + +/* BIN : Basic Instruction Name */ +#define BIN(n) YARVINSN_##n + +enum ruby_vminsn_type { +% RubyVM::Instructions.each do |i| + <%= i.bin %>, +% end + VM_INSTRUCTION_SIZE +}; + +#define ASSERT_VM_INSTRUCTION_SIZE(array) \ + STATIC_ASSERT(numberof_##array, numberof(array) == VM_INSTRUCTION_SIZE) diff --git a/tool/ruby_vm/views/insns_info.inc.erb b/tool/ruby_vm/views/insns_info.inc.erb new file mode 100644 index 0000000000..e08a15e5ef --- /dev/null +++ b/tool/ruby_vm/views/insns_info.inc.erb @@ -0,0 +1,19 @@ +/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */ + +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%# +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +<%= render 'copyright' %> +<%= render 'notice', locals: { + this_file: 'contains instruction information for yarv instruction sequence.', + edit: __FILE__, +} %> +<%= render 'insn_type_chars' %> +<%= render 'insn_name_info' %> +<%= render 'insn_len_info' %> +<%= render 'insn_operand_info' %> +<%= render 'attributes' %> +<%= render 'insn_stack_increase' %> diff --git a/tool/ruby_vm/views/opt_sc.inc.erb b/tool/ruby_vm/views/opt_sc.inc.erb new file mode 100644 index 0000000000..fdc9ee3d08 --- /dev/null +++ b/tool/ruby_vm/views/opt_sc.inc.erb @@ -0,0 +1,40 @@ +/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */ + +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%# +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +% raise ':FIXME:TBW' if RubyVM::VmOptsH['STACK_CACHING'] +<%= render 'copyright' %> +<%= render 'notice', locals: { + this_file: 'is for threaded code', + edit: __FILE__, +} -%> + +#define SC_STATE_SIZE 6 + +#define SCS_XX 1 +#define SCS_AX 2 +#define SCS_BX 3 +#define SCS_AB 4 +#define SCS_BA 5 + +#define SC_ERROR 0xffffffff + +static const VALUE sc_insn_info[][SC_STATE_SIZE] = { +#define NO_SC { SC_ERROR, SC_ERROR, SC_ERROR, SC_ERROR, SC_ERROR, SC_ERROR } +% RubyVM::Instructions.each_slice 8 do |a| + <%= a.map{|i| 'NO_SC' }.join(', ') %>, +% end +#undef NO_SC +}; + +static const VALUE sc_insn_next[] = { +% RubyVM::Instructions.each_slice 8 do |a| + <%= a.map{|i| 'SCS_XX' }.join(', ') %>, +% end +}; + +ASSERT_VM_INSTRUCTION_SIZE(sc_insn_next); diff --git a/tool/ruby_vm/views/optinsn.inc.erb b/tool/ruby_vm/views/optinsn.inc.erb new file mode 100644 index 0000000000..6e30078cae --- /dev/null +++ b/tool/ruby_vm/views/optinsn.inc.erb @@ -0,0 +1,71 @@ +/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */ + +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%# +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +<%= render 'copyright' -%> +<%= render 'notice', locals: { + this_file: 'is for threaded code', + edit: __FILE__, +} -%> + +static INSN * +insn_operands_unification(INSN *iobj) +{ +#ifdef OPT_OPERANDS_UNIFICATION + VALUE *op = iobj->operands; + + switch (iobj->insn_id) { + default: + /* do nothing */; + break; + +% RubyVM::OperandsUnifications.each_group do |orig, unifs| + case <%= orig.bin %>: +% unifs.each do |insn| + + /* <%= insn.pretty_name %> */ + if ( <%= insn.condition('op') %> ) { +% insn.opes.each_with_index do |o, x| +% n = insn.operand_shift_of(o) +% if n != 0 then + op[<%= x %>] = op[<%= x + n %>]; +% end +% end + iobj->insn_id = <%= insn.bin %>; + iobj->operand_size = <%= insn.opes.size %>; + break; + } +% end + + break; +% end + } +#endif + return iobj; +} + +int +rb_insn_unified_local_var_level(VALUE insn) +{ +#ifdef OPT_OPERANDS_UNIFICATION + /* optimize rule */ + switch (insn) { + default: + return -1; /* do nothing */; +% RubyVM::OperandsUnifications.each_group do |orig, unifs| +% unifs.each do|insn| + case <%= insn.bin %>: +% insn.spec.map{|(var,val)|val}.grep_v('*').each do |val| + return <%= val %>; +% break +% end +% end +% end + } +#endif + return -1; +} diff --git a/tool/ruby_vm/views/optunifs.inc.erb b/tool/ruby_vm/views/optunifs.inc.erb new file mode 100644 index 0000000000..29d8ca2855 --- /dev/null +++ b/tool/ruby_vm/views/optunifs.inc.erb @@ -0,0 +1,21 @@ +/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */ + +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%# +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +% raise ':FIXME:TBW' if RubyVM::VmOptsH['INSTRUCTIONS_UNIFICATION'] +% n = RubyVM::Instructions.size +<%= render 'copyright' %> +<%= render 'notice', locals: { + this_file: 'is for threaded code', + edit: __FILE__, +} -%> + +/* Let .bss section automatically initialize this variable */ +/* cf. Section 6.7.8 of ISO/IEC 9899:1999 */ +static const int *const *const unified_insns_data[<%= n %>]; + +ASSERT_VM_INSTRUCTION_SIZE(unified_insns_data); diff --git a/tool/ruby_vm/views/vm.inc.erb b/tool/ruby_vm/views/vm.inc.erb new file mode 100644 index 0000000000..24181fab95 --- /dev/null +++ b/tool/ruby_vm/views/vm.inc.erb @@ -0,0 +1,30 @@ +/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */ + +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%# +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +<%= render 'copyright' %> +<%= render 'notice', locals: { + this_file: 'is VM main loop', + edit: __FILE__, +} -%> + +#include "vm_insnhelper.h" +% RubyVM::BareInstructions.to_a.each do |insn| +<%= render 'insn_entry', locals: { insn: insn } -%> +% end +% +% RubyVM::OperandsUnifications.to_a.each do |insn| +<%= render 'insn_entry', locals: { insn: insn } -%> +% end +% +% RubyVM::InstructionsUnifications.to_a.each do |insn| +<%= render 'insn_entry', locals: { insn: insn } -%> +% end +% +% RubyVM::TraceInstructions.to_a.each do |insn| +<%= render 'trace_instruction', locals: { insn: insn } -%> +% end diff --git a/tool/ruby_vm/views/vmtc.inc.erb b/tool/ruby_vm/views/vmtc.inc.erb new file mode 100644 index 0000000000..16886a1ea6 --- /dev/null +++ b/tool/ruby_vm/views/vmtc.inc.erb @@ -0,0 +1,21 @@ +/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */ + +%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved. +%# +%# This file is a part of the programming language Ruby. Permission is hereby +%# granted, to either redistribute and/or modify this file, provided that the +%# conditions mentioned in the file COPYING are met. Consult the file for +%# details. +<%= render 'copyright' -%> +<%= render 'notice', locals: { + this_file: 'is for threaded code', + edit: __FILE__, +} -%> + +static const void *const insns_address_table[] = { +% RubyVM::Instructions.each do |i| + LABEL_PTR(<%= i.name %>), +% end +}; + +ASSERT_VM_INSTRUCTION_SIZE(insns_address_table); diff --git a/vm_exec.h b/vm_exec.h index 4362ace467..e0ceb642b8 100644 --- a/vm_exec.h +++ b/vm_exec.h @@ -189,4 +189,7 @@ default: \ #define CHECK_VM_STACK_OVERFLOW_FOR_INSN(cfp, margin) #endif +#define INSN_LABEL2(insn, name) INSN_LABEL_ ## insn ## _ ## name +#define INSN_LABEL(x) INSN_LABEL2(NAME_OF_CURRENT_INSN, x) + #endif /* RUBY_VM_EXEC_H */ diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 70440e1c9d..944573f1bb 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -1189,7 +1189,7 @@ INSNS = opt_sc.inc optinsn.inc optunifs.inc insns.inc insns_info.inc \ @for %J in (\ "%I: $$(srcdir)/insns.def {$$(VPATH)}vm_opts.h \" \ " $$(srcdir)/defs/opt_operand.def $$(srcdir)/defs/opt_insn_unif.def \" \ -" $$(srcdir)/tool/instruction.rb $$(srcdir)/tool/insns2vm.rb" \ +" $$(srcdir)/tool/insns2vm.rb" \ " @$$(RM) $$(PROGRAM)" \ " $$(BASERUBY) -Ku $$(srcdir)/tool/insns2vm.rb $$(INSNS2VMOPT) %I" \ "" \ -- cgit v1.2.3