mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	The instructions are just for optimization. To clarity the intention, this change adds the prefix "opt_", like "opt_case_dispatch". git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@65600 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
		
			
				
	
	
		
			1456 lines
		
	
	
	
		
			29 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1456 lines
		
	
	
	
		
			29 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* -*- 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
 | 
						|
  Massive rewrite by @shyouhei in 2017.
 | 
						|
 */
 | 
						|
 | 
						|
/* Some comments about this file's contents:
 | 
						|
 | 
						|
   - The new format aims to be editable by C editor of your choice;
 | 
						|
     your mileage might vary of course.
 | 
						|
 | 
						|
   - Each instructions are in following format:
 | 
						|
 | 
						|
     DEFINE_INSN
 | 
						|
     instruction_name
 | 
						|
     (type operand, type operand, ..)
 | 
						|
     (pop_values, ..)
 | 
						|
     (return values ..)
 | 
						|
     // attr type name contents..
 | 
						|
     {
 | 
						|
       .. // insn body
 | 
						|
     }
 | 
						|
 | 
						|
   - Unlike the old format which was line-oriented, you can now place
 | 
						|
     newlines and comments at liberal positions.
 | 
						|
 | 
						|
   - `DEFINE_INSN` is a keyword.
 | 
						|
 | 
						|
   - An instruction name must be a valid C identifier.
 | 
						|
 | 
						|
   - Operands, pop values, return values are series of either variable
 | 
						|
     declarations, keyword `void`, or keyword `...`.  They are much
 | 
						|
     like C function declarations.
 | 
						|
 | 
						|
   - Attribute pragmas are optional, and can include arbitrary C
 | 
						|
     expressions.  You can write anything there but as of writing,
 | 
						|
     supported attributes are:
 | 
						|
 | 
						|
       * sp_inc: Used to dynamically calculate sp increase in
 | 
						|
         `insn_stack_increase`.
 | 
						|
 | 
						|
       * handles_sp: If it is true, VM deals with sp in the insn.
 | 
						|
         Default is if the instruction takes ISEQ operand or not.
 | 
						|
 | 
						|
       * leaf: indicates that the instruction is "leaf" i.e. it does
 | 
						|
         not introduce new stack frame on top of it.
 | 
						|
         If an instruction handles sp, that can never be a leaf.
 | 
						|
 | 
						|
   - Attributes can access operands, but not stack (push/pop) variables.
 | 
						|
 | 
						|
   - An instruction's body is a pure C block, copied verbatimly into
 | 
						|
     the generated C source code.
 | 
						|
 */
 | 
						|
 | 
						|
/* nop */
 | 
						|
DEFINE_INSN
 | 
						|
nop
 | 
						|
()
 | 
						|
()
 | 
						|
()
 | 
						|
{
 | 
						|
    /* none */
 | 
						|
}
 | 
						|
 | 
						|
/**********************************************************/
 | 
						|
/* deal with variables                                    */
 | 
						|
/**********************************************************/
 | 
						|
 | 
						|
/* Get local variable (pointed by `idx' and `level').
 | 
						|
     'level' indicates the nesting depth from the current block.
 | 
						|
 */
 | 
						|
DEFINE_INSN
 | 
						|
getlocal
 | 
						|
(lindex_t idx, rb_num_t level)
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = *(vm_get_ep(GET_EP(), level) - idx);
 | 
						|
    RB_DEBUG_COUNTER_INC(lvar_get);
 | 
						|
    (void)RB_DEBUG_COUNTER_INC_IF(lvar_get_dynamic, level > 0);
 | 
						|
}
 | 
						|
 | 
						|
/* Set a local variable (pointed to by 'idx') as val.
 | 
						|
     'level' indicates the nesting depth from the current block.
 | 
						|
 */
 | 
						|
DEFINE_INSN
 | 
						|
setlocal
 | 
						|
(lindex_t idx, rb_num_t level)
 | 
						|
(VALUE val)
 | 
						|
()
 | 
						|
{
 | 
						|
    vm_env_write(vm_get_ep(GET_EP(), level), -(int)idx, val);
 | 
						|
    RB_DEBUG_COUNTER_INC(lvar_set);
 | 
						|
    (void)RB_DEBUG_COUNTER_INC_IF(lvar_set_dynamic, level > 0);
 | 
						|
}
 | 
						|
 | 
						|
/* Get a block parameter. */
 | 
						|
DEFINE_INSN
 | 
						|
getblockparam
 | 
						|
(lindex_t idx, rb_num_t level)
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    const VALUE *ep = vm_get_ep(GET_EP(), level);
 | 
						|
    VM_ASSERT(VM_ENV_LOCAL_P(ep));
 | 
						|
 | 
						|
    if (!VM_ENV_FLAGS(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM)) {
 | 
						|
	val = rb_vm_bh_to_procval(ec, VM_ENV_BLOCK_HANDLER(ep));
 | 
						|
	vm_env_write(ep, -(int)idx, val);
 | 
						|
	VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	val = *(ep - idx);
 | 
						|
	RB_DEBUG_COUNTER_INC(lvar_get);
 | 
						|
	(void)RB_DEBUG_COUNTER_INC_IF(lvar_get_dynamic, level > 0);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* Set block parameter. */
 | 
						|
DEFINE_INSN
 | 
						|
setblockparam
 | 
						|
(lindex_t idx, rb_num_t level)
 | 
						|
(VALUE val)
 | 
						|
()
 | 
						|
{
 | 
						|
    const VALUE *ep = vm_get_ep(GET_EP(), level);
 | 
						|
    VM_ASSERT(VM_ENV_LOCAL_P(ep));
 | 
						|
 | 
						|
    vm_env_write(ep, -(int)idx, val);
 | 
						|
    RB_DEBUG_COUNTER_INC(lvar_set);
 | 
						|
    (void)RB_DEBUG_COUNTER_INC_IF(lvar_set_dynamic, level > 0);
 | 
						|
 | 
						|
    VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM);
 | 
						|
}
 | 
						|
 | 
						|
/* Get special proxy object which only responds to `call` method if the block parameter
 | 
						|
     represents a iseq/ifunc block. Otherwise, same as `getblockparam`.
 | 
						|
 */
 | 
						|
DEFINE_INSN
 | 
						|
getblockparamproxy
 | 
						|
(lindex_t idx, rb_num_t level)
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    const VALUE *ep = vm_get_ep(GET_EP(), level);
 | 
						|
    VM_ASSERT(VM_ENV_LOCAL_P(ep));
 | 
						|
 | 
						|
    if (!VM_ENV_FLAGS(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM)) {
 | 
						|
	VALUE block_handler = VM_ENV_BLOCK_HANDLER(ep);
 | 
						|
 | 
						|
	if (block_handler) {
 | 
						|
	    switch (vm_block_handler_type(block_handler)) {
 | 
						|
	      case block_handler_type_iseq:
 | 
						|
	      case block_handler_type_ifunc:
 | 
						|
		val = rb_block_param_proxy;
 | 
						|
		break;
 | 
						|
	      case block_handler_type_symbol:
 | 
						|
		val = rb_sym_to_proc(VM_BH_TO_SYMBOL(block_handler));
 | 
						|
		goto INSN_LABEL(set);
 | 
						|
	      case block_handler_type_proc:
 | 
						|
		val = VM_BH_TO_PROC(block_handler);
 | 
						|
		goto INSN_LABEL(set);
 | 
						|
	      default:
 | 
						|
		VM_UNREACHABLE(getblockparamproxy);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    val = Qnil;
 | 
						|
	  INSN_LABEL(set):
 | 
						|
	    vm_env_write(ep, -(int)idx, val);
 | 
						|
	    VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	val = *(ep - idx);
 | 
						|
	RB_DEBUG_COUNTER_INC(lvar_get);
 | 
						|
	(void)RB_DEBUG_COUNTER_INC_IF(lvar_get_dynamic, level > 0);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* Get value of special local variable ($~, $_, ..). */
 | 
						|
DEFINE_INSN
 | 
						|
getspecial
 | 
						|
(rb_num_t key, rb_num_t type)
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_getspecial(ec, GET_LEP(), key, type);
 | 
						|
}
 | 
						|
 | 
						|
/* Set value of special local variable ($~, $_, ...) to obj. */
 | 
						|
DEFINE_INSN
 | 
						|
setspecial
 | 
						|
(rb_num_t key)
 | 
						|
(VALUE obj)
 | 
						|
()
 | 
						|
{
 | 
						|
    lep_svar_set(ec, GET_LEP(), key, obj);
 | 
						|
}
 | 
						|
 | 
						|
/* Get value of instance variable id of self. */
 | 
						|
DEFINE_INSN
 | 
						|
getinstancevariable
 | 
						|
(ID id, IC ic)
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
/* "instance variable not initialized" warning can be hooked. */
 | 
						|
// attr bool leaf = false; /* has rb_warning() */
 | 
						|
{
 | 
						|
    val = vm_getinstancevariable(GET_SELF(), id, ic);
 | 
						|
}
 | 
						|
 | 
						|
/* Set value of instance variable id of self to val. */
 | 
						|
DEFINE_INSN
 | 
						|
setinstancevariable
 | 
						|
(ID id, IC ic)
 | 
						|
(VALUE val)
 | 
						|
()
 | 
						|
{
 | 
						|
    vm_setinstancevariable(GET_SELF(), id, val, ic);
 | 
						|
}
 | 
						|
 | 
						|
/* Get value of class variable id of klass as val. */
 | 
						|
DEFINE_INSN
 | 
						|
getclassvariable
 | 
						|
(ID id)
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
/* "class variable access from toplevel" warning can be hooked. */
 | 
						|
// attr bool leaf = false; /* has rb_warning() */
 | 
						|
{
 | 
						|
    val = rb_cvar_get(vm_get_cvar_base(rb_vm_get_cref(GET_EP()), GET_CFP()), id);
 | 
						|
}
 | 
						|
 | 
						|
/* Set value of class variable id of klass as val. */
 | 
						|
DEFINE_INSN
 | 
						|
setclassvariable
 | 
						|
(ID id)
 | 
						|
(VALUE val)
 | 
						|
()
 | 
						|
/* "class variable access from toplevel" warning can be hooked. */
 | 
						|
// attr bool leaf = false; /* has rb_warning() */
 | 
						|
{
 | 
						|
    vm_ensure_not_refinement_module(GET_SELF());
 | 
						|
    rb_cvar_set(vm_get_cvar_base(rb_vm_get_cref(GET_EP()), GET_CFP()), id, val);
 | 
						|
}
 | 
						|
 | 
						|
/* Get constant variable id. If klass is Qnil, constants
 | 
						|
   are searched in the current scope. Otherwise, get constant under klass
 | 
						|
   class or module.
 | 
						|
 */
 | 
						|
DEFINE_INSN
 | 
						|
getconstant
 | 
						|
(ID id)
 | 
						|
(VALUE klass)
 | 
						|
(VALUE val)
 | 
						|
/* getconstant can kick autoload */
 | 
						|
// attr bool leaf = false; /* has rb_autoload_load() */
 | 
						|
{
 | 
						|
    val = vm_get_ev_const(ec, klass, id, 0);
 | 
						|
}
 | 
						|
 | 
						|
/* Set constant variable id under cbase class or module.
 | 
						|
 */
 | 
						|
DEFINE_INSN
 | 
						|
setconstant
 | 
						|
(ID id)
 | 
						|
(VALUE val, VALUE cbase)
 | 
						|
()
 | 
						|
/* Assigning an object to a constant is basically a leaf operation.
 | 
						|
 * The problem is, assigning a Module instance to a constant _names_
 | 
						|
 * that module.  Naming involves string manipulations, which are
 | 
						|
 * method calls. */
 | 
						|
// attr bool leaf = false; /* has StringValue() */
 | 
						|
{
 | 
						|
    vm_check_if_namespace(cbase);
 | 
						|
    vm_ensure_not_refinement_module(GET_SELF());
 | 
						|
    rb_const_set(cbase, id, val);
 | 
						|
}
 | 
						|
 | 
						|
/* get global variable id. */
 | 
						|
DEFINE_INSN
 | 
						|
getglobal
 | 
						|
(GENTRY entry)
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
// attr bool leaf = leafness_of_getglobal(entry);
 | 
						|
{
 | 
						|
    val = GET_GLOBAL((VALUE)entry);
 | 
						|
}
 | 
						|
 | 
						|
/* set global variable id as val. */
 | 
						|
DEFINE_INSN
 | 
						|
setglobal
 | 
						|
(GENTRY entry)
 | 
						|
(VALUE val)
 | 
						|
()
 | 
						|
// attr bool leaf = leafness_of_setglobal(entry);
 | 
						|
{
 | 
						|
    SET_GLOBAL((VALUE)entry, val);
 | 
						|
}
 | 
						|
 | 
						|
/**********************************************************/
 | 
						|
/* deal with values                                       */
 | 
						|
/**********************************************************/
 | 
						|
 | 
						|
/* put nil to stack. */
 | 
						|
DEFINE_INSN
 | 
						|
putnil
 | 
						|
()
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = Qnil;
 | 
						|
}
 | 
						|
 | 
						|
/* put self. */
 | 
						|
DEFINE_INSN
 | 
						|
putself
 | 
						|
()
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = GET_SELF();
 | 
						|
}
 | 
						|
 | 
						|
/* put some object.
 | 
						|
     i.e. Fixnum, true, false, nil, and so on.
 | 
						|
 */
 | 
						|
DEFINE_INSN
 | 
						|
putobject
 | 
						|
(VALUE val)
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    /* */
 | 
						|
}
 | 
						|
 | 
						|
/* put special object.  "value_type" is for expansion. */
 | 
						|
DEFINE_INSN
 | 
						|
putspecialobject
 | 
						|
(rb_num_t value_type)
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    enum vm_special_object_type type;
 | 
						|
 | 
						|
    type = (enum vm_special_object_type)value_type;
 | 
						|
    val = vm_get_special_object(GET_EP(), type);
 | 
						|
}
 | 
						|
 | 
						|
/* put iseq value. */
 | 
						|
DEFINE_INSN
 | 
						|
putiseq
 | 
						|
(ISEQ iseq)
 | 
						|
()
 | 
						|
(VALUE ret)
 | 
						|
// attr bool handles_sp = false; /* of course it doesn't */
 | 
						|
{
 | 
						|
    ret = (VALUE)iseq;
 | 
						|
}
 | 
						|
 | 
						|
/* put string val. string will be copied. */
 | 
						|
DEFINE_INSN
 | 
						|
putstring
 | 
						|
(VALUE str)
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = rb_str_resurrect(str);
 | 
						|
}
 | 
						|
 | 
						|
/* put concatenate strings */
 | 
						|
DEFINE_INSN
 | 
						|
concatstrings
 | 
						|
(rb_num_t num)
 | 
						|
(...)
 | 
						|
(VALUE val)
 | 
						|
// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num;
 | 
						|
{
 | 
						|
    val = rb_str_concat_literals(num, STACK_ADDR_FROM_TOP(num));
 | 
						|
}
 | 
						|
 | 
						|
/* push the result of to_s. */
 | 
						|
DEFINE_INSN
 | 
						|
tostring
 | 
						|
()
 | 
						|
(VALUE val, VALUE str)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = rb_obj_as_string_result(str, val);
 | 
						|
}
 | 
						|
 | 
						|
/* Freeze (dynamically) created strings. if debug_info is given, set it. */
 | 
						|
DEFINE_INSN
 | 
						|
freezestring
 | 
						|
(VALUE debug_info)
 | 
						|
(VALUE str)
 | 
						|
(VALUE str)
 | 
						|
{
 | 
						|
    vm_freezestring(str, debug_info);
 | 
						|
}
 | 
						|
 | 
						|
/* compile str to Regexp and push it.
 | 
						|
     opt is the option for the Regexp.
 | 
						|
 */
 | 
						|
DEFINE_INSN
 | 
						|
toregexp
 | 
						|
(rb_num_t opt, rb_num_t cnt)
 | 
						|
(...)
 | 
						|
(VALUE val)
 | 
						|
/* This instruction has StringValue(), which is a method call.  But it
 | 
						|
 * seems that path is never covered. */
 | 
						|
// attr bool leaf = true; /* yes it is */
 | 
						|
// attr rb_snum_t sp_inc = 1 - (rb_snum_t)cnt;
 | 
						|
{
 | 
						|
    const VALUE ary = rb_ary_tmp_new_from_values(0, cnt, STACK_ADDR_FROM_TOP(cnt));
 | 
						|
    val = rb_reg_new_ary(ary, (int)opt);
 | 
						|
    rb_ary_clear(ary);
 | 
						|
}
 | 
						|
 | 
						|
/* intern str to Symbol and push it. */
 | 
						|
DEFINE_INSN
 | 
						|
intern
 | 
						|
()
 | 
						|
(VALUE str)
 | 
						|
(VALUE sym)
 | 
						|
{
 | 
						|
    sym = rb_str_intern(str);
 | 
						|
}
 | 
						|
 | 
						|
/* put new array initialized with num values on the stack. */
 | 
						|
DEFINE_INSN
 | 
						|
newarray
 | 
						|
(rb_num_t num)
 | 
						|
(...)
 | 
						|
(VALUE val)
 | 
						|
// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num;
 | 
						|
{
 | 
						|
    val = rb_ary_new4(num, STACK_ADDR_FROM_TOP(num));
 | 
						|
}
 | 
						|
 | 
						|
/* dup array */
 | 
						|
DEFINE_INSN
 | 
						|
duparray
 | 
						|
(VALUE ary)
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = rb_ary_resurrect(ary);
 | 
						|
}
 | 
						|
 | 
						|
/* if TOS is an array expand, expand it to num objects.
 | 
						|
     if the number of the array is less than num, push nils to fill.
 | 
						|
     if it is greater than num, exceeding elements are dropped.
 | 
						|
     unless TOS is an array, push num - 1 nils.
 | 
						|
     if flags is non-zero, push the array of the rest elements.
 | 
						|
     flag: 0x01 - rest args array
 | 
						|
     flag: 0x02 - for postarg
 | 
						|
     flag: 0x04 - reverse?
 | 
						|
 */
 | 
						|
DEFINE_INSN
 | 
						|
expandarray
 | 
						|
(rb_num_t num, rb_num_t flag)
 | 
						|
(..., VALUE ary)
 | 
						|
(...)
 | 
						|
// attr bool leaf = false; /* has rb_check_array_type() */
 | 
						|
// attr rb_snum_t sp_inc = (rb_snum_t)num - 1 + (flag & 1 ? 1 : 0);
 | 
						|
{
 | 
						|
    vm_expandarray(GET_SP(), ary, num, (int)flag);
 | 
						|
}
 | 
						|
 | 
						|
/* concat two arrays */
 | 
						|
DEFINE_INSN
 | 
						|
concatarray
 | 
						|
()
 | 
						|
(VALUE ary1, VALUE ary2)
 | 
						|
(VALUE ary)
 | 
						|
// attr bool leaf = false; /* has rb_check_array_type() */
 | 
						|
{
 | 
						|
    ary = vm_concat_array(ary1, ary2);
 | 
						|
}
 | 
						|
 | 
						|
/* call to_a on array ary to splat */
 | 
						|
DEFINE_INSN
 | 
						|
splatarray
 | 
						|
(VALUE flag)
 | 
						|
(VALUE ary)
 | 
						|
(VALUE obj)
 | 
						|
// attr bool leaf = false; /* has rb_check_array_type() */
 | 
						|
{
 | 
						|
    obj = vm_splat_array(flag, ary);
 | 
						|
}
 | 
						|
 | 
						|
/* put new Hash from n elements. n must be an even number. */
 | 
						|
DEFINE_INSN
 | 
						|
newhash
 | 
						|
(rb_num_t num)
 | 
						|
(...)
 | 
						|
(VALUE val)
 | 
						|
// attr bool leaf = false; /* has rb_hash_key_str() */
 | 
						|
// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num;
 | 
						|
{
 | 
						|
    RUBY_DTRACE_CREATE_HOOK(HASH, num);
 | 
						|
 | 
						|
    val = rb_hash_new_with_size(num / 2);
 | 
						|
 | 
						|
    if (num) {
 | 
						|
        rb_hash_bulk_insert(num, STACK_ADDR_FROM_TOP(num), val);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* make new Hash object from (frozen) Array object */
 | 
						|
DEFINE_INSN
 | 
						|
newhashfromarray
 | 
						|
(rb_num_t num, VALUE ary)
 | 
						|
()
 | 
						|
(VALUE hash)
 | 
						|
// attr bool leaf = false; /* rb_hash_bulk_insert() can call methods. */
 | 
						|
{
 | 
						|
    VM_ASSERT(num * 2 == (rb_num_t)RARRAY_LEN(ary));
 | 
						|
    hash = rb_hash_new_with_size(num);
 | 
						|
    rb_hash_bulk_insert(num * 2, RARRAY_CONST_PTR_TRANSIENT(ary), hash);
 | 
						|
}
 | 
						|
 | 
						|
/* put new Range object.(Range.new(low, high, flag)) */
 | 
						|
DEFINE_INSN
 | 
						|
newrange
 | 
						|
(rb_num_t flag)
 | 
						|
(VALUE low, VALUE high)
 | 
						|
(VALUE val)
 | 
						|
/* rb_range_new() exercises "bad value for range" check. */
 | 
						|
// attr bool leaf = false; /* see also: range.c:range_init() */
 | 
						|
{
 | 
						|
    val = rb_range_new(low, high, (int)flag);
 | 
						|
}
 | 
						|
 | 
						|
/**********************************************************/
 | 
						|
/* deal with stack operation                              */
 | 
						|
/**********************************************************/
 | 
						|
 | 
						|
/* pop from stack. */
 | 
						|
DEFINE_INSN
 | 
						|
pop
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
()
 | 
						|
{
 | 
						|
    (void)val;
 | 
						|
    /* none */
 | 
						|
}
 | 
						|
 | 
						|
/* duplicate stack top. */
 | 
						|
DEFINE_INSN
 | 
						|
dup
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
(VALUE val1, VALUE val2)
 | 
						|
{
 | 
						|
    val1 = val2 = val;
 | 
						|
}
 | 
						|
 | 
						|
/* duplicate stack top n elements */
 | 
						|
DEFINE_INSN
 | 
						|
dupn
 | 
						|
(rb_num_t n)
 | 
						|
(...)
 | 
						|
(...)
 | 
						|
// attr rb_snum_t sp_inc = n;
 | 
						|
{
 | 
						|
    void *dst = GET_SP();
 | 
						|
    void *src = STACK_ADDR_FROM_TOP(n);
 | 
						|
 | 
						|
    MEMCPY(dst, src, VALUE, n);
 | 
						|
}
 | 
						|
 | 
						|
/* swap top 2 vals */
 | 
						|
DEFINE_INSN
 | 
						|
swap
 | 
						|
()
 | 
						|
(VALUE val, VALUE obj)
 | 
						|
(VALUE obj, VALUE val)
 | 
						|
{
 | 
						|
    /* none */
 | 
						|
}
 | 
						|
 | 
						|
/* reverse stack top N order. */
 | 
						|
DEFINE_INSN
 | 
						|
reverse
 | 
						|
(rb_num_t n)
 | 
						|
(...)
 | 
						|
(...)
 | 
						|
// attr rb_snum_t sp_inc = 0;
 | 
						|
{
 | 
						|
    rb_num_t i;
 | 
						|
    VALUE *sp = STACK_ADDR_FROM_TOP(n);
 | 
						|
 | 
						|
    for (i=0; i<n/2; i++) {
 | 
						|
	VALUE v0 = sp[i];
 | 
						|
	VALUE v1 = TOPN(i);
 | 
						|
	sp[i] = v1;
 | 
						|
	TOPN(i) = v0;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* for stack caching. */
 | 
						|
DEFINE_INSN
 | 
						|
reput
 | 
						|
()
 | 
						|
(..., VALUE val)
 | 
						|
(VALUE val)
 | 
						|
// attr rb_snum_t sp_inc = 0;
 | 
						|
{
 | 
						|
    /* none */
 | 
						|
}
 | 
						|
 | 
						|
/* get nth stack value from stack top */
 | 
						|
DEFINE_INSN
 | 
						|
topn
 | 
						|
(rb_num_t n)
 | 
						|
(...)
 | 
						|
(VALUE val)
 | 
						|
// attr rb_snum_t sp_inc = 1;
 | 
						|
{
 | 
						|
    val = TOPN(n);
 | 
						|
}
 | 
						|
 | 
						|
/* set Nth stack entry to stack top */
 | 
						|
DEFINE_INSN
 | 
						|
setn
 | 
						|
(rb_num_t n)
 | 
						|
(..., VALUE val)
 | 
						|
(VALUE val)
 | 
						|
// attr rb_snum_t sp_inc = 0;
 | 
						|
{
 | 
						|
    TOPN(n) = val;
 | 
						|
}
 | 
						|
 | 
						|
/* empty current stack */
 | 
						|
DEFINE_INSN
 | 
						|
adjuststack
 | 
						|
(rb_num_t n)
 | 
						|
(...)
 | 
						|
(...)
 | 
						|
// attr rb_snum_t sp_inc = -(rb_snum_t)n;
 | 
						|
{
 | 
						|
    /* none */
 | 
						|
}
 | 
						|
 | 
						|
/**********************************************************/
 | 
						|
/* deal with setting                                      */
 | 
						|
/**********************************************************/
 | 
						|
 | 
						|
/* defined? */
 | 
						|
DEFINE_INSN
 | 
						|
defined
 | 
						|
(rb_num_t op_type, VALUE obj, VALUE needstr)
 | 
						|
(VALUE v)
 | 
						|
(VALUE val)
 | 
						|
// attr bool leaf = leafness_of_defined(op_type);
 | 
						|
{
 | 
						|
    val = vm_defined(ec, GET_CFP(), op_type, obj, needstr, v);
 | 
						|
}
 | 
						|
 | 
						|
/* 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.
 | 
						|
 */
 | 
						|
DEFINE_INSN
 | 
						|
checkmatch
 | 
						|
(rb_num_t flag)
 | 
						|
(VALUE target, VALUE pattern)
 | 
						|
(VALUE result)
 | 
						|
// attr bool leaf = leafness_of_checkmatch(flag);
 | 
						|
{
 | 
						|
    result = vm_check_match(ec, target, pattern, flag);
 | 
						|
}
 | 
						|
 | 
						|
/* check keywords are specified or not. */
 | 
						|
DEFINE_INSN
 | 
						|
checkkeyword
 | 
						|
(lindex_t kw_bits_index, lindex_t keyword_index)
 | 
						|
()
 | 
						|
(VALUE ret)
 | 
						|
{
 | 
						|
    ret = vm_check_keyword(kw_bits_index, keyword_index, GET_EP());
 | 
						|
}
 | 
						|
 | 
						|
/* check if val is type. */
 | 
						|
DEFINE_INSN
 | 
						|
checktype
 | 
						|
(rb_num_t type)
 | 
						|
(VALUE val)
 | 
						|
(VALUE ret)
 | 
						|
{
 | 
						|
    ret = (TYPE(val) == (int)type) ? Qtrue : Qfalse;
 | 
						|
}
 | 
						|
 | 
						|
/**********************************************************/
 | 
						|
/* deal with control flow 1: class/module                 */
 | 
						|
/**********************************************************/
 | 
						|
 | 
						|
/* enter class definition scope. if super is Qfalse, and class
 | 
						|
   "klass" is defined, it's redefine. otherwise, define "klass" class.
 | 
						|
 */
 | 
						|
DEFINE_INSN
 | 
						|
defineclass
 | 
						|
(ID id, ISEQ class_iseq, rb_num_t flags)
 | 
						|
(VALUE cbase, VALUE super)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    VALUE klass = vm_find_or_create_class_by_id(id, flags, cbase, super);
 | 
						|
 | 
						|
    rb_iseq_check(class_iseq);
 | 
						|
 | 
						|
    /* enter scope */
 | 
						|
    vm_push_frame(ec, class_iseq, VM_FRAME_MAGIC_CLASS | VM_ENV_FLAG_LOCAL, klass,
 | 
						|
		  GET_BLOCK_HANDLER(),
 | 
						|
		  (VALUE)vm_cref_push(ec, klass, NULL, FALSE),
 | 
						|
		  class_iseq->body->iseq_encoded, GET_SP(),
 | 
						|
		  class_iseq->body->local_table_size,
 | 
						|
		  class_iseq->body->stack_max);
 | 
						|
    RESTORE_REGS();
 | 
						|
    NEXT_INSN();
 | 
						|
}
 | 
						|
 | 
						|
/**********************************************************/
 | 
						|
/* deal with control flow 2: method/iterator              */
 | 
						|
/**********************************************************/
 | 
						|
 | 
						|
/* invoke method. */
 | 
						|
DEFINE_INSN
 | 
						|
send
 | 
						|
(CALL_INFO ci, CALL_CACHE cc, ISEQ blockiseq)
 | 
						|
(...)
 | 
						|
(VALUE val)
 | 
						|
// attr rb_snum_t sp_inc = - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0));
 | 
						|
{
 | 
						|
    struct rb_calling_info calling;
 | 
						|
 | 
						|
    calling.block_handler = vm_caller_setup_arg_block(ec, reg_cfp, ci, blockiseq, FALSE);
 | 
						|
    calling.recv = TOPN(calling.argc = ci->orig_argc);
 | 
						|
    vm_search_method(ci, cc, calling.recv);
 | 
						|
    CALL_METHOD(&calling, ci, cc);
 | 
						|
}
 | 
						|
 | 
						|
/* Invoke method without block */
 | 
						|
DEFINE_INSN
 | 
						|
opt_send_without_block
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(...)
 | 
						|
(VALUE val)
 | 
						|
// attr bool handles_sp = true;
 | 
						|
// attr rb_snum_t sp_inc = -ci->orig_argc;
 | 
						|
{
 | 
						|
    struct rb_calling_info calling;
 | 
						|
    calling.block_handler = VM_BLOCK_HANDLER_NONE;
 | 
						|
    vm_search_method(ci, cc, calling.recv = TOPN(calling.argc = ci->orig_argc));
 | 
						|
    CALL_METHOD(&calling, ci, cc);
 | 
						|
}
 | 
						|
 | 
						|
DEFINE_INSN
 | 
						|
opt_str_freeze
 | 
						|
(VALUE str, CALL_INFO ci, CALL_CACHE cc)
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_str_freeze(str, BOP_FREEZE, idFreeze);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        PUSH(rb_str_resurrect(str));
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
DEFINE_INSN
 | 
						|
opt_str_uminus
 | 
						|
(VALUE str, CALL_INFO ci, CALL_CACHE cc)
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_str_freeze(str, BOP_UMINUS, idUMinus);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        PUSH(rb_str_resurrect(str));
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
DEFINE_INSN
 | 
						|
opt_newarray_max
 | 
						|
(rb_num_t num)
 | 
						|
(...)
 | 
						|
(VALUE val)
 | 
						|
/* This instruction typically has no funcalls.  But it compares array
 | 
						|
 * contents each other by nature.  That part could call methods when
 | 
						|
 * necessary.  No way to detect such method calls beforehand.  We
 | 
						|
 * cannot but mark it being not leaf. */
 | 
						|
// attr bool leaf = false; /* has rb_funcall() */
 | 
						|
// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num;
 | 
						|
{
 | 
						|
    val = vm_opt_newarray_max(num, STACK_ADDR_FROM_TOP(num));
 | 
						|
}
 | 
						|
 | 
						|
DEFINE_INSN
 | 
						|
opt_newarray_min
 | 
						|
(rb_num_t num)
 | 
						|
(...)
 | 
						|
(VALUE val)
 | 
						|
/* Same discussion as opt_newarray_max. */
 | 
						|
// attr bool leaf = false; /* has rb_funcall() */
 | 
						|
// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num;
 | 
						|
{
 | 
						|
    val = vm_opt_newarray_min(num, STACK_ADDR_FROM_TOP(num));
 | 
						|
}
 | 
						|
 | 
						|
/* super(args) # args.size => num */
 | 
						|
DEFINE_INSN
 | 
						|
invokesuper
 | 
						|
(CALL_INFO ci, CALL_CACHE cc, ISEQ blockiseq)
 | 
						|
(...)
 | 
						|
(VALUE val)
 | 
						|
// attr rb_snum_t sp_inc = - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0));
 | 
						|
{
 | 
						|
    struct rb_calling_info calling;
 | 
						|
 | 
						|
    calling.block_handler = vm_caller_setup_arg_block(ec, reg_cfp, ci, blockiseq, TRUE);
 | 
						|
    calling.recv = TOPN(calling.argc = ci->orig_argc);
 | 
						|
    vm_search_super_method(ec, GET_CFP(), &calling, ci, cc);
 | 
						|
    CALL_METHOD(&calling, ci, cc);
 | 
						|
}
 | 
						|
 | 
						|
/* yield(args) */
 | 
						|
DEFINE_INSN
 | 
						|
invokeblock
 | 
						|
(CALL_INFO ci)
 | 
						|
(...)
 | 
						|
(VALUE val)
 | 
						|
// attr bool handles_sp = true;
 | 
						|
// attr rb_snum_t sp_inc = 1 - ci->orig_argc;
 | 
						|
{
 | 
						|
    struct rb_calling_info calling;
 | 
						|
    VALUE block_handler;
 | 
						|
 | 
						|
    calling.argc = ci->orig_argc;
 | 
						|
    calling.block_handler = VM_BLOCK_HANDLER_NONE;
 | 
						|
    calling.recv = Qundef; /* should not be used */
 | 
						|
 | 
						|
    block_handler = VM_CF_BLOCK_HANDLER(GET_CFP());
 | 
						|
    if (block_handler == VM_BLOCK_HANDLER_NONE) {
 | 
						|
	rb_vm_localjump_error("no block given (yield)", Qnil, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    val = vm_invoke_block(ec, GET_CFP(), &calling, ci, block_handler);
 | 
						|
    if (val == Qundef) {
 | 
						|
        EXEC_EC_CFP(val);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* return from this scope. */
 | 
						|
DEFINE_INSN
 | 
						|
leave
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
(VALUE val)
 | 
						|
/* This is super surprising but when leaving from a frame, we check
 | 
						|
 * for interrupts.  If any, that should be executed on top of the
 | 
						|
 * current execution context.  This is a method call. */
 | 
						|
// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
 | 
						|
// attr bool handles_sp = true;
 | 
						|
{
 | 
						|
    if (OPT_CHECKED_RUN) {
 | 
						|
	const VALUE *const bp = vm_base_ptr(reg_cfp);
 | 
						|
	if (reg_cfp->sp != bp) {
 | 
						|
	    vm_stack_consistency_error(ec, reg_cfp, bp);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    RUBY_VM_CHECK_INTS(ec);
 | 
						|
 | 
						|
    if (vm_pop_frame(ec, GET_CFP(), GET_EP())) {
 | 
						|
#if OPT_CALL_THREADED_CODE
 | 
						|
	rb_ec_thread_ptr(ec)->retval = val;
 | 
						|
	return 0;
 | 
						|
#else
 | 
						|
	return val;
 | 
						|
#endif
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	RESTORE_REGS();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**********************************************************/
 | 
						|
/* deal with control flow 3: exception                    */
 | 
						|
/**********************************************************/
 | 
						|
 | 
						|
/* longjump */
 | 
						|
DEFINE_INSN
 | 
						|
throw
 | 
						|
(rb_num_t throw_state)
 | 
						|
(VALUE throwobj)
 | 
						|
(VALUE val)
 | 
						|
/* Same discussion as leave. */
 | 
						|
// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
 | 
						|
{
 | 
						|
    RUBY_VM_CHECK_INTS(ec);
 | 
						|
    val = vm_throw(ec, GET_CFP(), throw_state, throwobj);
 | 
						|
    THROW_EXCEPTION(val);
 | 
						|
    /* unreachable */
 | 
						|
}
 | 
						|
 | 
						|
/**********************************************************/
 | 
						|
/* deal with control flow 4: local jump                   */
 | 
						|
/**********************************************************/
 | 
						|
 | 
						|
/* set PC to (PC + dst). */
 | 
						|
DEFINE_INSN
 | 
						|
jump
 | 
						|
(OFFSET dst)
 | 
						|
()
 | 
						|
()
 | 
						|
/* Same discussion as leave. */
 | 
						|
// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
 | 
						|
{
 | 
						|
    RUBY_VM_CHECK_INTS(ec);
 | 
						|
    JUMP(dst);
 | 
						|
}
 | 
						|
 | 
						|
/* if val is not false or nil, set PC to (PC + dst). */
 | 
						|
DEFINE_INSN
 | 
						|
branchif
 | 
						|
(OFFSET dst)
 | 
						|
(VALUE val)
 | 
						|
()
 | 
						|
/* Same discussion as jump. */
 | 
						|
// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
 | 
						|
{
 | 
						|
    if (RTEST(val)) {
 | 
						|
	RUBY_VM_CHECK_INTS(ec);
 | 
						|
	JUMP(dst);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* if val is false or nil, set PC to (PC + dst). */
 | 
						|
DEFINE_INSN
 | 
						|
branchunless
 | 
						|
(OFFSET dst)
 | 
						|
(VALUE val)
 | 
						|
()
 | 
						|
/* Same discussion as jump. */
 | 
						|
// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
 | 
						|
{
 | 
						|
    if (!RTEST(val)) {
 | 
						|
	RUBY_VM_CHECK_INTS(ec);
 | 
						|
	JUMP(dst);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* if val is nil, set PC to (PC + dst). */
 | 
						|
DEFINE_INSN
 | 
						|
branchnil
 | 
						|
(OFFSET dst)
 | 
						|
(VALUE val)
 | 
						|
()
 | 
						|
/* Same discussion as jump. */
 | 
						|
// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */
 | 
						|
{
 | 
						|
    if (NIL_P(val)) {
 | 
						|
	RUBY_VM_CHECK_INTS(ec);
 | 
						|
	JUMP(dst);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**********************************************************/
 | 
						|
/* for optimize                                           */
 | 
						|
/**********************************************************/
 | 
						|
 | 
						|
/* push inline-cached value and go to dst if it is valid */
 | 
						|
DEFINE_INSN
 | 
						|
opt_getinlinecache
 | 
						|
(OFFSET dst, IC ic)
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    if (vm_ic_hit_p(ic, GET_EP())) {
 | 
						|
	val = ic->ic_value.value;
 | 
						|
	JUMP(dst);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	val = Qnil;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* set inline cache */
 | 
						|
DEFINE_INSN
 | 
						|
opt_setinlinecache
 | 
						|
(IC ic)
 | 
						|
(VALUE val)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    vm_ic_update(ic, val, GET_EP());
 | 
						|
}
 | 
						|
 | 
						|
/* run iseq only once */
 | 
						|
DEFINE_INSN
 | 
						|
once
 | 
						|
(ISEQ iseq, ISE ise)
 | 
						|
()
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_once_dispatch(ec, iseq, ise);
 | 
						|
}
 | 
						|
 | 
						|
/* case dispatcher, jump by table if possible */
 | 
						|
DEFINE_INSN
 | 
						|
opt_case_dispatch
 | 
						|
(CDHASH hash, OFFSET else_offset)
 | 
						|
(..., VALUE key)
 | 
						|
()
 | 
						|
// attr rb_snum_t sp_inc = -1;
 | 
						|
{
 | 
						|
    OFFSET dst = vm_case_dispatch(hash, else_offset, key);
 | 
						|
 | 
						|
    if (dst) {
 | 
						|
	JUMP(dst);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/** simple functions */
 | 
						|
 | 
						|
/* optimized X+Y. */
 | 
						|
DEFINE_INSN
 | 
						|
opt_plus
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE obj)
 | 
						|
(VALUE val)
 | 
						|
/* Array + anything can be handled inside of opt_plus, and that
 | 
						|
 * anything is converted into array using #to_ary. */
 | 
						|
// attr bool leaf = false; /* has rb_to_array_type() */
 | 
						|
{
 | 
						|
    val = vm_opt_plus(recv, obj);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized X-Y. */
 | 
						|
DEFINE_INSN
 | 
						|
opt_minus
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE obj)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_minus(recv, obj);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized X*Y. */
 | 
						|
DEFINE_INSN
 | 
						|
opt_mult
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE obj)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_mult(recv, obj);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized X/Y. */
 | 
						|
DEFINE_INSN
 | 
						|
opt_div
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE obj)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_div(recv, obj);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized X%Y. */
 | 
						|
DEFINE_INSN
 | 
						|
opt_mod
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE obj)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_mod(recv, obj);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized X==Y. */
 | 
						|
DEFINE_INSN
 | 
						|
opt_eq
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE obj)
 | 
						|
(VALUE val)
 | 
						|
/* This instruction can compare a string with non-string.  This
 | 
						|
 * (somewhat) coerces the non-string into a string, via a method
 | 
						|
 * call. */
 | 
						|
// attr bool leaf = false; /* has rb_str_equal() */
 | 
						|
{
 | 
						|
    val = opt_eq_func(recv, obj, ci, cc);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized X!=Y. */
 | 
						|
DEFINE_INSN
 | 
						|
opt_neq
 | 
						|
(CALL_INFO ci_eq, CALL_CACHE cc_eq, CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE obj)
 | 
						|
(VALUE val)
 | 
						|
/* Same discussion as opt_eq. */
 | 
						|
// attr bool leaf = false; /* has rb_str_equal() */
 | 
						|
{
 | 
						|
    val = vm_opt_neq(ci, cc, ci_eq, cc_eq, recv, obj);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized X<Y. */
 | 
						|
DEFINE_INSN
 | 
						|
opt_lt
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE obj)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_lt(recv, obj);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized X<=Y. */
 | 
						|
DEFINE_INSN
 | 
						|
opt_le
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE obj)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_le(recv, obj);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized X>Y. */
 | 
						|
DEFINE_INSN
 | 
						|
opt_gt
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE obj)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_gt(recv, obj);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized X>=Y. */
 | 
						|
DEFINE_INSN
 | 
						|
opt_ge
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE obj)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_ge(recv, obj);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* << */
 | 
						|
DEFINE_INSN
 | 
						|
opt_ltlt
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE obj)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_ltlt(recv, obj);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized X&Y. */
 | 
						|
DEFINE_INSN
 | 
						|
opt_and
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE obj)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_and(recv, obj);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized X|Y. */
 | 
						|
DEFINE_INSN
 | 
						|
opt_or
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE obj)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_or(recv, obj);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* [] */
 | 
						|
DEFINE_INSN
 | 
						|
opt_aref
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE obj)
 | 
						|
(VALUE val)
 | 
						|
/* This is complicated.  In case of hash, vm_opt_aref() resorts to
 | 
						|
 * rb_hash_aref().  If `recv` has no `obj`, this function then yields
 | 
						|
 * default_proc.  This is a method call.  So opt_aref is
 | 
						|
 * (surprisingly) not leaf. */
 | 
						|
// attr bool leaf = false; /* has rb_funcall() */ /* calls #yield */
 | 
						|
{
 | 
						|
    val = vm_opt_aref(recv, obj);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* recv[obj] = set */
 | 
						|
DEFINE_INSN
 | 
						|
opt_aset
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE obj, VALUE set)
 | 
						|
(VALUE val)
 | 
						|
/* This is another story than opt_aref.  When vm_opt_aset() resorts
 | 
						|
 * to rb_hash_aset(), which should call #hash for `obj`. */
 | 
						|
// attr bool leaf = false; /* has rb_funcall() */ /* calls #hash */
 | 
						|
{
 | 
						|
    val = vm_opt_aset(recv, obj, set);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* recv[str] = set */
 | 
						|
DEFINE_INSN
 | 
						|
opt_aset_with
 | 
						|
(VALUE key, CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv, VALUE val)
 | 
						|
(VALUE val)
 | 
						|
/* Same discussion as opt_aset. */
 | 
						|
// attr bool leaf = false; /* has rb_funcall() */ /* calls #hash */
 | 
						|
{
 | 
						|
    VALUE tmp = vm_opt_aset_with(recv, key, val);
 | 
						|
 | 
						|
    if (tmp != Qundef) {
 | 
						|
	val = tmp;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
#ifndef MJIT_HEADER
 | 
						|
	TOPN(0) = rb_str_resurrect(key);
 | 
						|
	PUSH(val);
 | 
						|
#endif
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* recv[str] */
 | 
						|
DEFINE_INSN
 | 
						|
opt_aref_with
 | 
						|
(VALUE key, CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv)
 | 
						|
(VALUE val)
 | 
						|
/* Same discussion as opt_aref. */
 | 
						|
// attr bool leaf = false; /* has rb_funcall() */ /* calls #yield */
 | 
						|
{
 | 
						|
    val = vm_opt_aref_with(recv, key);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
#ifndef MJIT_HEADER
 | 
						|
	PUSH(rb_str_resurrect(key));
 | 
						|
#endif
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized length */
 | 
						|
DEFINE_INSN
 | 
						|
opt_length
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_length(recv, BOP_LENGTH);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized size */
 | 
						|
DEFINE_INSN
 | 
						|
opt_size
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_length(recv, BOP_SIZE);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized empty? */
 | 
						|
DEFINE_INSN
 | 
						|
opt_empty_p
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_empty_p(recv);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized succ */
 | 
						|
DEFINE_INSN
 | 
						|
opt_succ
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_succ(recv);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized not */
 | 
						|
DEFINE_INSN
 | 
						|
opt_not
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE recv)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_not(ci, cc, recv);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* optimized regexp match */
 | 
						|
DEFINE_INSN
 | 
						|
opt_regexpmatch1
 | 
						|
(VALUE recv)
 | 
						|
(VALUE obj)
 | 
						|
(VALUE val)
 | 
						|
// attr bool leaf = BASIC_OP_UNREDEFINED_P(BOP_MATCH, REGEXP_REDEFINED_OP_FLAG);
 | 
						|
{
 | 
						|
    val = vm_opt_regexpmatch1(recv, obj);
 | 
						|
}
 | 
						|
 | 
						|
/* optimized regexp match 2 */
 | 
						|
DEFINE_INSN
 | 
						|
opt_regexpmatch2
 | 
						|
(CALL_INFO ci, CALL_CACHE cc)
 | 
						|
(VALUE obj2, VALUE obj1)
 | 
						|
(VALUE val)
 | 
						|
{
 | 
						|
    val = vm_opt_regexpmatch2(obj2, obj1);
 | 
						|
 | 
						|
    if (val == Qundef) {
 | 
						|
        CALL_SIMPLE_METHOD();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* call native compiled method */
 | 
						|
DEFINE_INSN
 | 
						|
opt_call_c_function
 | 
						|
(rb_insn_func_t funcptr)
 | 
						|
()
 | 
						|
()
 | 
						|
// attr bool leaf = false; /* anything can happen inside */
 | 
						|
// attr bool handles_sp = true;
 | 
						|
{
 | 
						|
    reg_cfp = (funcptr)(ec, reg_cfp);
 | 
						|
 | 
						|
    if (reg_cfp == 0) {
 | 
						|
	VALUE err = ec->errinfo;
 | 
						|
	ec->errinfo = Qnil;
 | 
						|
	THROW_EXCEPTION(err);
 | 
						|
    }
 | 
						|
 | 
						|
    RESTORE_REGS();
 | 
						|
    NEXT_INSN();
 | 
						|
}
 | 
						|
 | 
						|
/* BLT */
 | 
						|
DEFINE_INSN
 | 
						|
bitblt
 | 
						|
()
 | 
						|
()
 | 
						|
(VALUE ret)
 | 
						|
{
 | 
						|
    ret = rb_str_new2("a bit of bacon, lettuce and tomato");
 | 
						|
}
 | 
						|
 | 
						|
/* The Answer to Life, the Universe, and Everything */
 | 
						|
DEFINE_INSN
 | 
						|
answer
 | 
						|
()
 | 
						|
()
 | 
						|
(VALUE ret)
 | 
						|
{
 | 
						|
    ret = INT2FIX(42);
 | 
						|
}
 |