1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/insns.def

2174 lines
44 KiB
Modula-2
Raw Normal View History

/** ##skip -*- mode:c; style:ruby; coding: utf-8 -*-
insns.def - YARV instruction definitions
$Author: $
created at: 04/01/01 01:17:55 JST
Copyright (C) 2004-2007 Koichi Sasada
*/
/** ##skip
instruction comment
@c: category
@e: english description
@j: japanese description
instruction form:
DEFINE_INSN
instruction_name
(instruction_operands, ..)
(pop_values, ..)
(return value)
{
.. // insn body
}
*/
/**
@c nop
@e nop
@j nop
*/
DEFINE_INSN
nop
()
()
()
{
/* none */
}
/**********************************************************/
/* deal with variables */
/**********************************************************/
/**
@c variable
@e Get local variable (pointed by `idx' and `level').
'level' indicates the nesting depth from the current block.
@j level, idx
level
*/
DEFINE_INSN
getlocal
(lindex_t idx, rb_num_t level)
()
(VALUE val)
{
int i, lev = (int)level;
VALUE *ep = GET_EP();
for (i = 0; i < lev; i++) {
ep = GET_PREV_EP(ep);
}
val = *(ep - idx);
}
/**
@c variable
@e Set a local variable (pointed to by 'idx') as val.
'level' indicates the nesting depth from the current block.
@j level, idx val
level
*/
DEFINE_INSN
setlocal
(lindex_t idx, rb_num_t level)
(VALUE val)
()
{
int i, lev = (int)level;
VALUE *ep = GET_EP();
for (i = 0; i < lev; i++) {
ep = GET_PREV_EP(ep);
}
*(ep - idx) = val;
}
/**
@c variable
@e Get value of special local variable ($~, $_, ..).
@j $~, $_, ...
*/
DEFINE_INSN
getspecial
(rb_num_t key, rb_num_t type)
()
(VALUE val)
{
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-10 23:14:59 -04:00
val = vm_getspecial(th, GET_LEP(), key, type);
}
/**
@c variable
@e Set value of special local variable ($~, $_, ...) to obj.
@j $~, $_, ...
*/
DEFINE_INSN
setspecial
(rb_num_t key)
(VALUE obj)
()
{
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-10 23:14:59 -04:00
lep_svar_set(th, GET_LEP(), key, obj);
}
/**
@c variable
@e Get value of instance variable id of self.
If is_local is not 0, get value of class local variable.
@j self id
*/
DEFINE_INSN
getinstancevariable
(ID id, IC ic)
()
(VALUE val)
{
val = vm_getinstancevariable(GET_SELF(), id, ic);
}
/**
@c variable
@e Set value of instance variable id of self to val.
If is_local is not 0, set value of class local variable.
@j self id val
*/
DEFINE_INSN
setinstancevariable
(ID id, IC ic)
(VALUE val)
()
{
vm_setinstancevariable(GET_SELF(), id, val, ic);
}
/**
@c variable
@e Get value of class variable id of klass as val.
@j id
*/
DEFINE_INSN
getclassvariable
(ID id)
()
(VALUE val)
{
NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP());
val = rb_cvar_get(vm_get_cvar_base(cref, GET_CFP()), id);
}
/**
@c variable
@e Set value of class variable id of klass as val.
@j klass id val
*/
DEFINE_INSN
setclassvariable
(ID id)
(VALUE val)
()
{
NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP());
rb_cvar_set(vm_get_cvar_base(cref, GET_CFP()), id, val);
}
/**
@c variable
@e
Get constant variable id. If klass is Qnil, constants
are searched in the current scope. If klass is Qfalse, constants
are searched as top level constants. Otherwise, get constant under klass
class or module.
@j id
klass Qnil
Qfalse
klass
*/
DEFINE_INSN
getconstant
(ID id)
(VALUE klass)
(VALUE val)
{
val = vm_get_ev_const(th, GET_ISEQ(), klass, id, 0);
}
/**
@c variable
@e
Set constant variable id. If klass is Qfalse, constant
is able to access in this scope. if klass is Qnil, set
top level constant. otherwise, set constant under klass
class or module.
@j id val
klass Qfalse id
Qnil
klass
*/
DEFINE_INSN
setconstant
(ID id)
(VALUE val, VALUE cbase)
()
{
vm_check_if_namespace(cbase);
rb_const_set(cbase, id, val);
}
/**
@c variable
@e get global variable id.
@j id
*/
DEFINE_INSN
getglobal
(GENTRY entry)
()
(VALUE val)
{
val = GET_GLOBAL((VALUE)entry);
}
/**
@c variable
@e set global variable id as val.
@j id
*/
DEFINE_INSN
setglobal
(GENTRY entry)
(VALUE val)
()
{
SET_GLOBAL((VALUE)entry, val);
}
/**********************************************************/
/* deal with values */
/**********************************************************/
/**
@c put
@e put nil to stack.
@j nil
*/
DEFINE_INSN
putnil
()
()
(VALUE val)
{
val = Qnil;
}
/**
@c put
@e put self.
@j self
*/
DEFINE_INSN
putself
()
()
(VALUE val)
{
val = GET_SELF();
}
/**
@c put
@e put some object.
i.e. Fixnum, true, false, nil, and so on.
@j val
i.e. Fixnum, true, false, nil, and so on.
*/
DEFINE_INSN
putobject
(VALUE val)
()
(VALUE val)
{
/* */
}
/**
@c put
@e put special object. "value_type" is for expansion.
@j val
value_type
*/
DEFINE_INSN
putspecialobject
(rb_num_t value_type)
()
(VALUE val)
{
enum vm_special_object_type type = (enum vm_special_object_type)value_type;
switch (type) {
case VM_SPECIAL_OBJECT_VMCORE:
val = rb_mRubyVMFrozenCore;
break;
case VM_SPECIAL_OBJECT_CBASE:
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-10 23:14:59 -04:00
val = vm_get_cbase(GET_ISEQ(), GET_EP());
break;
case VM_SPECIAL_OBJECT_CONST_BASE:
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-10 23:14:59 -04:00
val = vm_get_const_base(GET_ISEQ(), GET_EP());
break;
default:
rb_bug("putspecialobject insn: unknown value_type");
}
}
/**
@c put
@e put iseq value.
@j put iseq value.
*/
DEFINE_INSN
putiseq
(ISEQ iseq)
()
(VALUE ret)
{
ret = iseq->self;
}
/**
@c put
@e put string val. string will be copied.
@j
*/
DEFINE_INSN
putstring
(VALUE str)
()
(VALUE val)
{
val = rb_str_resurrect(str);
}
/**
@c put
@e put concatenate strings
@j n
*/
DEFINE_INSN
concatstrings
(rb_num_t num)
(...)
(VALUE val) // inc += 1 - num;
{
rb_num_t i = num - 1;
val = rb_str_resurrect(TOPN(i));
while (i-- > 0) {
const VALUE v = TOPN(i);
rb_str_append(val, v);
}
POPN(num);
}
/**
@c put
@e to_str
@j to_str
*/
DEFINE_INSN
tostring
()
(VALUE val)
(VALUE val)
{
val = rb_obj_as_string(val);
}
/**
@c put
@e to Regexp
@j str
opt
*/
DEFINE_INSN
toregexp
(rb_num_t opt, rb_num_t cnt)
(...)
(VALUE val) // inc += 1 - cnt;
{
VALUE rb_reg_new_ary(VALUE ary, int options);
rb_num_t i;
const VALUE ary = rb_ary_tmp_new(cnt);
for (i = 0; i < cnt; i++) {
rb_ary_store(ary, cnt-i-1, TOPN(i));
}
POPN(cnt);
val = rb_reg_new_ary(ary, (int)opt);
rb_ary_clear(ary);
}
/**
@c put
@e put new array.
@j num
*/
DEFINE_INSN
newarray
(rb_num_t num)
(...)
(VALUE val) // inc += 1 - num;
{
val = rb_ary_new4((long)num, STACK_ADDR_FROM_TOP(num));
POPN(num);
}
/**
@c put
@e dup array
@j ary dup
*/
DEFINE_INSN
duparray
(VALUE ary)
()
(VALUE val)
{
val = rb_ary_resurrect(ary);
}
/**
@c put
@e expand array to num objects.
@j
num以下ならば nil num以上なら
num以上の要素は切り捨てる
num - 1 nil
flag
flag: 0x01 -
flag: 0x02 - postarg
flag: 0x04 - reverse?
*/
DEFINE_INSN
expandarray
(rb_num_t num, rb_num_t flag)
(..., VALUE ary)
(...) // inc += num - 1 + (flag & 1 ? 1 : 0);
{
vm_expandarray(GET_CFP(), ary, num, (int)flag);
}
/**
@c put
@e concat two arrays
@j ary1, ary2
*/
DEFINE_INSN
concatarray
()
(VALUE ary1, VALUE ary2st)
(VALUE ary)
{
const VALUE ary2 = ary2st;
VALUE tmp1 = rb_check_convert_type(ary1, T_ARRAY, "Array", "to_a");
VALUE tmp2 = rb_check_convert_type(ary2, T_ARRAY, "Array", "to_a");
if (NIL_P(tmp1)) {
tmp1 = rb_ary_new3(1, ary1);
}
if (NIL_P(tmp2)) {
tmp2 = rb_ary_new3(1, ary2);
}
if (tmp1 == ary1) {
tmp1 = rb_ary_dup(ary1);
}
ary = rb_ary_concat(tmp1, tmp2);
}
/**
@c put
@e splat array
@j ary to_a
*/
DEFINE_INSN
splatarray
(VALUE flag)
(VALUE ary)
(VALUE obj)
{
VALUE tmp = rb_check_convert_type(ary, T_ARRAY, "Array", "to_a");
if (NIL_P(tmp)) {
tmp = rb_ary_new3(1, ary);
}
else if (RTEST(flag)) {
tmp = rb_ary_dup(tmp);
}
obj = tmp;
}
/**
@c put
@e put new Hash.
@j n
n 2
*/
DEFINE_INSN
newhash
(rb_num_t num)
(...)
(VALUE val) // inc += 1 - num;
{
rb_num_t i;
* probes.d: add DTrace probe declarations. [ruby-core:27448] * array.c (empty_ary_alloc, ary_new): added array create DTrace probe. * compile.c (rb_insns_name): allowing DTrace probes to access instruction sequence name. * Makefile.in: translate probes.d file to appropriate header file. * common.mk: declare dependencies on the DTrace header. * configure.in: add a test for existence of DTrace. * eval.c (setup_exception): add a probe for when an exception is raised. * gc.c: Add DTrace probes for mark begin and end, and sweep begin and end. * hash.c (empty_hash_alloc): Add a probe for hash allocation. * insns.def: Add probes for function entry and return. * internal.h: function declaration for compile.c change. * load.c (rb_f_load): add probes for `load` entry and exit, require entry and exit, and wrapping search_required for load path search. * object.c (rb_obj_alloc): added a probe for general object creation. * parse.y (yycompile0): added a probe around parse and compile phase. * string.c (empty_str_alloc, str_new): DTrace probes for string allocation. * test/dtrace/*: tests for DTrace probes. * vm.c (vm_invoke_proc): add probes for function return on exception raise, hash create, and instruction sequence execution. * vm_core.h: add probe declarations for function entry and exit. * vm_dump.c: add probes header file. * vm_eval.c (vm_call0_cfunc, vm_call0_cfunc_with_frame): add probe on function entry and return. * vm_exec.c: expose instruction number to instruction name function. * vm_insnshelper.c: add function entry and exit probes for cfunc methods. * vm_insnhelper.h: vm usage information is always collected, so uncomment the functions. 12 19:14:50 2012 Akinori MUSHA <knu@iDaemons.org> * configure.in (isinf, isnan): isinf() and isnan() are macros on DragonFly which cannot be found by AC_REPLACE_FUNCS(). This workaround enforces the fact that they exist on DragonFly. 12 15:59:38 2012 Shugo Maeda <shugo@ruby-lang.org> * vm_core.h (rb_call_info_t::refinements), compile.c (new_callinfo), vm_insnhelper.c (vm_search_method): revert r37616 because it's too slow. [ruby-dev:46477] * test/ruby/test_refinement.rb (test_inline_method_cache): skip the test until the bug is fixed efficiently. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37631 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-11-12 16:52:12 -05:00
if(RUBY_DTRACE_HASH_CREATE_ENABLED()) {
RUBY_DTRACE_HASH_CREATE(num, rb_sourcefile(), rb_sourceline());
}
val = rb_hash_new();
for (i = num; i > 0; i -= 2) {
const VALUE v = TOPN(i - 2);
const VALUE k = TOPN(i - 1);
rb_hash_aset(val, k, v);
}
POPN(num);
}
/**
@c put
@e put new Range object.(Range.new(low, high, flag))
@j Range.new(low, high, flag)
*/
DEFINE_INSN
newrange
(rb_num_t flag)
(VALUE low, VALUE high)
(VALUE val)
{
val = rb_range_new(low, high, (int)flag);
}
/**********************************************************/
/* deal with stack operation */
/**********************************************************/
/**
@c stack
@e pop from stack.
@j
*/
DEFINE_INSN
pop
()
(VALUE val)
()
{
(void)val;
/* none */
}
/**
@c stack
@e duplicate stack top.
@j
*/
DEFINE_INSN
dup
()
(VALUE val)
(VALUE val1, VALUE val2)
{
val1 = val2 = val;
}
/**
@c stack
@e duplicate stack top n elements
@j n
*/
DEFINE_INSN
dupn
(rb_num_t n)
(...)
(...) // inc += n;
{
rb_num_t i;
VALUE *sp = STACK_ADDR_FROM_TOP(n);
for (i = 0; i < n; i++) {
GET_SP()[i] = sp[i];
}
INC_SP(n);
}
/**
@c stack
@e swap top 2 vals
@j 2
*/
DEFINE_INSN
swap
()
(VALUE val, VALUE obj)
(VALUE obj, VALUE val)
{
/* none */
}
/**
@c stack
@e for stack caching.
@j 調
*/
DEFINE_INSN
reput
()
(..., VALUE val)
(VALUE val) // inc += 0;
{
/* none */
}
/**
@c stack
@e get nth stack value from stack top
@j n
*/
DEFINE_INSN
topn
(rb_num_t n)
(...)
(VALUE val) // inc += 1;
{
val = TOPN(n);
}
/**
@c stack
@e set Nth stack entry to stack top
@j n
*/
DEFINE_INSN
setn
(rb_num_t n)
(..., VALUE val)
(VALUE val) // inc += 0
{
TOPN(n-1) = val;
}
/**
@c stack
@e empt current stack
@j current stack
*/
DEFINE_INSN
adjuststack
(rb_num_t n)
(...)
(...) // inc -= n
{
DEC_SP(n);
}
/**********************************************************/
/* deal with setting */
/**********************************************************/
/**
@c setting
@e defined?
@j defined?
*/
DEFINE_INSN
defined
(rb_num_t op_type, VALUE obj, VALUE needstr)
(VALUE v)
(VALUE val)
{
VALUE klass;
enum defined_type expr_type = 0;
enum defined_type type = (enum defined_type)op_type;
val = Qnil;
switch (type) {
case DEFINED_IVAR:
if (rb_ivar_defined(GET_SELF(), SYM2ID(obj))) {
expr_type = DEFINED_IVAR;
}
break;
case DEFINED_IVAR2:
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-10 23:14:59 -04:00
klass = vm_get_cbase(GET_ISEQ(), GET_EP());
break;
case DEFINED_GVAR:
if (rb_gvar_defined(rb_global_entry(SYM2ID(obj)))) {
expr_type = DEFINED_GVAR;
}
break;
case DEFINED_CVAR: {
NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP());
klass = vm_get_cvar_base(cref, GET_CFP());
if (rb_cvar_defined(klass, SYM2ID(obj))) {
expr_type = DEFINED_CVAR;
}
break;
}
case DEFINED_CONST:
klass = v;
if (vm_get_ev_const(th, GET_ISEQ(), klass, SYM2ID(obj), 1)) {
expr_type = DEFINED_CONST;
}
break;
case DEFINED_FUNC:
klass = CLASS_OF(v);
if (rb_method_boundp(klass, SYM2ID(obj), 0)) {
expr_type = DEFINED_METHOD;
}
break;
case DEFINED_METHOD:{
VALUE klass = CLASS_OF(v);
const rb_method_entry_t *me = rb_method_entry(klass, SYM2ID(obj), 0);
if (me) {
if (!(me->flag & NOEX_PRIVATE)) {
if (!((me->flag & NOEX_PROTECTED) &&
!rb_obj_is_kind_of(GET_SELF(),
rb_class_real(klass)))) {
expr_type = DEFINED_METHOD;
}
}
}
{
VALUE args[2];
VALUE r;
args[0] = obj; args[1] = Qfalse;
r = rb_check_funcall(v, idRespond_to_missing, 2, args);
if (r != Qundef && RTEST(r))
expr_type = DEFINED_METHOD;
}
break;
}
case DEFINED_YIELD:
if (GET_BLOCK_PTR()) {
expr_type = DEFINED_YIELD;
}
break;
case DEFINED_ZSUPER:{
rb_call_info_t cit;
if (vm_search_superclass(GET_CFP(), GET_ISEQ(), Qnil, &cit) == 0) {
VALUE klass = cit.klass;
ID id = cit.mid;
if (rb_method_boundp(klass, id, 0)) {
expr_type = DEFINED_ZSUPER;
}
}
break;
}
case DEFINED_REF:{
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-10 23:14:59 -04:00
val = vm_getspecial(th, GET_LEP(), Qfalse, FIX2INT(obj));
if (val != Qnil) {
expr_type = DEFINED_GVAR;
}
break;
}
default:
rb_bug("unimplemented defined? type (VM)");
break;
}
if (expr_type != 0) {
if (needstr != Qfalse) {
val = rb_iseq_defined_string(expr_type);
}
else {
val = Qtrue;
}
}
}
/**
@c setting
@e check `target' matches `pattern'.
`flag & VM_CHECKMATCH_TYPE_MASK' describe how to check pattern.
VM_CHECKMATCH_TYPE_WHEN: ignore target and check pattern is truthy.
VM_CHECKMATCH_TYPE_CASE: check `patten === target'.
VM_CHECKMATCH_TYPE_RESCUE: check `pattern.kind_op?(Module) && pattern == target'.
if `flag & VM_CHECKMATCH_ARRAY' is not 0, then `patten' is array of patterns.
@j see above comments.
*/
DEFINE_INSN
checkmatch
(rb_num_t flag)
(VALUE target, VALUE pattern)
(VALUE result)
{
enum vm_check_match_type checkmatch_type =
(enum vm_check_match_type)(flag & VM_CHECKMATCH_TYPE_MASK);
result = Qfalse;
if (flag & VM_CHECKMATCH_ARRAY) {
int i;
for (i = 0; i < RARRAY_LEN(pattern); i++) {
if (RTEST(check_match(RARRAY_AREF(pattern, i), target, checkmatch_type))) {
result = Qtrue;
break;
}
}
}
else {
if (RTEST(check_match(pattern, target, checkmatch_type))) {
result = Qtrue;
}
}
}
/**
@c setting
@e trace
@j trace
*/
DEFINE_INSN
trace
(rb_num_t nf)
()
()
{
rb_event_flag_t flag = (rb_event_flag_t)nf;
if (RUBY_DTRACE_METHOD_ENTRY_ENABLED() ||
RUBY_DTRACE_METHOD_RETURN_ENABLED() ||
RUBY_DTRACE_CMETHOD_ENTRY_ENABLED() ||
RUBY_DTRACE_CMETHOD_RETURN_ENABLED()) {
switch(flag) {
case RUBY_EVENT_CALL:
RUBY_DTRACE_METHOD_ENTRY_HOOK(th, 0, 0);
break;
case RUBY_EVENT_C_CALL:
RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, 0, 0);
break;
case RUBY_EVENT_RETURN:
RUBY_DTRACE_METHOD_RETURN_HOOK(th, 0, 0);
break;
case RUBY_EVENT_C_RETURN:
RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, 0, 0);
break;
* probes.d: add DTrace probe declarations. [ruby-core:27448] * array.c (empty_ary_alloc, ary_new): added array create DTrace probe. * compile.c (rb_insns_name): allowing DTrace probes to access instruction sequence name. * Makefile.in: translate probes.d file to appropriate header file. * common.mk: declare dependencies on the DTrace header. * configure.in: add a test for existence of DTrace. * eval.c (setup_exception): add a probe for when an exception is raised. * gc.c: Add DTrace probes for mark begin and end, and sweep begin and end. * hash.c (empty_hash_alloc): Add a probe for hash allocation. * insns.def: Add probes for function entry and return. * internal.h: function declaration for compile.c change. * load.c (rb_f_load): add probes for `load` entry and exit, require entry and exit, and wrapping search_required for load path search. * object.c (rb_obj_alloc): added a probe for general object creation. * parse.y (yycompile0): added a probe around parse and compile phase. * string.c (empty_str_alloc, str_new): DTrace probes for string allocation. * test/dtrace/*: tests for DTrace probes. * vm.c (vm_invoke_proc): add probes for function return on exception raise, hash create, and instruction sequence execution. * vm_core.h: add probe declarations for function entry and exit. * vm_dump.c: add probes header file. * vm_eval.c (vm_call0_cfunc, vm_call0_cfunc_with_frame): add probe on function entry and return. * vm_exec.c: expose instruction number to instruction name function. * vm_insnshelper.c: add function entry and exit probes for cfunc methods. * vm_insnhelper.h: vm usage information is always collected, so uncomment the functions. 12 19:14:50 2012 Akinori MUSHA <knu@iDaemons.org> * configure.in (isinf, isnan): isinf() and isnan() are macros on DragonFly which cannot be found by AC_REPLACE_FUNCS(). This workaround enforces the fact that they exist on DragonFly. 12 15:59:38 2012 Shugo Maeda <shugo@ruby-lang.org> * vm_core.h (rb_call_info_t::refinements), compile.c (new_callinfo), vm_insnhelper.c (vm_search_method): revert r37616 because it's too slow. [ruby-dev:46477] * test/ruby/test_refinement.rb (test_inline_method_cache): skip the test until the bug is fixed efficiently. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37631 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-11-12 16:52:12 -05:00
}
}
EXEC_EVENT_HOOK(th, flag, GET_SELF(), 0, 0 /* id and klass are resolved at callee */,
(flag & (RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN)) ? TOPN(0) : Qundef);
}
/**********************************************************/
/* deal with control flow 1: class/module */
/**********************************************************/
/**
@c class/module
@e
enter class definition scope. if super is Qfalse, and clsas
"klass" is defined, it's redefine. otherwise, define "klass" class.
@j
super Qfalse klassクラスが定義されていれば再定義である
klass
*/
DEFINE_INSN
defineclass
(ID id, ISEQ class_iseq, rb_num_t flags)
(VALUE cbase, VALUE super)
(VALUE val)
{
VALUE klass;
rb_vm_defineclass_type_t type = VM_DEFINECLASS_TYPE(flags);
switch (type) {
case VM_DEFINECLASS_TYPE_CLASS:
/* val is dummy. classdef returns class scope value */
if (VM_DEFINECLASS_HAS_SUPERCLASS_P(flags) &&
!RB_TYPE_P(super, T_CLASS)) {
rb_raise(rb_eTypeError, "superclass must be a Class (%s given)",
rb_obj_classname(super));
}
if (super == Qnil) {
super = rb_cObject;
}
vm_check_if_namespace(cbase);
/* find klass */
rb_autoload_load(cbase, id);
if ((klass = vm_search_const_defined_class(cbase, id)) != 0) {
/* already exist */
klass = VM_DEFINECLASS_SCOPED_P(flags) ?
rb_public_const_get_at(klass, id) : rb_const_get_at(klass, id);
if (!RB_TYPE_P(klass, T_CLASS)) {
rb_raise(rb_eTypeError, "%s is not a class", rb_id2name(id));
}
if (super != rb_cObject) {
VALUE tmp;
tmp = rb_class_real(RCLASS_SUPER(klass));
if (tmp != super) {
rb_raise(rb_eTypeError, "superclass mismatch for class %s",
rb_id2name(id));
}
}
}
else {
/* new class declaration */
klass = rb_define_class_id(id, super);
rb_set_class_path_string(klass, cbase, rb_id2str(id));
rb_const_set(cbase, id, klass);
rb_class_inherited(super, klass);
}
break;
case VM_DEFINECLASS_TYPE_SINGLETON_CLASS:
/* val is dummy. classdef returns class scope value */
/* super is dummy */
klass = rb_singleton_class(cbase);
break;
case VM_DEFINECLASS_TYPE_MODULE:
/* val is dummy. classdef returns class scope value */
/* super is dummy */
vm_check_if_namespace(cbase);
/* find klass */
if ((klass = vm_search_const_defined_class(cbase, id)) != 0) {
klass = VM_DEFINECLASS_SCOPED_P(flags) ?
rb_public_const_get_at(klass, id) : rb_const_get_at(klass, id);
/* already exist */
if (!RB_TYPE_P(klass, T_MODULE)) {
rb_raise(rb_eTypeError, "%s is not a module", rb_id2name(id));
}
}
else {
/* new module declaration */
klass = rb_define_module_id(id);
rb_set_class_path_string(klass, cbase, rb_id2str(id));
rb_const_set(cbase, id, klass);
}
break;
default:
rb_bug("unknown defineclass type: %d", (int)type);
}
COPY_CREF(class_iseq->cref_stack, vm_cref_push(th, klass, NOEX_PUBLIC, NULL));
/* enter scope */
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-10 23:14:59 -04:00
vm_push_frame(th, class_iseq, VM_FRAME_MAGIC_CLASS,
klass, 0, VM_ENVVAL_BLOCK_PTR(GET_BLOCK_PTR()),
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-10 23:14:59 -04:00
class_iseq->iseq_encoded, GET_SP(),
class_iseq->local_size, 0, class_iseq->stack_max);
RESTORE_REGS();
NEXT_INSN();
}
/**********************************************************/
/* deal with control flow 2: method/iterator */
/**********************************************************/
/**
@c method/iterator
@e invoke method.
@j ci
*/
DEFINE_INSN
send
(CALL_INFO ci)
(...)
(VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0));
{
ci->argc = ci->orig_argc;
ci->blockptr = 0;
vm_caller_setup_args(th, reg_cfp, ci);
vm_search_method(ci, ci->recv = TOPN(ci->argc));
CALL_METHOD(ci);
}
DEFINE_INSN
opt_str_freeze
(VALUE str)
()
(VALUE val)
{
if (BASIC_OP_UNREDEFINED_P(BOP_FREEZE, STRING_REDEFINED_OP_FLAG)) {
val = str;
}
else {
val = rb_funcall(rb_str_resurrect(str), idFreeze, 0);
}
}
/**
@c optimize
@e Invoke method without block, splat
@j Invoke method without block, splat
*/
DEFINE_INSN
opt_send_simple
(CALL_INFO ci)
(...)
(VALUE val) // inc += -ci->orig_argc;
{
vm_search_method(ci, ci->recv = TOPN(ci->argc));
CALL_METHOD(ci);
}
/**
@c method/iterator
@e super(args) # args.size => num
@j super ci
*/
DEFINE_INSN
invokesuper
(CALL_INFO ci)
(...)
(VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0));
{
ci->argc = ci->orig_argc;
ci->blockptr = !(ci->flag & VM_CALL_ARGS_BLOCKARG) ? GET_BLOCK_PTR() : 0;
if (UNLIKELY(!(ci->flag & VM_CALL_ARGS_SKIP_SETUP))) {
vm_caller_setup_args(th, reg_cfp, ci);
}
ci->recv = GET_SELF();
vm_search_super_method(th, GET_CFP(), ci);
CALL_METHOD(ci);
}
/**
@c method/iterator
@e yield(args)
@j yield
*/
DEFINE_INSN
invokeblock
(CALL_INFO ci)
(...)
(VALUE val) // inc += 1 - ci->orig_argc;
{
ci->argc = ci->orig_argc;
ci->blockptr = 0;
ci->recv = GET_SELF();
val = vm_invoke_block(th, GET_CFP(), ci);
if (val == Qundef) {
RESTORE_REGS();
NEXT_INSN();
}
}
/**
@c method/iterator
@e return from this scope.
@j
*/
DEFINE_INSN
leave
()
(VALUE val)
(VALUE val)
{
if (OPT_CHECKED_RUN) {
if (reg_cfp->sp != vm_base_ptr(reg_cfp)) {
rb_bug("Stack consistency error (sp: %"PRIdPTRDIFF", bp: %"PRIdPTRDIFF")",
VM_SP_CNT(th, reg_cfp->sp), VM_SP_CNT(th, vm_base_ptr(reg_cfp)));
}
}
RUBY_VM_CHECK_INTS(th);
* vm_core.h: remove VM_FRAME_MAGIC_FINISH (finish frame type). Before this commit: `finish frame' was place holder which indicates that VM loop needs to return function. If a C method calls a Ruby methods (a method written by Ruby), then VM loop will be (re-)invoked. When the Ruby method returns, then also VM loop should be escaped. `finish frame' has only one instruction `finish', which returns VM loop function. VM loop function executes `finish' instruction, then VM loop function returns itself. With such mechanism, `leave' instruction (which returns one frame from current scope) doesn't need to check that this `leave' should also return from VM loop function. Strictly, one branch can be removed from `leave' instructon. Consideration: However, pushing the `finish frame' needs costs because it needs several memory accesses. The number of pushing `finish frame' is greater than I had assumed. Of course, pushing `finish frame' consumes additional control frame. Moreover, recent processors has good branch prediction, with which we can ignore such trivial checking. After this commit: Finally, I decide to remove `finish frame' and `finish' instruction. Some parts of VM depend on `finish frame', so the new frame flag VM_FRAME_FLAG_FINISH is introduced. If this frame should escape from VM function loop, then the result of VM_FRAME_TYPE_FINISH_P(cfp) is true. `leave' instruction checks this flag every time. I measured performance on it. However on my environments, it improves some benchmarks and slows some benchmarks down. Maybe it is because of C compiler optimization parameters. I'll re-visit here if this cause problems. * insns.def (leave, finish): remove finish instruction. * vm.c, vm_eval.c, vm_exec.c, vm_backtrace.c, vm_dump.c: apply above changes. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36099 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-15 06:22:34 -04:00
if (UNLIKELY(VM_FRAME_TYPE_FINISH_P(GET_CFP()))) {
vm_pop_frame(th);
#if OPT_CALL_THREADED_CODE
th->retval = val;
return 0;
#else
* vm_core.h: remove VM_FRAME_MAGIC_FINISH (finish frame type). Before this commit: `finish frame' was place holder which indicates that VM loop needs to return function. If a C method calls a Ruby methods (a method written by Ruby), then VM loop will be (re-)invoked. When the Ruby method returns, then also VM loop should be escaped. `finish frame' has only one instruction `finish', which returns VM loop function. VM loop function executes `finish' instruction, then VM loop function returns itself. With such mechanism, `leave' instruction (which returns one frame from current scope) doesn't need to check that this `leave' should also return from VM loop function. Strictly, one branch can be removed from `leave' instructon. Consideration: However, pushing the `finish frame' needs costs because it needs several memory accesses. The number of pushing `finish frame' is greater than I had assumed. Of course, pushing `finish frame' consumes additional control frame. Moreover, recent processors has good branch prediction, with which we can ignore such trivial checking. After this commit: Finally, I decide to remove `finish frame' and `finish' instruction. Some parts of VM depend on `finish frame', so the new frame flag VM_FRAME_FLAG_FINISH is introduced. If this frame should escape from VM function loop, then the result of VM_FRAME_TYPE_FINISH_P(cfp) is true. `leave' instruction checks this flag every time. I measured performance on it. However on my environments, it improves some benchmarks and slows some benchmarks down. Maybe it is because of C compiler optimization parameters. I'll re-visit here if this cause problems. * insns.def (leave, finish): remove finish instruction. * vm.c, vm_eval.c, vm_exec.c, vm_backtrace.c, vm_dump.c: apply above changes. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36099 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-15 06:22:34 -04:00
return val;
#endif
* vm_core.h: remove VM_FRAME_MAGIC_FINISH (finish frame type). Before this commit: `finish frame' was place holder which indicates that VM loop needs to return function. If a C method calls a Ruby methods (a method written by Ruby), then VM loop will be (re-)invoked. When the Ruby method returns, then also VM loop should be escaped. `finish frame' has only one instruction `finish', which returns VM loop function. VM loop function executes `finish' instruction, then VM loop function returns itself. With such mechanism, `leave' instruction (which returns one frame from current scope) doesn't need to check that this `leave' should also return from VM loop function. Strictly, one branch can be removed from `leave' instructon. Consideration: However, pushing the `finish frame' needs costs because it needs several memory accesses. The number of pushing `finish frame' is greater than I had assumed. Of course, pushing `finish frame' consumes additional control frame. Moreover, recent processors has good branch prediction, with which we can ignore such trivial checking. After this commit: Finally, I decide to remove `finish frame' and `finish' instruction. Some parts of VM depend on `finish frame', so the new frame flag VM_FRAME_FLAG_FINISH is introduced. If this frame should escape from VM function loop, then the result of VM_FRAME_TYPE_FINISH_P(cfp) is true. `leave' instruction checks this flag every time. I measured performance on it. However on my environments, it improves some benchmarks and slows some benchmarks down. Maybe it is because of C compiler optimization parameters. I'll re-visit here if this cause problems. * insns.def (leave, finish): remove finish instruction. * vm.c, vm_eval.c, vm_exec.c, vm_backtrace.c, vm_dump.c: apply above changes. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36099 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-15 06:22:34 -04:00
}
else {
vm_pop_frame(th);
RESTORE_REGS();
}
}
/**********************************************************/
/* deal with control flow 3: exception */
/**********************************************************/
/**
@c exception
@e longjump
@j
*/
DEFINE_INSN
throw
(rb_num_t throw_state)
(VALUE throwobj)
(VALUE val)
{
RUBY_VM_CHECK_INTS(th);
val = vm_throw(th, GET_CFP(), throw_state, throwobj);
THROW_EXCEPTION(val);
/* unreachable */
}
/**********************************************************/
/* deal with control flow 4: local jump */
/**********************************************************/
/**
@c jump
@e set PC to (PC + dst).
@j PC (PC + dst)
*/
DEFINE_INSN
jump
(OFFSET dst)
()
()
{
RUBY_VM_CHECK_INTS(th);
JUMP(dst);
}
/**
@c jump
@e if val is not false or nil, set PC to (PC + dst).
@j val false nil PC (PC + dst)
*/
DEFINE_INSN
branchif
(OFFSET dst)
(VALUE val)
()
{
if (RTEST(val)) {
RUBY_VM_CHECK_INTS(th);
JUMP(dst);
}
}
/**
@c jump
@e if val is false or nil, set PC to (PC + dst).
@j val false nil PC (PC + dst)
*/
DEFINE_INSN
branchunless
(OFFSET dst)
(VALUE val)
()
{
if (!RTEST(val)) {
RUBY_VM_CHECK_INTS(th);
JUMP(dst);
}
}
/**********************************************************/
/* for optimize */
/**********************************************************/
/**
@c optimize
@e push inline-cached value and go to dst if it is valid
@j dst
*/
DEFINE_INSN
getinlinecache
(OFFSET dst, IC ic)
()
(VALUE val)
{
if (ic->ic_serial == GET_CONSTANT_SERIAL()) {
val = ic->ic_value.value;
JUMP(dst);
}
else {
/* none */
val = Qnil;
}
}
/**
@c optimize
@e set inline cache
@j
*/
DEFINE_INSN
setinlinecache
(IC ic)
(VALUE val)
(VALUE val)
{
if (ic->ic_value.value == Qundef) {
rb_iseq_add_mark_object(GET_ISEQ(), val);
}
ic->ic_value.value = val;
ic->ic_serial = GET_CONSTANT_SERIAL() - ruby_vm_const_missing_count;
ruby_vm_const_missing_count = 0;
}
/**
@c optimize
@e run iseq only once
@j once
*/
DEFINE_INSN
once
(ISEQ iseq, IC ic)
()
(VALUE val)
{
union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)ic;
retry:
if (is->once.done == Qfalse) {
if (is->once.running_thread == NULL) {
is->once.running_thread = th;
val = is->once.value = rb_ensure(vm_once_exec, (VALUE)iseq, vm_once_clear, (VALUE)is);
/* is->once.running_thread is cleared by vm_once_clear() */
is->once.done = Qtrue;
rb_iseq_add_mark_object(GET_ISEQ(), val);
}
else if (is->once.running_thread == th) {
/* recursive once */
val = vm_once_exec(iseq);
}
else {
/* waiting for finish */
RUBY_VM_CHECK_INTS(th);
rb_thread_schedule();
goto retry;
}
}
else {
val = is->once.value;
}
}
/**
@c optimize
@e case dispatcher, jump by table if possible
@j case
*/
DEFINE_INSN
opt_case_dispatch
(CDHASH hash, OFFSET else_offset)
(..., VALUE key)
() // inc += -1;
{
switch(TYPE(key)) {
case T_FLOAT: {
double ival;
if (modf(RFLOAT_VALUE(key), &ival) == 0.0) {
key = FIXABLE(ival) ? LONG2FIX((long)ival) : rb_dbl2big(ival);
}
}
case T_SYMBOL: /* fall through */
case T_FIXNUM:
case T_BIGNUM:
case T_STRING:
if (BASIC_OP_UNREDEFINED_P(BOP_EQQ,
SYMBOL_REDEFINED_OP_FLAG |
FIXNUM_REDEFINED_OP_FLAG |
BIGNUM_REDEFINED_OP_FLAG |
STRING_REDEFINED_OP_FLAG)) {
st_data_t val;
if (st_lookup(RHASH_TBL_RAW(hash), key, &val)) {
JUMP(FIX2INT((VALUE)val));
}
else {
JUMP(else_offset);
}
break;
}
default:
break;
}
}
/** simple functions */
/**
@c optimize
@e optimized X+Y.
@j X+Y
*/
DEFINE_INSN
opt_plus
(CALL_INFO ci)
(VALUE recv, VALUE obj)
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS,FIXNUM_REDEFINED_OP_FLAG)) {
/* fixnum + fixnum */
#ifndef LONG_LONG_VALUE
val = (recv + (obj & (~1)));
if ((~(recv ^ obj) & (recv ^ val)) &
((VALUE)0x01 << ((sizeof(VALUE) * CHAR_BIT) - 1))) {
val = rb_big_plus(rb_int2big(FIX2LONG(recv)),
rb_int2big(FIX2LONG(obj)));
}
#else
long a, b, c;
a = FIX2LONG(recv);
b = FIX2LONG(obj);
c = a + b;
if (FIXABLE(c)) {
val = LONG2FIX(c);
}
else {
val = rb_big_plus(rb_int2big(a), rb_int2big(b));
}
#endif
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS, FLOAT_REDEFINED_OP_FLAG)) {
val = DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj));
}
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS, FLOAT_REDEFINED_OP_FLAG)) {
val = DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj));
}
else if (RBASIC_CLASS(recv) == rb_cString && RBASIC_CLASS(obj) == rb_cString &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS, STRING_REDEFINED_OP_FLAG)) {
val = rb_str_plus(recv, obj);
}
else if (RBASIC_CLASS(recv) == rb_cArray &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS, ARRAY_REDEFINED_OP_FLAG)) {
val = rb_ary_plus(recv, obj);
}
else {
goto INSN_LABEL(normal_dispatch);
}
}
else {
INSN_LABEL(normal_dispatch):
PUSH(recv);
PUSH(obj);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e optimized X-Y.
@j X-Y
*/
DEFINE_INSN
opt_minus
(CALL_INFO ci)
(VALUE recv, VALUE obj)
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_MINUS, FIXNUM_REDEFINED_OP_FLAG)) {
long a, b, c;
a = FIX2LONG(recv);
b = FIX2LONG(obj);
c = a - b;
if (FIXABLE(c)) {
val = LONG2FIX(c);
}
else {
val = rb_big_minus(rb_int2big(a), rb_int2big(b));
}
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_MINUS, FLOAT_REDEFINED_OP_FLAG)) {
val = DBL2NUM(RFLOAT_VALUE(recv) - RFLOAT_VALUE(obj));
}
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
BASIC_OP_UNREDEFINED_P(BOP_MINUS, FLOAT_REDEFINED_OP_FLAG)) {
val = DBL2NUM(RFLOAT_VALUE(recv) - RFLOAT_VALUE(obj));
}
else {
goto INSN_LABEL(normal_dispatch);
}
}
else {
/* other */
INSN_LABEL(normal_dispatch):
PUSH(recv);
PUSH(obj);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e optimized X*Y.
@j X*Y
*/
DEFINE_INSN
opt_mult
(CALL_INFO ci)
(VALUE recv, VALUE obj)
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_MULT, FIXNUM_REDEFINED_OP_FLAG)) {
long a, b;
a = FIX2LONG(recv);
if (a == 0) {
val = recv;
}
else {
b = FIX2LONG(obj);
if (MUL_OVERFLOW_FIXNUM_P(a, b)) {
val = rb_big_mul(rb_int2big(a), rb_int2big(b));
}
else {
val = LONG2FIX(a * b);
}
}
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) {
val = DBL2NUM(RFLOAT_VALUE(recv) * RFLOAT_VALUE(obj));
}
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) {
val = DBL2NUM(RFLOAT_VALUE(recv) * RFLOAT_VALUE(obj));
}
else {
goto INSN_LABEL(normal_dispatch);
}
}
else {
INSN_LABEL(normal_dispatch):
PUSH(recv);
PUSH(obj);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e optimized X/Y.
@j X/Y
*/
DEFINE_INSN
opt_div
(CALL_INFO ci)
(VALUE recv, VALUE obj)
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_DIV, FIXNUM_REDEFINED_OP_FLAG)) {
long x, y, div;
x = FIX2LONG(recv);
y = FIX2LONG(obj);
{
/* copied from numeric.c#fixdivmod */
long mod;
if (y == 0)
goto INSN_LABEL(normal_dispatch);
if (y < 0) {
if (x < 0)
div = -x / -y;
else
div = -(x / -y);
}
else {
if (x < 0)
div = -(-x / y);
else
div = x / y;
}
mod = x - div * y;
if ((mod < 0 && y > 0) || (mod > 0 && y < 0)) {
mod += y;
div -= 1;
}
}
val = LONG2NUM(div);
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) {
val = DBL2NUM(RFLOAT_VALUE(recv) / RFLOAT_VALUE(obj));
}
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
BASIC_OP_UNREDEFINED_P(BOP_DIV, FLOAT_REDEFINED_OP_FLAG)) {
val = DBL2NUM(RFLOAT_VALUE(recv) / RFLOAT_VALUE(obj));
}
else {
goto INSN_LABEL(normal_dispatch);
}
}
else {
INSN_LABEL(normal_dispatch):
PUSH(recv);
PUSH(obj);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e optimized X%Y.
@j X%Y
*/
DEFINE_INSN
opt_mod
(CALL_INFO ci)
(VALUE recv, VALUE obj)
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_MOD, FIXNUM_REDEFINED_OP_FLAG )) {
long x, y;
x = FIX2LONG(recv);
y = FIX2LONG(obj);
if (x > 0 && y > 0) {
val = LONG2FIX(x % y);
} else {
/* copied from numeric.c#fixdivmod */
long div, mod;
if (y == 0)
rb_num_zerodiv();
if (y < 0) {
if (x < 0)
div = -x / -y;
else
div = -(x / -y);
}
else {
if (x < 0)
div = -(-x / y);
else
div = x / y;
}
mod = x - div * y;
if ((mod < 0 && y > 0) || (mod > 0 && y < 0)) {
mod += y;
div -= 1;
}
val = LONG2FIX(mod);
}
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_MOD, FLOAT_REDEFINED_OP_FLAG)) {
val = DBL2NUM(ruby_float_mod(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)));
}
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
BASIC_OP_UNREDEFINED_P(BOP_MOD, FLOAT_REDEFINED_OP_FLAG)) {
val = DBL2NUM(ruby_float_mod(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)));
}
else {
goto INSN_LABEL(normal_dispatch);
}
}
else {
INSN_LABEL(normal_dispatch):
PUSH(recv);
PUSH(obj);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e optimized X==Y.
@j X==Y
*/
DEFINE_INSN
opt_eq
(CALL_INFO ci)
(VALUE recv, VALUE obj)
(VALUE val)
{
val = opt_eq_func(recv, obj, ci);
if (val == Qundef) {
/* other */
PUSH(recv);
PUSH(obj);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e optimized X!=Y.
@j X!=Y
*/
DEFINE_INSN
opt_neq
(CALL_INFO ci, CALL_INFO ci_eq)
(VALUE recv, VALUE obj)
(VALUE val)
{
extern VALUE rb_obj_not_equal(VALUE obj1, VALUE obj2);
vm_search_method(ci, recv);
val = Qundef;
if (check_cfunc(ci->me, rb_obj_not_equal)) {
val = opt_eq_func(recv, obj, ci_eq);
if (val != Qundef) {
val = RTEST(val) ? Qfalse : Qtrue;
}
}
if (val == Qundef) {
/* other */
PUSH(recv);
PUSH(obj);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e optimized X<Y.
@j X<Y
*/
DEFINE_INSN
opt_lt
(CALL_INFO ci)
(VALUE recv, VALUE obj)
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_LT, FIXNUM_REDEFINED_OP_FLAG)) {
SIGNED_VALUE a = recv, b = obj;
if (a < b) {
val = Qtrue;
}
else {
val = Qfalse;
}
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_LT, FLOAT_REDEFINED_OP_FLAG)) {
/* flonum is not NaN */
val = RFLOAT_VALUE(recv) < RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
}
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
BASIC_OP_UNREDEFINED_P(BOP_LT, FLOAT_REDEFINED_OP_FLAG)) {
val = double_cmp_lt(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj));
}
else {
goto INSN_LABEL(normal_dispatch);
}
}
else {
INSN_LABEL(normal_dispatch):
PUSH(recv);
PUSH(obj);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e optimized X<=Y.
@j X<=Y
*/
DEFINE_INSN
opt_le
(CALL_INFO ci)
(VALUE recv, VALUE obj)
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_LE, FIXNUM_REDEFINED_OP_FLAG)) {
SIGNED_VALUE a = recv, b = obj;
if (a <= b) {
val = Qtrue;
}
else {
val = Qfalse;
}
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_LE, FLOAT_REDEFINED_OP_FLAG)) {
/* flonum is not NaN */
val = RFLOAT_VALUE(recv) <= RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
}
else {
/* other */
PUSH(recv);
PUSH(obj);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e optimized X>Y.
@j X>Y
*/
DEFINE_INSN
opt_gt
(CALL_INFO ci)
(VALUE recv, VALUE obj)
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_GT, FIXNUM_REDEFINED_OP_FLAG)) {
SIGNED_VALUE a = recv, b = obj;
if (a > b) {
val = Qtrue;
}
else {
val = Qfalse;
}
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) {
/* flonum is not NaN */
val = RFLOAT_VALUE(recv) > RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
}
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) {
val = double_cmp_gt(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj));
}
else {
goto INSN_LABEL(normal_dispatch);
}
}
else {
INSN_LABEL(normal_dispatch):
PUSH(recv);
PUSH(obj);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e optimized X>=Y.
@j X>=Y
*/
DEFINE_INSN
opt_ge
(CALL_INFO ci)
(VALUE recv, VALUE obj)
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_GE, FIXNUM_REDEFINED_OP_FLAG)) {
SIGNED_VALUE a = recv, b = obj;
if (a >= b) {
val = Qtrue;
}
else {
val = Qfalse;
}
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_GE, FLOAT_REDEFINED_OP_FLAG)) {
/* flonum is not NaN */
val = RFLOAT_VALUE(recv) >= RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
}
else {
PUSH(recv);
PUSH(obj);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e <<
@j X<<Y
*/
DEFINE_INSN
opt_ltlt
(CALL_INFO ci)
(VALUE recv, VALUE obj)
(VALUE val)
{
if (!SPECIAL_CONST_P(recv)) {
if (RBASIC_CLASS(recv) == rb_cString &&
BASIC_OP_UNREDEFINED_P(BOP_LTLT, STRING_REDEFINED_OP_FLAG)) {
val = rb_str_concat(recv, obj);
}
else if (RBASIC_CLASS(recv) == rb_cArray &&
BASIC_OP_UNREDEFINED_P(BOP_LTLT, ARRAY_REDEFINED_OP_FLAG)) {
val = rb_ary_push(recv, obj);
}
else {
goto INSN_LABEL(normal_dispatch);
}
}
else {
INSN_LABEL(normal_dispatch):
PUSH(recv);
PUSH(obj);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e []
@j recv[obj]
*/
DEFINE_INSN
opt_aref
(CALL_INFO ci)
(VALUE recv, VALUE obj)
(VALUE val)
{
if (!SPECIAL_CONST_P(recv)) {
if (RBASIC_CLASS(recv) == rb_cArray && BASIC_OP_UNREDEFINED_P(BOP_AREF, ARRAY_REDEFINED_OP_FLAG) && FIXNUM_P(obj)) {
val = rb_ary_entry(recv, FIX2LONG(obj));
}
else if (RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG)) {
val = rb_hash_aref(recv, obj);
}
else {
goto INSN_LABEL(normal_dispatch);
}
}
else {
INSN_LABEL(normal_dispatch):
PUSH(recv);
PUSH(obj);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e recv[obj] = set
@j recv[obj] = set
*/
DEFINE_INSN
opt_aset
(CALL_INFO ci)
(VALUE recv, VALUE obj, VALUE set)
(VALUE val)
{
if (!SPECIAL_CONST_P(recv)) {
if (RBASIC_CLASS(recv) == rb_cArray && BASIC_OP_UNREDEFINED_P(BOP_ASET, ARRAY_REDEFINED_OP_FLAG) && FIXNUM_P(obj)) {
rb_ary_store(recv, FIX2LONG(obj), set);
val = set;
}
else if (RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG)) {
rb_hash_aset(recv, obj, set);
val = set;
}
else {
goto INSN_LABEL(normal_dispatch);
}
}
else {
INSN_LABEL(normal_dispatch):
PUSH(recv);
PUSH(obj);
PUSH(set);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e optimized length
@j recv.length()
*/
DEFINE_INSN
opt_length
(CALL_INFO ci)
(VALUE recv)
(VALUE val)
{
if (!SPECIAL_CONST_P(recv)) {
if (RBASIC_CLASS(recv) == rb_cString &&
BASIC_OP_UNREDEFINED_P(BOP_LENGTH, STRING_REDEFINED_OP_FLAG)) {
val = rb_str_length(recv);
}
else if (RBASIC_CLASS(recv) == rb_cArray &&
BASIC_OP_UNREDEFINED_P(BOP_LENGTH, ARRAY_REDEFINED_OP_FLAG)) {
val = LONG2NUM(RARRAY_LEN(recv));
}
else if (RBASIC_CLASS(recv) == rb_cHash &&
BASIC_OP_UNREDEFINED_P(BOP_LENGTH, HASH_REDEFINED_OP_FLAG)) {
val = INT2FIX(RHASH_SIZE(recv));
}
else {
goto INSN_LABEL(normal_dispatch);
}
}
else {
INSN_LABEL(normal_dispatch):
PUSH(recv);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e optimized size
@j recv.size()
*/
DEFINE_INSN
opt_size
(CALL_INFO ci)
(VALUE recv)
(VALUE val)
{
if (!SPECIAL_CONST_P(recv)) {
if (RBASIC_CLASS(recv) == rb_cString &&
BASIC_OP_UNREDEFINED_P(BOP_SIZE, STRING_REDEFINED_OP_FLAG)) {
val = rb_str_length(recv);
}
else if (RBASIC_CLASS(recv) == rb_cArray &&
BASIC_OP_UNREDEFINED_P(BOP_SIZE, ARRAY_REDEFINED_OP_FLAG)) {
val = LONG2NUM(RARRAY_LEN(recv));
}
else if (RBASIC_CLASS(recv) == rb_cHash &&
BASIC_OP_UNREDEFINED_P(BOP_SIZE, HASH_REDEFINED_OP_FLAG)) {
val = INT2FIX(RHASH_SIZE(recv));
}
else {
goto INSN_LABEL(normal_dispatch);
}
}
else {
INSN_LABEL(normal_dispatch):
PUSH(recv);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e optimized empty?
@j recv.empty?()
*/
DEFINE_INSN
opt_empty_p
(CALL_INFO ci)
(VALUE recv)
(VALUE val)
{
if (!SPECIAL_CONST_P(recv)) {
if (RBASIC_CLASS(recv) == rb_cString &&
BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, STRING_REDEFINED_OP_FLAG)) {
if (RSTRING_LEN(recv) == 0) val = Qtrue;
else val = Qfalse;
}
else if (RBASIC_CLASS(recv) == rb_cArray &&
BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, ARRAY_REDEFINED_OP_FLAG)) {
if (RARRAY_LEN(recv) == 0) val = Qtrue;
else val = Qfalse;
}
else if (RBASIC_CLASS(recv) == rb_cHash &&
BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, HASH_REDEFINED_OP_FLAG)) {
if (RHASH_EMPTY_P(recv)) val = Qtrue;
else val = Qfalse;
}
else {
goto INSN_LABEL(normal_dispatch);
}
}
else {
INSN_LABEL(normal_dispatch):
PUSH(recv);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e optimized succ
@j recv.succ()
*/
DEFINE_INSN
opt_succ
(CALL_INFO ci)
(VALUE recv)
(VALUE val)
{
if (SPECIAL_CONST_P(recv)) {
if (FIXNUM_P(recv) &&
BASIC_OP_UNREDEFINED_P(BOP_SUCC, FIXNUM_REDEFINED_OP_FLAG)) {
const VALUE obj = INT2FIX(1);
/* fixnum + INT2FIX(1) */
val = (recv + (obj & (~1)));
if ((~(recv ^ obj) & (recv ^ val)) & ((unsigned long)LONG_MAX + 1)) {
val = rb_big_plus(rb_int2big(FIX2LONG(recv)),
rb_int2big(FIX2LONG(obj)));
}
}
else {
goto INSN_LABEL(normal_dispatch);
}
}
else {
if (RBASIC_CLASS(recv) == rb_cString &&
BASIC_OP_UNREDEFINED_P(BOP_SUCC, STRING_REDEFINED_OP_FLAG)) {
val = rb_str_succ(recv);
}
else if (RBASIC_CLASS(recv) == rb_cTime &&
BASIC_OP_UNREDEFINED_P(BOP_SUCC, TIME_REDEFINED_OP_FLAG)) {
val = rb_time_succ(recv);
}
else
{
goto INSN_LABEL(normal_dispatch);
}
}
if (0) {
INSN_LABEL(normal_dispatch):
PUSH(recv);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e optimized not
@j recv.!()
*/
DEFINE_INSN
opt_not
(CALL_INFO ci)
(VALUE recv)
(VALUE val)
{
extern VALUE rb_obj_not(VALUE obj);
vm_search_method(ci, recv);
if (check_cfunc(ci->me, rb_obj_not)) {
val = RTEST(recv) ? Qfalse : Qtrue;
}
else {
PUSH(recv);
CALL_SIMPLE_METHOD(recv);
}
}
/**
@c optimize
@e optimized regexp match
@j
*/
DEFINE_INSN
opt_regexpmatch1
(VALUE r)
(VALUE obj)
(VALUE val)
{
if (BASIC_OP_UNREDEFINED_P(BOP_MATCH, REGEXP_REDEFINED_OP_FLAG)) {
val = rb_reg_match(r, obj);
} else {
val = rb_funcall(r, idEqTilde, 1, obj);
}
}
/**
@c optimize
@e optimized regexp match 2
@j 2
*/
DEFINE_INSN
opt_regexpmatch2
(CALL_INFO ci)
(VALUE obj2, VALUE obj1)
(VALUE val)
{
if (RB_TYPE_P(obj2, T_STRING) &&
BASIC_OP_UNREDEFINED_P(BOP_MATCH, STRING_REDEFINED_OP_FLAG)) {
val = rb_reg_match(obj1, obj2);
}
else {
PUSH(obj2);
PUSH(obj1);
CALL_SIMPLE_METHOD(obj2);
}
}
/**
@c optimize
@e call native compiled method
@j
*/
DEFINE_INSN
opt_call_c_function
(rb_insn_func_t funcptr)
()
()
{
reg_cfp = (funcptr)(th, reg_cfp);
if (reg_cfp == 0) {
VALUE err = th->errinfo;
th->errinfo = Qnil;
THROW_EXCEPTION(err);
}
RESTORE_REGS();
NEXT_INSN();
}
/**
@c joke
@e BLT
@j BLT
*/
DEFINE_INSN
bitblt
()
()
(VALUE ret)
{
ret = rb_str_new2("a bit of bacon, lettuce and tomato");
}
/**
@c joke
@e The Answer to Life, the Universe, and Everything
@j
*/
DEFINE_INSN
answer
()
()
(VALUE ret)
{
ret = INT2FIX(42);
}