* *.def, proc.c, vm_opts.h.base, template/*: set properties.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11617 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nobu 2007-02-02 15:21:41 +00:00
parent 6cee897b59
commit c1f5d22719
17 changed files with 2850 additions and 2850 deletions

View File

@ -1,29 +1,29 @@
# #
# a definition of instruction unification # a definition of instruction unification
# #
# #
__END__ __END__
putobject putobject putobject putobject
putobject putstring putobject putstring
putobject setlocal putobject setlocal
putobject setdynamic putobject setdynamic
putstring putstring putstring putstring
putstring putobject putstring putobject
putstring setlocal putstring setlocal
putstring setdynamic putstring setdynamic
# putnil end # putnil end
dup setlocal dup setlocal
# from tarai # from tarai
getlocal getlocal getlocal getlocal
# getlocal send # getlocal send
# from tak, ackermann # from tak, ackermann
getlocal putobject getlocal putobject

View File

@ -1,59 +1,59 @@
# #
# configration file for operand union optimization # configration file for operand union optimization
# #
# format: # format:
# [insn name] op1, op2 ... # [insn name] op1, op2 ...
# #
# wildcard: * # wildcard: *
# #
__END__ __END__
getlocal 2 getlocal 2
getlocal 3 getlocal 3
getlocal 4 getlocal 4
setlocal 2 setlocal 2
setlocal 3 setlocal 3
setlocal 4 setlocal 4
getdynamic *, 0 getdynamic *, 0
getdynamic 1, 0 getdynamic 1, 0
getdynamic 2, 0 getdynamic 2, 0
getdynamic 3, 0 getdynamic 3, 0
getdynamic 4, 0 getdynamic 4, 0
setdynamic *, 0 setdynamic *, 0
setdynamic 1, 0 setdynamic 1, 0
setdynamic 2, 0 setdynamic 2, 0
setdynamic 3, 0 setdynamic 3, 0
setdynamic 4, 0 setdynamic 4, 0
putobject INT2FIX(0) putobject INT2FIX(0)
putobject INT2FIX(1) putobject INT2FIX(1)
putobject Qtrue putobject Qtrue
putobject Qfalse putobject Qfalse
# CALL # CALL
send *, *, Qfalse, 0, * send *, *, Qfalse, 0, *
send *, 0, Qfalse, 0, * send *, 0, Qfalse, 0, *
send *, 1, Qfalse, 0, * send *, 1, Qfalse, 0, *
send *, 2, Qfalse, 0, * send *, 2, Qfalse, 0, *
send *, 3, Qfalse, 0, * send *, 3, Qfalse, 0, *
# FCALL # FCALL
send *, *, Qfalse, 0x04, * send *, *, Qfalse, 0x04, *
send *, 0, Qfalse, 0x04, * send *, 0, Qfalse, 0x04, *
send *, 1, Qfalse, 0x04, * send *, 1, Qfalse, 0x04, *
send *, 2, Qfalse, 0x04, * send *, 2, Qfalse, 0x04, *
send *, 3, Qfalse, 0x04, * send *, 3, Qfalse, 0x04, *
# VCALL # VCALL
send *, 0, Qfalse, 0x0c, * send *, 0, Qfalse, 0x0c, *
__END__ __END__

3220
proc.c

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +1,21 @@
/** -*-c-*- /** -*-c-*-
This file contains YARV instructions list. This file contains YARV instructions list.
---- ----
This file is auto generated by insns2vm.rb This file is auto generated by insns2vm.rb
DO NOT TOUCH! DO NOT TOUCH!
If you want to fix something, you must edit 'template/insns.inc.tmpl' If you want to fix something, you must edit 'template/insns.inc.tmpl'
or insns2vm.rb or insns2vm.rb
*/ */
/* BIN : Basic Instruction Name */ /* BIN : Basic Instruction Name */
#define BIN(n) YARVINSN_##n #define BIN(n) YARVINSN_##n
enum{ enum{
<%= insns %> <%= insns %>
}; };
#define YARV_MAX_INSTRUCTION_SIZE <%= @insns.size %> #define YARV_MAX_INSTRUCTION_SIZE <%= @insns.size %>

View File

@ -1,77 +1,77 @@
/** -*-c-*- /** -*-c-*-
This file contains instruction information for yarv instruction sequence. This file contains instruction information for yarv instruction sequence.
---- ----
This file is auto generated by insns2vm.rb This file is auto generated by insns2vm.rb
DO NOT TOUCH! DO NOT TOUCH!
If you want to fix something, you must edit 'template/insns_info.inc.tmpl' If you want to fix something, you must edit 'template/insns_info.inc.tmpl'
or insns2vm.rb or insns2vm.rb
*/ */
<%= insn_type_chars %> <%= insn_type_chars %>
static char *insn_name_info[] = { static char *insn_name_info[] = {
<%= insn_names %> <%= insn_names %>
}; };
static char *insn_operand_info[] = { static char *insn_operand_info[] = {
<%= operands_info %> <%= operands_info %>
}; };
static int insn_len_info[] = { static int insn_len_info[] = {
<%= operands_num_info %> <%= operands_num_info %>
}; };
static int insn_stack_push_num_info[] = { static int insn_stack_push_num_info[] = {
<%= stack_num_info %> <%= stack_num_info %>
}; };
static int static int
insn_stack_increase(int depth, int insn, VALUE *opes) insn_stack_increase(int depth, int insn, VALUE *opes)
{ {
switch(insn){ switch(insn){
<%= stack_increase %> <%= stack_increase %>
default: default:
rb_bug("insn_sp_increase: unreachable"); rb_bug("insn_sp_increase: unreachable");
} }
return 0; return 0;
} }
/* some utilities */ /* some utilities */
static int static int
insn_len(int insn) insn_len(int insn)
{ {
return insn_len_info[insn]; return insn_len_info[insn];
} }
static char * static char *
insn_name(int insn) insn_name(int insn)
{ {
return insn_name_info[insn]; return insn_name_info[insn];
} }
static char * static char *
insn_op_types(int insn) insn_op_types(int insn)
{ {
return insn_operand_info[insn]; return insn_operand_info[insn];
} }
static int static int
insn_op_type(int insn, int pos) insn_op_type(int insn, int pos)
{ {
int len = insn_len(insn) - 1; int len = insn_len(insn) - 1;
if(pos < len){ if(pos < len){
return insn_operand_info[insn][pos]; return insn_operand_info[insn][pos];
} }
else{ else{
return 0; return 0;
} }
} }
static int static int
insn_ret_num(int insn) insn_ret_num(int insn)
{ {
return insn_stack_push_num_info[insn]; return insn_stack_push_num_info[insn];
} }

View File

@ -1,39 +1,39 @@
<?xml version='1.0' encoding='SHIFT_JIS'?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><!-- <?xml version='1.0' encoding='SHIFT_JIS'?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><!--
$Id: insnstbl.html,v 1.1 2004/02/05 04:09:25 ko1 Exp $ $Id$
diary index diary index
--><html xml:lang='ja' xmlns='http://www.w3.org/1999/xhtml'> --><html xml:lang='ja' xmlns='http://www.w3.org/1999/xhtml'>
<head> <head>
<meta content='text/html;charset=Shift_JIS' http-equiv='Content-Type'/> <meta content='text/html;charset=Shift_JIS' http-equiv='Content-Type'/>
<meta content='text/css' http-equiv='Content-Style-Type'/> <meta content='text/css' http-equiv='Content-Style-Type'/>
<meta content='text/javascript' http-equiv='Content-Script-Type'/> <meta content='text/javascript' http-equiv='Content-Script-Type'/>
<link href='./contents.css' rel='stylesheet' type='text/css'/> <link href='./contents.css' rel='stylesheet' type='text/css'/>
<title>YARV: Yet another RubyVM / Instruction Table</title> <title>YARV: Yet another RubyVM / Instruction Table</title>
<link href='mailto:ko1 at atdot.net' rev='made'/> <link href='mailto:ko1 at atdot.net' rev='made'/>
<link href='../index.html' rel='index'/> <link href='../index.html' rel='index'/>
</head> </head>
<body> <body>
<h1>YARV: Instruction Table</h1> <h1>YARV: Instruction Table</h1>
<table border='1' cellspacing='1' align='center'> <table border='1' cellspacing='1' align='center'>
<tr> <tr>
<th>type</th> <th>type</th>
<th>Index</th> <th>Index</th>
<th>Instruction</th> <th>Instruction</th>
<th>Operands</th> <th>Operands</th>
<th colspan='3'>Stacks</th> <th colspan='3'>Stacks</th>
</tr> </tr>
<%= tbl %> <%= tbl %>
</table> </table>
<address> <address>
SASADA Koichi / ko1 at atdot.net SASADA Koichi / ko1 at atdot.net
</address> </address>
</div> </div>
</body> </body>
</html> </html>

View File

@ -1,14 +1,14 @@
/** -*-c-*- /** -*-c-*-
This file contains YARV instructions list, to define YARVCore::Instructions. This file contains YARV instructions list, to define YARVCore::Instructions.
---- ----
This file is auto generated by insns2vm.rb This file is auto generated by insns2vm.rb
DO NOT TOUCH! DO NOT TOUCH!
If you want to fix something, you must edit 'template/minsns.inc.tmpl' If you want to fix something, you must edit 'template/minsns.inc.tmpl'
or insns2vm.rb or insns2vm.rb
*/ */
<%= defs %> <%= defs %>

View File

@ -1,32 +1,32 @@
/* -*-c-*- *********************************************************/ /* -*-c-*- *********************************************************/
/*******************************************************************/ /*******************************************************************/
/*******************************************************************/ /*******************************************************************/
/** /**
This file is for threaded code. This file is for threaded code.
---- ----
This file is auto generated by insns2vm.rb This file is auto generated by insns2vm.rb
DO NOT TOUCH! DO NOT TOUCH!
If you want to fix something, you must edit 'template/opt_sc.inc.tmpl' If you want to fix something, you must edit 'template/opt_sc.inc.tmpl'
or rb/insns2vm.rb or rb/insns2vm.rb
*/ */
#define SC_STATE_SIZE 6 #define SC_STATE_SIZE 6
#define SCS_XX 1 #define SCS_XX 1
#define SCS_AX 2 #define SCS_AX 2
#define SCS_BX 3 #define SCS_BX 3
#define SCS_AB 4 #define SCS_AB 4
#define SCS_BA 5 #define SCS_BA 5
#define SC_ERROR 0xffffffff #define SC_ERROR 0xffffffff
static VALUE sc_insn_info[][SC_STATE_SIZE] = { static VALUE sc_insn_info[][SC_STATE_SIZE] = {
<%= sc_insn_info %> <%= sc_insn_info %>
}; };
static VALUE sc_insn_next[] = { static VALUE sc_insn_next[] = {
<%= sc_insn_next %> <%= sc_insn_next %>
}; };

View File

@ -1,30 +1,30 @@
/* -*-c-*- *********************************************************/ /* -*-c-*- *********************************************************/
/*******************************************************************/ /*******************************************************************/
/*******************************************************************/ /*******************************************************************/
/** /**
This file is for threaded code. This file is for threaded code.
---- ----
This file is auto generated by insns2vm.rb This file is auto generated by insns2vm.rb
DO NOT TOUCH! DO NOT TOUCH!
If you want to fix something, you must edit 'template/optinsn.inc.tmpl' If you want to fix something, you must edit 'template/optinsn.inc.tmpl'
or rb/insns2vm.rb or rb/insns2vm.rb
*/ */
static INSN * static INSN *
insn_operands_unification(INSN *insnobj){ insn_operands_unification(INSN *insnobj){
#ifdef OPT_OPERANDS_UNIFICATION #ifdef OPT_OPERANDS_UNIFICATION
/* optimize rule */ /* optimize rule */
switch(insnobj->insn_id){ switch(insnobj->insn_id){
<%= rule %> <%= rule %>
default: default:
/* do nothing */; /* do nothing */;
break; break;
} }
#endif #endif
return insnobj; return insnobj;
} }

View File

@ -1,35 +1,35 @@
/* -*-c-*- *********************************************************/ /* -*-c-*- *********************************************************/
/*******************************************************************/ /*******************************************************************/
/*******************************************************************/ /*******************************************************************/
/** /**
This file is for threaded code. This file is for threaded code.
---- ----
This file is auto generated by insns2vm.rb This file is auto generated by insns2vm.rb
DO NOT TOUCH! DO NOT TOUCH!
If you want to fix something, you must edit 'template/optunifs.inc.tmpl' If you want to fix something, you must edit 'template/optunifs.inc.tmpl'
or rb/insns2vm.rb or rb/insns2vm.rb
*/ */
/* /*
static int UNIFIED_insn_name_1[] = {id, size, ...}; static int UNIFIED_insn_name_1[] = {id, size, ...};
static int UNIFIED_insn_name_2[] = {id, size, ...}; static int UNIFIED_insn_name_2[] = {id, size, ...};
... ...
static *int UNIFIED_insn_name[] = {size, static *int UNIFIED_insn_name[] = {size,
UNIFIED_insn_name_1, UNIFIED_insn_name_1,
UNIFIED_insn_name_2, ...}; UNIFIED_insn_name_2, ...};
... ...
static **int unified_insns_data[] = { static **int unified_insns_data[] = {
UNIFIED_insn_nameA, UNIFIED_insn_nameA,
UNIFIED_insn_nameB, ...}; UNIFIED_insn_nameB, ...};
*/ */
<%= unif_insns_each %> <%= unif_insns_each %>
<%= unif_insns %> <%= unif_insns %>
<%= unif_insns_data %> <%= unif_insns_data %>
#undef GET_INSN_NAME #undef GET_INSN_NAME

View File

@ -1,28 +1,28 @@
/* -*-c-*- *********************************************************/ /* -*-c-*- *********************************************************/
/*******************************************************************/ /*******************************************************************/
/*******************************************************************/ /*******************************************************************/
/** /**
This file is VM main loop. This file is VM main loop.
---- ----
This file is auto generated by insns2vm.rb This file is auto generated by insns2vm.rb
DO NOT TOUCH! DO NOT TOUCH!
If you want to fix something, you must edit 'insns.c' If you want to fix something, you must edit 'insns.c'
*/ */
<%= <%=
ret = '' ret = ''
offset = 15 offset = 15
line_no = 0 line_no = 0
vm_body.each_line{|line| vm_body.each_line{|line|
if line =~ /^\#line __CURRENT_LINE__/ if line =~ /^\#line __CURRENT_LINE__/
ret << line.sub(/__CURRENT_LINE__/, "#{line_no+offset}") ret << line.sub(/__CURRENT_LINE__/, "#{line_no+offset}")
else else
ret <<line ret <<line
end end
line_no += 1 line_no += 1
} }
ret ret
%> %>

View File

@ -1,18 +1,18 @@
/* -*-c-*- *********************************************************/ /* -*-c-*- *********************************************************/
/*******************************************************************/ /*******************************************************************/
/*******************************************************************/ /*******************************************************************/
/** /**
This file is for threaded code. This file is for threaded code.
---- ----
This file is auto generated by insns2vm.rb This file is auto generated by insns2vm.rb
DO NOT TOUCH! DO NOT TOUCH!
If you want to fix something, you must edit 'template/vmtc.inc.tmpl' If you want to fix something, you must edit 'template/vmtc.inc.tmpl'
or insns2vm.rb or insns2vm.rb
*/ */
static const void *insns_address_table[] = { static const void *insns_address_table[] = {
<%= insns_table %> <%= insns_table %>
}; };

View File

@ -1,7 +1,7 @@
#title YARV: Yet another RubyVM - Software Architecture #title YARV: Yet another RubyVM - Software Architecture
maybe writing. maybe writing.
* YARV instruction set * YARV instruction set
<%= d %> <%= d %>

View File

@ -1,454 +1,454 @@
#title YARVアーキテクチャ #title YARVアーキテクチャ
#set author 日本 Ruby の会 ささだこういち #set author 日本 Ruby の会 ささだこういち
- 2005-03-03(Thu) 00:31:12 +0900 いろいろと書き直し - 2005-03-03(Thu) 00:31:12 +0900 いろいろと書き直し
---- ----
* これは? * これは?
[[YARV: Yet Another RubyVM|http://www.atdot.net/yarv]] の 設計メモです。 [[YARV: Yet Another RubyVM|http://www.atdot.net/yarv]] の 設計メモです。
YARV は、Ruby プログラムのための次の機能を提供します。 YARV は、Ruby プログラムのための次の機能を提供します。
- Compiler - Compiler
- VM Generator - VM Generator
- VM (Virtual Machine) - VM (Virtual Machine)
- Assembler - Assembler
- Dis-Assembler - Dis-Assembler
- (experimental) JIT Compiler - (experimental) JIT Compiler
- (experimental) AOT Compiler - (experimental) AOT Compiler
現在の YARV は Ruby インタプリタの拡張ライブラリとして実装しています。こ 現在の YARV は Ruby インタプリタの拡張ライブラリとして実装しています。こ
れにより、Ruby インタプリタの必要な機能(パーサ、オブジェクト管理、既存 れにより、Ruby インタプリタの必要な機能(パーサ、オブジェクト管理、既存
の拡張ライブラリ)などがほぼそのまま利用できます。 の拡張ライブラリ)などがほぼそのまま利用できます。
ただし、いくつかのパッチを Ruby インタプリタに当てなければなりません。 ただし、いくつかのパッチを Ruby インタプリタに当てなければなりません。
今後は、Ruby 本体のインタプリタ部分eval.cを置き換えることを目指して 今後は、Ruby 本体のインタプリタ部分eval.cを置き換えることを目指して
開発を継続する予定です。 開発を継続する予定です。
* Compiler (compile.h, compile.c) * Compiler (compile.h, compile.c)
コンパイラは、Ruby インタプリタのパーサによって生成された構文木RNode コンパイラは、Ruby インタプリタのパーサによって生成された構文木RNode
データによる木)を YARV 命令列に変換します。YARV 命令については後述しま データによる木)を YARV 命令列に変換します。YARV 命令については後述しま
す。 す。
とくに難しいことはしていませんが、スコープなどの開始時にローカル変数の初 とくに難しいことはしていませんが、スコープなどの開始時にローカル変数の初
期化などを行い、あとは構文木を辿り変換していきます。 期化などを行い、あとは構文木を辿り変換していきます。
変換中は Ruby の Array オブジェクトに YARV 命令オブジェクト、およびオペ 変換中は Ruby の Array オブジェクトに YARV 命令オブジェクト、およびオペ
ランドを格納していき、最後に実行できる形に変換します。コンパイラでは、コ ランドを格納していき、最後に実行できる形に変換します。コンパイラでは、コ
ンパイル中に生成するメモリ領域の管理が問題になることがありますが、YARV ンパイル中に生成するメモリ領域の管理が問題になることがありますが、YARV
の場合、Ruby インタプリタがすべて面倒をみてくれるのでこの部分は非常に楽 の場合、Ruby インタプリタがすべて面倒をみてくれるのでこの部分は非常に楽
に作ることができました(ガーベージコレクタによって自動的にメモリ管理をし に作ることができました(ガーベージコレクタによって自動的にメモリ管理をし
てくれるため)。 てくれるため)。
YARV 命令は、命令を示す識別子、オペランドなど、すべて 1 word (マシンで YARV 命令は、命令を示す識別子、オペランドなど、すべて 1 word (マシンで
表現できる自然な値。C 言語ではポインタのサイズ。Ruby インタプリタ用語で 表現できる自然な値。C 言語ではポインタのサイズ。Ruby インタプリタ用語で
は VALUE のサイズで表現されます。そのため、YARV 命令はいわゆる「バイト は VALUE のサイズで表現されます。そのため、YARV 命令はいわゆる「バイト
コード」ではありません。そのため、YARV の説明などでは「命令列」という用 コード」ではありません。そのため、YARV の説明などでは「命令列」という用
語を使っています。 語を使っています。
1 word であるため、メモリの利用効率は多少悪くなりますが、アクセス速度な 1 word であるため、メモリの利用効率は多少悪くなりますが、アクセス速度な
どを考慮すると、本方式が一番いいと考えております。たとえばオペランドをコ どを考慮すると、本方式が一番いいと考えております。たとえばオペランドをコ
ンスタントプールに格納し、インデックスのみをオペランドで示すことも可能で ンスタントプールに格納し、インデックスのみをオペランドで示すことも可能で
すが、間接アクセスになってしまうので性能に影響が出るため、却下しました。 すが、間接アクセスになってしまうので性能に影響が出るため、却下しました。
* VM Generator (rb/insns2vm.rb, insns.def) * VM Generator (rb/insns2vm.rb, insns.def)
rb/insns2vm.rb というスクリプトは、insns.def というファイルを読み込み、 rb/insns2vm.rb というスクリプトは、insns.def というファイルを読み込み、
VM のために必要なファイルを生成します。具体的には、命令を実行する部分を VM のために必要なファイルを生成します。具体的には、命令を実行する部分を
生成しますが、ほかにもコンパイルに必要な情報、最適化に必要な情報、やアセ 生成しますが、ほかにもコンパイルに必要な情報、最適化に必要な情報、やアセ
ンブラ、逆アセンブラに必要な情報を示すファイルも生成します。 ンブラ、逆アセンブラに必要な情報を示すファイルも生成します。
** 命令記述 ** 命令記述
insns.def には、各命令がどのような命令であるかを記述します。具体的には次 insns.def には、各命令がどのような命令であるかを記述します。具体的には次
の情報を記述します。 の情報を記述します。
- 命令の名前 - 命令の名前
- その命令のカテゴリ、コメント(英語、日本語) - その命令のカテゴリ、コメント(英語、日本語)
- オペランドの名前 - オペランドの名前
- その命令実行前にスタックからポップする値 - その命令実行前にスタックからポップする値
- その命令実行後にスタックにプッシュする値 - その命令実行後にスタックにプッシュする値
- その命令のロジックC 言語で記述) - その命令のロジックC 言語で記述)
たとえば、スタックに self をおく putself という命令は次のように記述しま たとえば、スタックに self をおく putself という命令は次のように記述しま
す。 す。
#code #code
/** /**
@c put @c put
@e put self. @e put self.
@j self を置く。 @j self を置く。
*/ */
DEFINE_INSN DEFINE_INSN
putself putself
() ()
() ()
(VALUE val) (VALUE val)
{ {
val = GET_SELF(); val = GET_SELF();
} }
#end #end
この場合、オペランドと、スタックからポップする値は無いことになります。命 この場合、オペランドと、スタックからポップする値は無いことになります。命
令終了後、self をスタックトップに置きたいわけですが、それは val という、 令終了後、self をスタックトップに置きたいわけですが、それは val という、
スタックにプッシュする値として宣言しておいた変数に代入しておくことで、こ スタックにプッシュする値として宣言しておいた変数に代入しておくことで、こ
れを変換するとスタックトップに置く C プログラムが生成されます。 れを変換するとスタックトップに置く C プログラムが生成されます。
細かいフォーマットは insns.def の冒頭を参照してください。そんなに難しく 細かいフォーマットは insns.def の冒頭を参照してください。そんなに難しく
ないと思います。 ないと思います。
insnhelper.h というファイルに、命令ロジックを記述するために必要なマクロ insnhelper.h というファイルに、命令ロジックを記述するために必要なマクロ
が定義されています。また、VM の内部構造に関する定義は vm.h というファイ が定義されています。また、VM の内部構造に関する定義は vm.h というファイ
ルにあります。 ルにあります。
* VM (Virtual Machine, vm.h, vm.c) * VM (Virtual Machine, vm.h, vm.c)
VM は、実際にコンパイルした結果生成される YARV 命令列を実行します。まさ VM は、実際にコンパイルした結果生成される YARV 命令列を実行します。まさ
に、この部分が YARV のキモになり、将来的には eval.c をこの VM で置き換え に、この部分が YARV のキモになり、将来的には eval.c をこの VM で置き換え
たいと考えています。 たいと考えています。
現在の Ruby インタプリタで実行できるすべてのことが、この VM で実現できる 現在の Ruby インタプリタで実行できるすべてのことが、この VM で実現できる
ように作っています(現段階ではまだ完全ではありませんが、そうなるべきです)。 ように作っています(現段階ではまだ完全ではありませんが、そうなるべきです)。
VM は、単純なスタックマシンとして実装しています。スレッドひとつにスタッ VM は、単純なスタックマシンとして実装しています。スレッドひとつにスタッ
クひとつを保持します。スタックの領域はヒープから取得するので、柔軟な領域 クひとつを保持します。スタックの領域はヒープから取得するので、柔軟な領域
設定が可能です。 設定が可能です。
** レジスタ ** レジスタ
VM は 5 つの仮想的なレジスタによって制御されます。 VM は 5 つの仮想的なレジスタによって制御されます。
- PC (Program Counter) - PC (Program Counter)
- SP (Stack Pointer) - SP (Stack Pointer)
- CFP (Control Frame Pointer) - CFP (Control Frame Pointer)
- LFP (Local Frame Pointer) - LFP (Local Frame Pointer)
- DFP (Dynamic Frame Pointer) - DFP (Dynamic Frame Pointer)
PC は現在実行中の命令列の位置を示します。SP はスタックトップの位置を示し PC は現在実行中の命令列の位置を示します。SP はスタックトップの位置を示し
ます。CFP、LFP、DFP はそれぞれフレームの情報を示します。詳細は後述します。 ます。CFP、LFP、DFP はそれぞれフレームの情報を示します。詳細は後述します。
** スタックフレーム ** スタックフレーム
obsolete (update soon) obsolete (update soon)
** フレームデザインについての補足 ** フレームデザインについての補足
Lisp の処理系などをかんがえると、わざわざブロックローカルフレームとメソ Lisp の処理系などをかんがえると、わざわざブロックローカルフレームとメソ
ッドローカルフレームのようなものを用意するのは奇異に見えるかもしれません。 ッドローカルフレームのようなものを用意するのは奇異に見えるかもしれません。
あるフレームを、入れ子構造にして、ローカル変数のアクセスはその入れ子を外 あるフレームを、入れ子構造にして、ローカル変数のアクセスはその入れ子を外
側に辿れば必ずたどり着くことができるからですつまり、lfp は必要ない)。 側に辿れば必ずたどり着くことができるからですつまり、lfp は必要ない)。
しかし、Ruby ではいくつか状況が違います。まず、メソッドローカルな情報が しかし、Ruby ではいくつか状況が違います。まず、メソッドローカルな情報が
あること、具体的にはブロックとselfcallee からみると recieverです。こ あること、具体的にはブロックとselfcallee からみると recieverです。こ
の情報をそれぞれのフレームにもたせるのは無駄です。 の情報をそれぞれのフレームにもたせるのは無駄です。
また、Ruby2.0 からはブロックローカル変数はなくなります(ブロックローカル また、Ruby2.0 からはブロックローカル変数はなくなります(ブロックローカル
引数は残るので、構造自体はあまり変わりません)。そのため、メソッドローカ 引数は残るので、構造自体はあまり変わりません)。そのため、メソッドローカ
ル変数へのアクセスが頻発することが予想されます。 ル変数へのアクセスが頻発することが予想されます。
このとき、メソッドローカル変数へのアクセスのたびにフレーム(スコープ)の このとき、メソッドローカル変数へのアクセスのたびにフレーム(スコープ)の
リストをたどるのは無駄であると判断し、明示的にメソッドローカルスコープと リストをたどるのは無駄であると判断し、明示的にメソッドローカルスコープと
ブロックフレームを分離し、ブロックフレームからはメソッドローカルフレーム ブロックフレームを分離し、ブロックフレームからはメソッドローカルフレーム
が lfpレジスタによって容易にアクセスできるようにしました。 が lfpレジスタによって容易にアクセスできるようにしました。
** メソッド呼び出しについて ** メソッド呼び出しについて
メソッド呼び出しは、YARV 命令列で記述されたメソッドか、C で記述されたメ メソッド呼び出しは、YARV 命令列で記述されたメソッドか、C で記述されたメ
ソッドかによってディスパッチ手法が変わります。 ソッドかによってディスパッチ手法が変わります。
YARV 命令列であった場合、上述したスタックフレームを作成して命令を継続し YARV 命令列であった場合、上述したスタックフレームを作成して命令を継続し
ます。とくに VM の関数を再帰呼び出すすることは行ないません。 ます。とくに VM の関数を再帰呼び出すすることは行ないません。
C で記述されたメソッドだった場合、単純にその関数を呼び出します(ただし、 C で記述されたメソッドだった場合、単純にその関数を呼び出します(ただし、
バックトレースを正しく生成するためにメソッド呼び出しの情報を付加してから バックトレースを正しく生成するためにメソッド呼び出しの情報を付加してから
行ないます)。 行ないます)。
このため、VM 用スタックを別途用意したものの、プログラムによってはマシン このため、VM 用スタックを別途用意したものの、プログラムによってはマシン
スタックを使い切ってしまう可能性がありますC -> Ruby -> C -> ... という スタックを使い切ってしまう可能性がありますC -> Ruby -> C -> ... という
呼び出しが続いた場合)。これは、現在では避けられない仕様となっています。 呼び出しが続いた場合)。これは、現在では避けられない仕様となっています。
** 例外 ** 例外
例外は、Java の JVM と同様に例外テーブルを用意することで実現します。例外 例外は、Java の JVM と同様に例外テーブルを用意することで実現します。例外
が発生したら、当該フレームを、例外テーブルを検査します。そこで、例外が発 が発生したら、当該フレームを、例外テーブルを検査します。そこで、例外が発
生したときの PC の値に合致するエントリがあった場合、そのエントリに従って 生したときの PC の値に合致するエントリがあった場合、そのエントリに従って
動作します。もしエントリが見つからなかった場合、スタックを撒き戻してまた 動作します。もしエントリが見つからなかった場合、スタックを撒き戻してまた
同様にそのスコープの例外テーブルを検査します。 同様にそのスコープの例外テーブルを検査します。
また、break、returnブロック中、retry なども同様の仕組みで実現します。 また、break、returnブロック中、retry なども同様の仕組みで実現します。
*** 例外テーブル *** 例外テーブル
例外テーブルエントリは具体的には次の情報が格納されています。 例外テーブルエントリは具体的には次の情報が格納されています。
- 対象とする PC の範囲 - 対象とする PC の範囲
- 対象とする例外の種類 - 対象とする例外の種類
- もし対象となったときにジャンプする先(種類による) - もし対象となったときにジャンプする先(種類による)
- もし対象となったときに起動するブロックの iseq - もし対象となったときに起動するブロックの iseq
*** rescue *** rescue
rescue 節はブロックとして実現しています。$! の値を唯一の引数として持ちま rescue 節はブロックとして実現しています。$! の値を唯一の引数として持ちま
す。 す。
#code #code
begin begin
rescue A rescue A
rescue B rescue B
rescue C rescue C
end end
#end #end
は、次のような Ruby スクリプトに変換されます。 は、次のような Ruby スクリプトに変換されます。
#code #code
{|err| {|err|
case err case err
when A === err when A === err
when B === err when B === err
when C === err when C === err
else else
raise # yarv の命令では throw raise # yarv の命令では throw
end end
} }
#end #end
*** ensure *** ensure
正常系例外が発生しなかった場合と異常系例外が発生したときなどの2 正常系例外が発生しなかった場合と異常系例外が発生したときなどの2
種類の命令列が生成されます。正常系では、ただの連続したコード領域としてコ 種類の命令列が生成されます。正常系では、ただの連続したコード領域としてコ
ンパイルされます。また、異常系ではブロックとして実装します。最後は必ず ンパイルされます。また、異常系ではブロックとして実装します。最後は必ず
throw 命令で締めることになります。 throw 命令で締めることになります。
*** break, returnブロック中、retry *** break, returnブロック中、retry
break 文、ブロック中の return 文、retry 文は throw 命令としてコンパイル break 文、ブロック中の return 文、retry 文は throw 命令としてコンパイル
されます。どこまで戻るかは、break をフックする例外テーブルのエントリが判 されます。どこまで戻るかは、break をフックする例外テーブルのエントリが判
断します。 断します。
** 定数の検索 ** 定数の検索
定数という名前なのに、Ruby ではコンパイル時に決定しません。というか、い 定数という名前なのに、Ruby ではコンパイル時に決定しません。というか、い
つまでも再定義可能になっています。 つまでも再定義可能になっています。
定数アクセスのためのRuby記述は次のようになります。 定数アクセスのためのRuby記述は次のようになります。
#code #code
Ruby表現: Ruby表現:
expr::ID::...::ID expr::ID::...::ID
#end #end
これは、yarv命令セットでは次のようになります。 これは、yarv命令セットでは次のようになります。
#code #code
(expr) (expr)
getconstant ID getconstant ID
... ...
getconstant ID getconstant ID
#end #end
*** 定数検索パス *** 定数検索パス
もし expr が nil だった場合、定数検索パスに従って定数を検索します。この もし expr が nil だった場合、定数検索パスに従って定数を検索します。この
挙動は今後 Ruby 2.0 に向けて変更される場合があります。 挙動は今後 Ruby 2.0 に向けて変更される場合があります。
+ クラス、モジュールの動的ネスト関係(プログラムの字面上)をルートまで辿る + クラス、モジュールの動的ネスト関係(プログラムの字面上)をルートまで辿る
+ 継承関係をルートObjectまで辿る + 継承関係をルートObjectまで辿る
このため、クラス、モジュールの動的ネスト関係を保存しなければなりません。 このため、クラス、モジュールの動的ネスト関係を保存しなければなりません。
このために、thread_object には klass_nest_stack というものを用意しました。 このために、thread_object には klass_nest_stack というものを用意しました。
これは、現在のネストの情報を保存します。 これは、現在のネストの情報を保存します。
メソッド定義時、その現在のネスト情報をメソッド定義時にdupして加える メソッド定義時、その現在のネスト情報をメソッド定義時にdupして加える
ことで、そのメソッドの実行時、そのネスト情報を参照することが可能になりま ことで、そのメソッドの実行時、そのネスト情報を参照することが可能になりま
す。 す。
トップレベルでは、その情報はないことになります。 トップレベルでは、その情報はないことになります。
クラス/モジュール定義文実行時は、現在の情報そのものを参照することになり クラス/モジュール定義文実行時は、現在の情報そのものを参照することになり
ます。これは、クラススコープ突入時、その情報をクラス定義文にコピーします ます。これは、クラススコープ突入時、その情報をクラス定義文にコピーします
(すでにコピーされていれば、これを行いません)。 (すでにコピーされていれば、これを行いません)。
これにより、動的なネスト情報を統一的に扱うことができます。 これにより、動的なネスト情報を統一的に扱うことができます。
** 最適化手法 ** 最適化手法
YARV では高速化を目的としているので、さまざまな最適化手法を利用していま YARV では高速化を目的としているので、さまざまな最適化手法を利用していま
す。詳細は割愛しますが、以下に述べる最適化などを行なっております。 す。詳細は割愛しますが、以下に述べる最適化などを行なっております。
*** threaded code *** threaded code
GCC の C 言語拡張である値としてのラベルを利用して direct threaded code GCC の C 言語拡張である値としてのラベルを利用して direct threaded code
を実現しています。 を実現しています。
*** Peephole optimization *** Peephole optimization
いくつかの簡単な最適化をしています。 いくつかの簡単な最適化をしています。
*** inline method cache *** inline method cache
命令列の中にメソッド検索結果を埋め込みます。 命令列の中にメソッド検索結果を埋め込みます。
*** inline constant cache *** inline constant cache
命令列の中に定数検索結果を埋め込みます。 命令列の中に定数検索結果を埋め込みます。
*** ブロックと Proc オブジェクトの分離 *** ブロックと Proc オブジェクトの分離
ブロック付きメソッド呼び出しが行なわれたときにはすぐにはブロックを Proc ブロック付きメソッド呼び出しが行なわれたときにはすぐにはブロックを Proc
オブジェクトとして生成しません。これにより、必要ない Proc オブジェクトの オブジェクトとして生成しません。これにより、必要ない Proc オブジェクトの
生成を抑えています。 生成を抑えています。
Proc メソッドは、実際に必要になった時点で作られ、そのときに環境(スコー Proc メソッドは、実際に必要になった時点で作られ、そのときに環境(スコー
プ上に確保された変数など)をヒープに保存します。 プ上に確保された変数など)をヒープに保存します。
*** 特化命令 *** 特化命令
Fixnum 同士の加算などを正直に関数呼び出しによって行なうと、コストがかか Fixnum 同士の加算などを正直に関数呼び出しによって行なうと、コストがかか
るので、これらのプリミティブな操作を行なうためのメソッド呼び出しは専用命 るので、これらのプリミティブな操作を行なうためのメソッド呼び出しは専用命
令を用意しました。 令を用意しました。
*** 命令融合 *** 命令融合
複数の命令を 1 命令に変換します。融合命令は opt_insn_unif.def の記述によ 複数の命令を 1 命令に変換します。融合命令は opt_insn_unif.def の記述によ
り自動的に生成されます。 り自動的に生成されます。
*** オペランド融合 *** オペランド融合
複数のオペランドを含めた命令を生成します。融合命令は opt_operand.def の 複数のオペランドを含めた命令を生成します。融合命令は opt_operand.def の
記述によって自動的に生成されます。 記述によって自動的に生成されます。
*** stack caching *** stack caching
スタックトップを仮想レジスタに保持するようにします。現在は 2 個の仮想レ スタックトップを仮想レジスタに保持するようにします。現在は 2 個の仮想レ
ジスタを想定し、5状態のスタックキャッシングを行ないます。スタックキャッ ジスタを想定し、5状態のスタックキャッシングを行ないます。スタックキャッ
シングする命令は自動的に生成されます。 シングする命令は自動的に生成されます。
*** JIT Compile *** JIT Compile
機械語を切り貼りします。非常に実験的なコードものしか作っておりません。ほ 機械語を切り貼りします。非常に実験的なコードものしか作っておりません。ほ
とんどのプログラムは動きません。 とんどのプログラムは動きません。
*** AOT Compile *** AOT Compile
YARV 命令列を C 言語に変換します。まだ十分な最適化を行なえておりませんが、 YARV 命令列を C 言語に変換します。まだ十分な最適化を行なえておりませんが、
それなりに動きます。rb/aotc.rb がコンパイラです。 それなりに動きます。rb/aotc.rb がコンパイラです。
* Assembler (rb/yasm.rb) * Assembler (rb/yasm.rb)
YARV 命令列のアセンブラを用意しました。使い方は rb/yasm.rb を参照してく YARV 命令列のアセンブラを用意しました。使い方は rb/yasm.rb を参照してく
ださい(まだ、例示してある生成手法のすべてをサポートしているわけではあり ださい(まだ、例示してある生成手法のすべてをサポートしているわけではあり
ません)。 ません)。
* Dis-Assembler (disasm.c) * Dis-Assembler (disasm.c)
YARV 命令列を示すオブジェクト YARVCore::InstructionSequence には disasm YARV 命令列を示すオブジェクト YARVCore::InstructionSequence には disasm
メソッドがあります。これは、命令列を逆アセンブルした文字列を返します。 メソッドがあります。これは、命令列を逆アセンブルした文字列を返します。
* YARV 命令セット * YARV 命令セット
<%= d %> <%= d %>
* その他 * その他
** テスト ** テスト
test/test_* がテストケースです。一応、ミスなく動くはずです。逆にいうと、 test/test_* がテストケースです。一応、ミスなく動くはずです。逆にいうと、
このテストに記述されている例ではきちんと動作するということです。 このテストに記述されている例ではきちんと動作するということです。
** ベンチマーク ** ベンチマーク
benchmark/bm_* にベンチマークプログラムがおいてあります。 benchmark/bm_* にベンチマークプログラムがおいてあります。
** 今後の予定 ** 今後の予定
まだまだやらなければいけないこと、未実装部分がたくさんありますんでやって まだまだやらなければいけないこと、未実装部分がたくさんありますんでやって
いかなければなりません。一番大きな目標は eval.c を置き換えることでしょう いかなければなりません。一番大きな目標は eval.c を置き換えることでしょう
か。 か。
*** Verifier *** Verifier
YARV 命令列は、ミスがあっても動かしてしまうため危険である可能性がありま YARV 命令列は、ミスがあっても動かしてしまうため危険である可能性がありま
す。そのため、スタックの利用状態をきちんと事前に検証するようなベリファイ す。そのため、スタックの利用状態をきちんと事前に検証するようなベリファイ
アを用意しなければならないと考えています。 アを用意しなければならないと考えています。
*** Compiled File の構想 *** Compiled File の構想
Ruby プログラムをこの命令セットにシリアライズしたデータ構造をファイルに Ruby プログラムをこの命令セットにシリアライズしたデータ構造をファイルに
出力できるようにしたいと考えています。これを利用して一度コンパイルした命 出力できるようにしたいと考えています。これを利用して一度コンパイルした命
令列をファイルに保存しておけば、次回ロード時にはコンパイルの手間、コスト 令列をファイルに保存しておけば、次回ロード時にはコンパイルの手間、コスト
を省くことができます。 を省くことができます。
**** 全体構成 **** 全体構成
次のようなファイル構成を考えていますが、まだ未定です。 次のようなファイル構成を考えていますが、まだ未定です。
#code #code
u4 : 4 byte unsigned storage u4 : 4 byte unsigned storage
u2 : 2 byte unsigned storage u2 : 2 byte unsigned storage
u1 : 1 byte unsigned storage u1 : 1 byte unsigned storage
every storages are little endian :-) every storages are little endian :-)
CompiledFile{ CompiledFile{
u4 magic; u4 magic;
u2 major; u2 major;
u2 minor; u2 minor;
u4 character_code; u4 character_code;
u4 constants_pool_count; u4 constants_pool_count;
ConstantEntry constants_pool[constants_pool_count]; ConstantEntry constants_pool[constants_pool_count];
u4 block_count; u4 block_count;
blockEntry blocks[block_count]; blockEntry blocks[block_count];
u4 method_count; u4 method_count;
MethodEntry methods[method_count]; MethodEntry methods[method_count];
} }
#end #end
Java classfile のパクリ。 Java classfile のパクリ。

View File

@ -1,20 +1,20 @@
# -*-ruby-*- # -*-ruby-*-
# #
module YARVCore module YARVCore
class InstructionSequence class InstructionSequence
class Instruction class Instruction
InsnID2NO = { InsnID2NO = {
<%= insn_id2no %> <%= insn_id2no %>
} }
def self.id2insn_no id def self.id2insn_no id
if InsnID2NO.has_key? id if InsnID2NO.has_key? id
InsnID2NO[id] InsnID2NO[id]
end end
end end
end end
end end
end end

View File

@ -1,330 +1,330 @@
/* -*- c -*- */ /* -*- c -*- */
/* do not use C++ style comment */ /* do not use C++ style comment */
/* */ /* */
MACRO macro_eval_setup_send_arguments(num, blockptr, flag, blockiseq) MACRO macro_eval_setup_send_arguments(num, blockptr, flag, blockiseq)
{ {
if (flag & VM_CALL_ARGS_BLOCKARG_BIT) { if (flag & VM_CALL_ARGS_BLOCKARG_BIT) {
yarv_proc_t *po; yarv_proc_t *po;
VALUE proc; VALUE proc;
proc = TOPN(0); proc = TOPN(0);
if (proc != Qnil) { if (proc != Qnil) {
if (!yarv_obj_is_proc(proc)) { if (!yarv_obj_is_proc(proc)) {
proc = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc"); proc = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc");
if (!yarv_obj_is_proc(proc)) { if (!yarv_obj_is_proc(proc)) {
rb_raise(rb_eTypeError, rb_raise(rb_eTypeError,
"wrong argument type %s (expected Proc)", "wrong argument type %s (expected Proc)",
rb_obj_classname(proc)); rb_obj_classname(proc));
} }
} }
GetProcPtr(proc, po); GetProcPtr(proc, po);
blockptr = &po->block; blockptr = &po->block;
GET_BLOCK_PTR_IN_CFP(reg_cfp)->proc = proc; GET_BLOCK_PTR_IN_CFP(reg_cfp)->proc = proc;
} }
INC_SP(-1); INC_SP(-1);
} }
else if (blockiseq) { else if (blockiseq) {
blockptr = GET_BLOCK_PTR_IN_CFP(reg_cfp); blockptr = GET_BLOCK_PTR_IN_CFP(reg_cfp);
blockptr->iseq = blockiseq; blockptr->iseq = blockiseq;
blockptr->proc = 0; blockptr->proc = 0;
} }
/* expand top of stack? */ /* expand top of stack? */
if (flag & VM_CALL_ARGS_SPLAT_BIT) { if (flag & VM_CALL_ARGS_SPLAT_BIT) {
VALUE ary = TOPN(0); VALUE ary = TOPN(0);
VALUE *ptr, *dst; VALUE *ptr, *dst;
int i; int i;
VALUE tmp = rb_check_convert_type(ary, T_ARRAY, "Array", "to_splat"); VALUE tmp = rb_check_convert_type(ary, T_ARRAY, "Array", "to_splat");
if (NIL_P(tmp)) { if (NIL_P(tmp)) {
tmp = rb_ary_new3(1, ary); tmp = rb_ary_new3(1, ary);
} }
ary = tmp; ary = tmp;
ptr = RARRAY_PTR(ary); ptr = RARRAY_PTR(ary);
dst = GET_SP() - 1; dst = GET_SP() - 1;
for (i = 0; i < RARRAY_LEN(ary); i++) { for (i = 0; i < RARRAY_LEN(ary); i++) {
dst[i] = ptr[i]; dst[i] = ptr[i];
} }
num += i - 1; num += i - 1;
INC_SP(i - 1); INC_SP(i - 1);
} }
} }
MACRO macro_eval_invoke_cfunc(num, id, recv, klass, mn, blockptr) MACRO macro_eval_invoke_cfunc(num, id, recv, klass, mn, blockptr)
{ {
yarv_control_frame_t *cfp = yarv_control_frame_t *cfp =
push_frame(th, 0, FRAME_MAGIC_CFUNC, push_frame(th, 0, FRAME_MAGIC_CFUNC,
recv, (VALUE) blockptr, 0, GET_SP(), 0, 1); recv, (VALUE) blockptr, 0, GET_SP(), 0, 1);
cfp->callee_id = id; /* TODO */ cfp->callee_id = id; /* TODO */
cfp->method_id = id; cfp->method_id = id;
cfp->method_klass = klass; cfp->method_klass = klass;
reg_cfp->sp -= num + 1; reg_cfp->sp -= num + 1;
val = call_cfunc(mn->nd_cfnc, recv, mn->nd_argc, num, reg_cfp->sp + 1); val = call_cfunc(mn->nd_cfnc, recv, mn->nd_argc, num, reg_cfp->sp + 1);
if (reg_cfp != th->cfp + 1) { if (reg_cfp != th->cfp + 1) {
SDR2(reg_cfp); SDR2(reg_cfp);
SDR2(th->cfp-5); SDR2(th->cfp-5);
rb_bug("cfp consistency error - send"); rb_bug("cfp consistency error - send");
th->cfp = reg_cfp; th->cfp = reg_cfp;
} }
pop_frame(th); pop_frame(th);
} }
MACRO macro_eval_invoke_func(niseqval, recv, klass, blockptr, num) MACRO macro_eval_invoke_func(niseqval, recv, klass, blockptr, num)
{ {
yarv_iseq_t *niseq; yarv_iseq_t *niseq;
VALUE *sp = GET_SP(); VALUE *sp = GET_SP();
VALUE *rsp = sp - num - 1; VALUE *rsp = sp - num - 1;
int opt_pc = 0, clear_local_size, i; int opt_pc = 0, clear_local_size, i;
/* TODO: eliminate it */ /* TODO: eliminate it */
GetISeqPtr(niseqval, niseq); GetISeqPtr(niseqval, niseq);
clear_local_size = niseq->local_size - num; clear_local_size = niseq->local_size - num;
/* set arguments */ /* set arguments */
if (niseq->arg_simple) { if (niseq->arg_simple) {
if (niseq->argc != num) { if (niseq->argc != num) {
rb_raise(rb_eArgError, "wrong number of arguments (%lu for %d)", rb_raise(rb_eArgError, "wrong number of arguments (%lu for %d)",
(unsigned long)num, niseq->argc); (unsigned long)num, niseq->argc);
} }
} }
else { else {
/* check optional arguments */ /* check optional arguments */
if (niseq->arg_opts) { if (niseq->arg_opts) {
int iseq_argc = niseq->argc; int iseq_argc = niseq->argc;
int opts = niseq->arg_opts - 1; int opts = niseq->arg_opts - 1;
if (num < iseq_argc || if (num < iseq_argc ||
(niseq->arg_rest == 0 && num > iseq_argc + opts)) { (niseq->arg_rest == 0 && num > iseq_argc + opts)) {
if (0) { if (0) {
printf("num: %lu, iseq_argc: %d, opts: %d\n", printf("num: %lu, iseq_argc: %d, opts: %d\n",
(unsigned long)num, iseq_argc, opts); (unsigned long)num, iseq_argc, opts);
} }
rb_raise(rb_eArgError, rb_raise(rb_eArgError,
"wrong number of arguments (%lu for %d)", "wrong number of arguments (%lu for %d)",
(unsigned long)num, iseq_argc); (unsigned long)num, iseq_argc);
} }
if (0) { if (0) {
printf("num: %lu, opts: %d, iseq_argc: %d\n", printf("num: %lu, opts: %d, iseq_argc: %d\n",
(unsigned long)num, opts, iseq_argc); (unsigned long)num, opts, iseq_argc);
} }
if (num - iseq_argc < opts) { if (num - iseq_argc < opts) {
opt_pc = niseq->arg_opt_tbl[num - iseq_argc]; opt_pc = niseq->arg_opt_tbl[num - iseq_argc];
sp += opts - (num - iseq_argc); sp += opts - (num - iseq_argc);
num += opts - (num - iseq_argc); num += opts - (num - iseq_argc);
clear_local_size = niseq->local_size - (iseq_argc + opts); clear_local_size = niseq->local_size - (iseq_argc + opts);
} }
else { else {
opt_pc = niseq->arg_opt_tbl[opts]; opt_pc = niseq->arg_opt_tbl[opts];
} }
} }
/* check rest */ /* check rest */
if (niseq->arg_rest == -1) { if (niseq->arg_rest == -1) {
if (niseq->arg_opts) { if (niseq->arg_opts) {
num = niseq->argc + niseq->arg_opts; num = niseq->argc + niseq->arg_opts;
} }
else { else {
num = niseq->argc; num = niseq->argc;
} }
sp = &rsp[1 + num + 1]; sp = &rsp[1 + num + 1];
} }
else if (niseq->arg_rest != 0) { else if (niseq->arg_rest != 0) {
int rest = niseq->arg_rest - 1; int rest = niseq->arg_rest - 1;
int pack_size = num - rest; int pack_size = num - rest;
if (0) { if (0) {
printf("num: %lu, rest: %d, ps: %d\n", printf("num: %lu, rest: %d, ps: %d\n",
(unsigned long)num, niseq->arg_rest, pack_size); (unsigned long)num, niseq->arg_rest, pack_size);
} }
if (pack_size < 0) { if (pack_size < 0) {
rb_raise(rb_eArgError, rb_raise(rb_eArgError,
"wrong number of arguments (%lu for %d)", "wrong number of arguments (%lu for %d)",
(unsigned long)num, rest - niseq->arg_opts); (unsigned long)num, rest - niseq->arg_opts);
} }
/* /*
* def m(x,y,z,*a) => * def m(x,y,z,*a) =>
* x, y, z, a, b, c <SP> => x, y, z, [a,b,c], <SP> * x, y, z, a, b, c <SP> => x, y, z, [a,b,c], <SP>
*/ */
rsp[1 + rest] = rb_ary_new4(pack_size, &rsp[1 + rest]); rsp[1 + rest] = rb_ary_new4(pack_size, &rsp[1 + rest]);
sp = &rsp[2 + rest]; sp = &rsp[2 + rest];
num = rest + 1; num = rest + 1;
clear_local_size = niseq->local_size - rest - 1; clear_local_size = niseq->local_size - rest - 1;
} }
/* block argument */ /* block argument */
if (niseq->arg_block != 0) { if (niseq->arg_block != 0) {
VALUE arg_block_val = Qnil; VALUE arg_block_val = Qnil;
if (!((niseq->arg_rest && num == niseq->arg_rest) || if (!((niseq->arg_rest && num == niseq->arg_rest) ||
(niseq->arg_opts (niseq->arg_opts
&& num == niseq->argc + niseq->arg_opts - 1) && num == niseq->argc + niseq->arg_opts - 1)
|| num == niseq->argc)) { || num == niseq->argc)) {
rb_raise(rb_eArgError, rb_raise(rb_eArgError,
"wrong number of arguments (%lu for %d)", "wrong number of arguments (%lu for %d)",
(unsigned long)num, niseq->argc); (unsigned long)num, niseq->argc);
} }
if (blockptr) { if (blockptr) {
/* make Proc object */ /* make Proc object */
if (blockptr->proc == 0) { if (blockptr->proc == 0) {
yarv_proc_t *proc; yarv_proc_t *proc;
reg_cfp->sp = sp; reg_cfp->sp = sp;
arg_block_val = th_make_proc(th, GET_CFP(), blockptr); arg_block_val = th_make_proc(th, GET_CFP(), blockptr);
GetProcPtr(arg_block_val, proc); GetProcPtr(arg_block_val, proc);
blockptr = &proc->block; blockptr = &proc->block;
} }
else { else {
arg_block_val = blockptr->proc; arg_block_val = blockptr->proc;
} }
} }
rsp[1 + niseq->arg_block - 1] = arg_block_val; rsp[1 + niseq->arg_block - 1] = arg_block_val;
sp++; sp++;
clear_local_size--; clear_local_size--;
} }
} }
/* stack overflow check */ /* stack overflow check */
if (CHECK_STACK_OVERFLOW(th, GET_CFP(), niseq->stack_max + 0x100)) { if (CHECK_STACK_OVERFLOW(th, GET_CFP(), niseq->stack_max + 0x100)) {
rb_exc_raise(sysstack_error); rb_exc_raise(sysstack_error);
} }
for (i = 0; i < clear_local_size; i++) { for (i = 0; i < clear_local_size; i++) {
*sp++ = Qnil; *sp++ = Qnil;
} }
{ {
if (0 && (flag & VM_CALL_TAILCALL_BIT)) { if (0 && (flag & VM_CALL_TAILCALL_BIT)) {
th->cfp++; th->cfp++;
push_frame(th, niseq, FRAME_MAGIC_METHOD, push_frame(th, niseq, FRAME_MAGIC_METHOD,
recv, (VALUE) blockptr, recv, (VALUE) blockptr,
niseq->iseq_encoded + opt_pc, sp, 0, 0); niseq->iseq_encoded + opt_pc, sp, 0, 0);
} }
else if (0 && else if (0 &&
(flag & VM_CALL_TAILRECURSION_BIT) && niseq == GET_ISEQ()) { (flag & VM_CALL_TAILRECURSION_BIT) && niseq == GET_ISEQ()) {
/* do nothing */ /* do nothing */
GET_CFP()->self = recv; GET_CFP()->self = recv;
SET_LFP(sp); SET_LFP(sp);
SET_DFP(sp); SET_DFP(sp);
*sp++ = (VALUE) blockptr; *sp++ = (VALUE) blockptr;
reg_cfp->sp = sp; reg_cfp->sp = sp;
reg_cfp->bp = sp; reg_cfp->bp = sp;
SET_PC(niseq->iseq_encoded + opt_pc); SET_PC(niseq->iseq_encoded + opt_pc);
} }
else { else {
push_frame(th, niseq, push_frame(th, niseq,
FRAME_MAGIC_METHOD, recv, (VALUE) blockptr, FRAME_MAGIC_METHOD, recv, (VALUE) blockptr,
niseq->iseq_encoded + opt_pc, sp, 0, 0); niseq->iseq_encoded + opt_pc, sp, 0, 0);
reg_cfp->sp = rsp; reg_cfp->sp = rsp;
} }
RESTORE_REGS(); RESTORE_REGS();
} }
} }
MACRO macro_eval_invoke_method(recv, klass, id, num, mn, blockptr) MACRO macro_eval_invoke_method(recv, klass, id, num, mn, blockptr)
{ {
/* method missing */ /* method missing */
if (mn == 0) { if (mn == 0) {
/* temporarily */ /* temporarily */
if (id == idMethodMissing) { if (id == idMethodMissing) {
rb_bug("method missing"); rb_bug("method missing");
} }
else { else {
int stat = 0; int stat = 0;
if (flag & VM_CALL_VCALL_BIT) { if (flag & VM_CALL_VCALL_BIT) {
stat |= NOEX_VCALL; stat |= NOEX_VCALL;
} }
if (flag & VM_CALL_SUPER_BIT) { if (flag & VM_CALL_SUPER_BIT) {
stat |= NOEX_SUPER; stat |= NOEX_SUPER;
} }
val = eval_method_missing(th, id, recv, num, blockptr, stat); val = eval_method_missing(th, id, recv, num, blockptr, stat);
} }
} }
else if (!(flag & VM_CALL_FCALL_BIT) && else if (!(flag & VM_CALL_FCALL_BIT) &&
(mn->nd_noex & NOEX_MASK) & NOEX_PRIVATE) { (mn->nd_noex & NOEX_MASK) & NOEX_PRIVATE) {
int stat = NOEX_PRIVATE; int stat = NOEX_PRIVATE;
if (flag & VM_CALL_VCALL_BIT) { if (flag & VM_CALL_VCALL_BIT) {
stat |= NOEX_VCALL; stat |= NOEX_VCALL;
} }
val = eval_method_missing(th, id, recv, num, blockptr, stat); val = eval_method_missing(th, id, recv, num, blockptr, stat);
} }
else if ((mn->nd_noex & NOEX_MASK) & NOEX_PROTECTED) { else if ((mn->nd_noex & NOEX_MASK) & NOEX_PROTECTED) {
VALUE defined_class = mn->nd_clss; VALUE defined_class = mn->nd_clss;
if (TYPE(defined_class) == T_ICLASS) { if (TYPE(defined_class) == T_ICLASS) {
defined_class = RBASIC(defined_class)->klass; defined_class = RBASIC(defined_class)->klass;
} }
if (!rb_obj_is_kind_of(GET_SELF(), rb_class_real(defined_class))) { if (!rb_obj_is_kind_of(GET_SELF(), rb_class_real(defined_class))) {
val = val =
eval_method_missing(th, id, recv, num, blockptr, eval_method_missing(th, id, recv, num, blockptr,
NOEX_PROTECTED); NOEX_PROTECTED);
} }
else { else {
goto INSN_LABEL(normal_method_dispatch); goto INSN_LABEL(normal_method_dispatch);
} }
} }
else { else {
NODE *node; NODE *node;
INSN_LABEL(normal_method_dispatch): INSN_LABEL(normal_method_dispatch):
node = mn->nd_body; node = mn->nd_body;
switch (nd_type(node)) { switch (nd_type(node)) {
case YARV_METHOD_NODE:{ case YARV_METHOD_NODE:{
macro_eval_invoke_func(node->nd_body, recv, klass, macro_eval_invoke_func(node->nd_body, recv, klass,
blockptr, num); blockptr, num);
NEXT_INSN(); NEXT_INSN();
} }
case NODE_CFUNC:{ case NODE_CFUNC:{
macro_eval_invoke_cfunc(num, id, recv, klass, node, blockptr); macro_eval_invoke_cfunc(num, id, recv, klass, node, blockptr);
break; break;
} }
case NODE_ATTRSET:{ case NODE_ATTRSET:{
val = rb_ivar_set(recv, node->nd_vid, TOPN(0)); val = rb_ivar_set(recv, node->nd_vid, TOPN(0));
POPN(2); POPN(2);
break; break;
} }
case NODE_IVAR:{ case NODE_IVAR:{
val = rb_ivar_get(recv, node->nd_vid); val = rb_ivar_get(recv, node->nd_vid);
POP(); POP();
break; break;
} }
case NODE_BMETHOD:{ case NODE_BMETHOD:{
VALUE *argv = GET_SP() - num; VALUE *argv = GET_SP() - num;
val = th_invoke_bmethod(th, id, node->nd_cval, val = th_invoke_bmethod(th, id, node->nd_cval,
recv, klass, num, argv); recv, klass, num, argv);
INC_SP(-num-1); INC_SP(-num-1);
break; break;
} }
case NODE_ZSUPER:{ case NODE_ZSUPER:{
klass = RCLASS(mn->nd_clss)->super; klass = RCLASS(mn->nd_clss)->super;
mn = rb_method_node(klass, id); mn = rb_method_node(klass, id);
if (mn != 0) { if (mn != 0) {
goto INSN_LABEL(normal_method_dispatch); goto INSN_LABEL(normal_method_dispatch);
} }
else { else {
goto LABEL_IS_SC(start_method_dispatch); goto LABEL_IS_SC(start_method_dispatch);
} }
} }
case NODE_SCOPE:{ case NODE_SCOPE:{
dpi(id); dpi(id);
SDR(); SDR();
rb_bug("eval_invoke_method: NODE_SCOPE should not be appear"); rb_bug("eval_invoke_method: NODE_SCOPE should not be appear");
/* unreachable */ /* unreachable */
break; break;
} }
default:{ default:{
printf("node: %s\n", node_name(nd_type(node))); printf("node: %s\n", node_name(nd_type(node)));
rb_bug("eval_invoke_method: unreachable"); rb_bug("eval_invoke_method: unreachable");
/* unreachable */ /* unreachable */
break; break;
} }
} }
} }
} }

View File

@ -1,47 +1,47 @@
/*-*-c-*-*/ /*-*-c-*-*/
/********************************************************************** /**********************************************************************
vm_opts.h.base - VM optimize option vm_opts.h.base - VM optimize option
$Author: $ $Author$
$Date: $ $Date$
Copyright (C) 2004-2006 Koichi Sasada Copyright (C) 2004-2006 Koichi Sasada
**********************************************************************/ **********************************************************************/
#ifndef VM_OPTS_H_INCLUDED #ifndef VM_OPTS_H_INCLUDED
#define VM_OPTS_H_INCLUDED #define VM_OPTS_H_INCLUDED
/* C compiler depend */ /* C compiler depend */
#define OPT_DIRECT_THREADED_CODE 1 #define OPT_DIRECT_THREADED_CODE 1
#define OPT_CALL_THREADED_CODE 0 #define OPT_CALL_THREADED_CODE 0
/* architecture independent */ /* architecture independent */
/* VM running option */ /* VM running option */
#define OPT_CHECKED_RUN 1 #define OPT_CHECKED_RUN 1
/* at compile */ /* at compile */
#define OPT_INLINE_CONST_CACHE 1 #define OPT_INLINE_CONST_CACHE 1
#define OPT_PEEPHOLE_OPTIMIZATION 1 #define OPT_PEEPHOLE_OPTIMIZATION 1
#define OPT_SPECIALISED_INSTRUCTION 1 #define OPT_SPECIALISED_INSTRUCTION 1
/* at runtime */ /* at runtime */
#define OPT_INLINE_METHOD_CACHE 1 #define OPT_INLINE_METHOD_CACHE 1
#define OPT_BLOCKINLINING 0 #define OPT_BLOCKINLINING 0
/* architecture independent, affects generated code */ /* architecture independent, affects generated code */
#define OPT_OPERANDS_UNIFICATION 0 #define OPT_OPERANDS_UNIFICATION 0
#define OPT_INSTRUCTIONS_UNIFICATION 0 #define OPT_INSTRUCTIONS_UNIFICATION 0
/* code generation parameter */ /* code generation parameter */
#define OPT_UNIFY_ALL_COMBINATION 0 #define OPT_UNIFY_ALL_COMBINATION 0
#define OPT_STACK_CACHING 0 #define OPT_STACK_CACHING 0
/* misc */ /* misc */
#define SUPPORT_JOKE 0 #define SUPPORT_JOKE 0
#endif /* VM_OPTS_H_INCLUDED */ #endif /* VM_OPTS_H_INCLUDED */