/* -*- 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. - 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) { 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) { 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) () { 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) { 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) () { 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) { val = GET_GLOBAL((VALUE)entry); } /* set global variable id as val. */ DEFINE_INSN setglobal (GENTRY entry) (VALUE val) () { 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) { 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 - 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) // attr rb_snum_t sp_inc = 1 - 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 - 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 rb_snum_t sp_inc = 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) { ary = vm_concat_array(ary1, ary2); } /* call to_a on array ary to splat */ DEFINE_INSN splatarray (VALUE flag) (VALUE ary) (VALUE obj) { 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 rb_snum_t sp_inc = 1 - 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); } } /* put new Range object.(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 */ /**********************************************************/ /* 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; ibody->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 bool handles_sp = true; // 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); } DEFINE_INSN opt_str_freeze (VALUE str) () (VALUE val) { val = vm_opt_str_freeze(str, BOP_FREEZE, idFreeze); } DEFINE_INSN opt_str_uminus (VALUE str) () (VALUE val) { val = vm_opt_str_freeze(str, BOP_UMINUS, idUMinus); } DEFINE_INSN opt_newarray_max (rb_num_t num) (...) (VALUE val) // attr rb_snum_t sp_inc = 1 - num; { val = vm_opt_newarray_max(num, STACK_ADDR_FROM_TOP(num)); } DEFINE_INSN opt_newarray_min (rb_num_t num) (...) (VALUE val) // attr rb_snum_t sp_inc = 1 - num; { val = vm_opt_newarray_min(num, STACK_ADDR_FROM_TOP(num)); } /* 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); } /* super(args) # args.size => num */ DEFINE_INSN invokesuper (CALL_INFO ci, CALL_CACHE cc, ISEQ blockiseq) (...) (VALUE val) // attr bool handles_sp = true; // 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) // 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) { 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) () () { 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) () { 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) () { 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) () { 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 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 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) { val = vm_opt_plus(recv, obj); if (val == Qundef) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* 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) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* 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) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* 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) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* 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) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* optimized X==Y. */ DEFINE_INSN opt_eq (CALL_INFO ci, CALL_CACHE cc) (VALUE recv, VALUE obj) (VALUE val) { val = opt_eq_func(recv, obj, ci, cc); if (val == Qundef) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* 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) { val = vm_opt_neq(ci, cc, ci_eq, cc_eq, recv, obj); if (val == Qundef) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* optimized XY. */ 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) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* 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) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* << */ 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) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* [] */ DEFINE_INSN opt_aref (CALL_INFO ci, CALL_CACHE cc) (VALUE recv, VALUE obj) (VALUE val) { val = vm_opt_aref(recv, obj); if (val == Qundef) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* recv[obj] = set */ DEFINE_INSN opt_aset (CALL_INFO ci, CALL_CACHE cc) (VALUE recv, VALUE obj, VALUE set) (VALUE val) { val = vm_opt_aset(recv, obj, set); if (val == Qundef) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* recv[str] = set */ DEFINE_INSN opt_aset_with (VALUE key, CALL_INFO ci, CALL_CACHE cc) (VALUE recv, VALUE val) (VALUE val) { 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); ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* recv[str] */ DEFINE_INSN opt_aref_with (VALUE key, CALL_INFO ci, CALL_CACHE cc) (VALUE recv) (VALUE val) { val = vm_opt_aref_with(recv, key); if (val == Qundef) { #ifndef MJIT_HEADER PUSH(rb_str_resurrect(key)); ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* 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) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* 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) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* 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) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* optimized succ */ DEFINE_INSN opt_succ (CALL_INFO ci, CALL_CACHE cc) (VALUE recv) (VALUE val) { val = vm_opt_succ(recv); if (val == Qundef) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* 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) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* optimized regexp match */ DEFINE_INSN opt_regexpmatch1 (VALUE recv) (VALUE obj) (VALUE val) { 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) { #ifndef MJIT_HEADER ADD_PC(-WIDTH_OF_opt_send_without_block); #endif DISPATCH_ORIGINAL_INSN(opt_send_without_block); } } /* call native compiled method */ DEFINE_INSN opt_call_c_function (rb_insn_func_t funcptr) () () // 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); }