mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
ed935aa5be
which has been developed by Takashi Kokubun <takashikkbn@gmail> as YARV-MJIT. Many of its bugs are fixed by wanabe <s.wanabe@gmail.com>. This JIT compiler is designed to be a safe migration path to introduce JIT compiler to MRI. So this commit does not include any bytecode changes or dynamic instruction modifications, which are done in original MJIT. This commit even strips off some aggressive optimizations from YARV-MJIT, and thus it's slower than YARV-MJIT too. But it's still fairly faster than Ruby 2.5 in some benchmarks (attached below). Note that this JIT compiler passes `make test`, `make test-all`, `make test-spec` without JIT, and even with JIT. Not only it's perfectly safe with JIT disabled because it does not replace VM instructions unlike MJIT, but also with JIT enabled it stably runs Ruby applications including Rails applications. I'm expecting this version as just "initial" JIT compiler. I have many optimization ideas which are skipped for initial merging, and you may easily replace this JIT compiler with a faster one by just replacing mjit_compile.c. `mjit_compile` interface is designed for the purpose. common.mk: update dependencies for mjit_compile.c. internal.h: declare `rb_vm_insn_addr2insn` for MJIT. vm.c: exclude some definitions if `-DMJIT_HEADER` is provided to compiler. This avoids to include some functions which take a long time to compile, e.g. vm_exec_core. Some of the purpose is achieved in transform_mjit_header.rb (see `IGNORED_FUNCTIONS`) but others are manually resolved for now. Load mjit_helper.h for MJIT header. mjit_helper.h: New. This is a file used only by JIT-ed code. I'll refactor `mjit_call_cfunc` later. vm_eval.c: add some #ifdef switches to skip compiling some functions like Init_vm_eval. win32/mkexports.rb: export thread/ec functions, which are used by MJIT. include/ruby/defines.h: add MJIT_FUNC_EXPORTED macro alis to clarify that a function is exported only for MJIT. array.c: export a function used by MJIT. bignum.c: ditto. class.c: ditto. compile.c: ditto. error.c: ditto. gc.c: ditto. hash.c: ditto. iseq.c: ditto. numeric.c: ditto. object.c: ditto. proc.c: ditto. re.c: ditto. st.c: ditto. string.c: ditto. thread.c: ditto. variable.c: ditto. vm_backtrace.c: ditto. vm_insnhelper.c: ditto. vm_method.c: ditto. I would like to improve maintainability of function exports, but I believe this way is acceptable as initial merging if we clarify the new exports are for MJIT (so that we can use them as TODO list to fix) and add unit tests to detect unresolved symbols. I'll add unit tests of JIT compilations in succeeding commits. Author: Takashi Kokubun <takashikkbn@gmail.com> Contributor: wanabe <s.wanabe@gmail.com> Part of [Feature #14235] --- * Known issues * Code generated by gcc is faster than clang. The benchmark may be worse in macOS. Following benchmark result is provided by gcc w/ Linux. * Performance is decreased when Google Chrome is running * JIT can work on MinGW, but it doesn't improve performance at least in short running benchmark. * Currently it doesn't perform well with Rails. We'll try to fix this before release. --- * Benchmark reslts Benchmarked with: Intel 4.0GHz i7-4790K with 16GB memory under x86-64 Ubuntu 8 Cores - 2.0.0-p0: Ruby 2.0.0-p0 - r62186: Ruby trunk (early 2.6.0), before MJIT changes - JIT off: On this commit, but without `--jit` option - JIT on: On this commit, and with `--jit` option ** Optcarrot fps Benchmark: https://github.com/mame/optcarrot | |2.0.0-p0 |r62186 |JIT off |JIT on | |:--------|:--------|:--------|:--------|:--------| |fps |37.32 |51.46 |51.31 |58.88 | |vs 2.0.0 |1.00x |1.38x |1.37x |1.58x | ** MJIT benchmarks Benchmark: https://github.com/benchmark-driver/mjit-benchmarks (Original: https://github.com/vnmakarov/ruby/tree/rtl_mjit_branch/MJIT-benchmarks) | |2.0.0-p0 |r62186 |JIT off |JIT on | |:----------|:--------|:--------|:--------|:--------| |aread |1.00 |1.09 |1.07 |2.19 | |aref |1.00 |1.13 |1.11 |2.22 | |aset |1.00 |1.50 |1.45 |2.64 | |awrite |1.00 |1.17 |1.13 |2.20 | |call |1.00 |1.29 |1.26 |2.02 | |const2 |1.00 |1.10 |1.10 |2.19 | |const |1.00 |1.11 |1.10 |2.19 | |fannk |1.00 |1.04 |1.02 |1.00 | |fib |1.00 |1.32 |1.31 |1.84 | |ivread |1.00 |1.13 |1.12 |2.43 | |ivwrite |1.00 |1.23 |1.21 |2.40 | |mandelbrot |1.00 |1.13 |1.16 |1.28 | |meteor |1.00 |2.97 |2.92 |3.17 | |nbody |1.00 |1.17 |1.15 |1.49 | |nest-ntimes|1.00 |1.22 |1.20 |1.39 | |nest-while |1.00 |1.10 |1.10 |1.37 | |norm |1.00 |1.18 |1.16 |1.24 | |nsvb |1.00 |1.16 |1.16 |1.17 | |red-black |1.00 |1.02 |0.99 |1.12 | |sieve |1.00 |1.30 |1.28 |1.62 | |trees |1.00 |1.14 |1.13 |1.19 | |while |1.00 |1.12 |1.11 |2.41 | ** Discourse's script/bench.rb Benchmark: https://github.com/discourse/discourse/blob/v1.8.7/script/bench.rb NOTE: Rails performance was somehow a little degraded with JIT for now. We should fix this. (At least I know opt_aref is performing badly in JIT and I have an idea to fix it. Please wait for the fix.) *** JIT off Your Results: (note for timings- percentile is first, duration is second in millisecs) categories_admin: 50: 17 75: 18 90: 22 99: 29 home_admin: 50: 21 75: 21 90: 27 99: 40 topic_admin: 50: 17 75: 18 90: 22 99: 32 categories: 50: 35 75: 41 90: 43 99: 77 home: 50: 39 75: 46 90: 49 99: 95 topic: 50: 46 75: 52 90: 56 99: 101 *** JIT on Your Results: (note for timings- percentile is first, duration is second in millisecs) categories_admin: 50: 19 75: 21 90: 25 99: 33 home_admin: 50: 24 75: 26 90: 30 99: 35 topic_admin: 50: 19 75: 20 90: 25 99: 30 categories: 50: 40 75: 44 90: 48 99: 76 home: 50: 42 75: 48 90: 51 99: 89 topic: 50: 49 75: 55 90: 58 99: 99 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62197 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
1367 lines
26 KiB
C
1367 lines
26 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_frame: If it is true, VM moves pc before insn body.
|
|
|
|
- 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. If klass is Qfalse, constants
|
|
are searched as top level constants. 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. 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.
|
|
*/
|
|
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)
|
|
{
|
|
VALUE rb_obj_as_string_result(VALUE str, VALUE obj);
|
|
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;
|
|
{
|
|
VALUE rb_reg_new_ary(VALUE ary, int options);
|
|
VALUE rb_ary_tmp_new_from_values(VALUE, long, const VALUE *);
|
|
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_CFP(), 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);
|
|
|
|
INC_SP(n); /* alloca */
|
|
MEMCPY(dst, src, VALUE, n);
|
|
DEC_SP(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)
|
|
{
|
|
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)
|
|
{
|
|
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());
|
|
}
|
|
|
|
/* fire a coverage event (currently, this is used for line coverage and branch coverage) */
|
|
DEFINE_INSN
|
|
tracecoverage
|
|
(rb_num_t nf, VALUE data)
|
|
()
|
|
()
|
|
{
|
|
rb_event_flag_t flag = (rb_event_flag_t)nf;
|
|
|
|
vm_dtrace(flag, ec);
|
|
EXEC_EVENT_HOOK(ec, flag, GET_SELF(), 0, 0, 0 /* id and klass are resolved at callee */, data);
|
|
}
|
|
|
|
/**********************************************************/
|
|
/* 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)
|
|
// attr bool handles_frame = true;
|
|
{
|
|
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);
|
|
EXEC_EC_CFP();
|
|
}
|
|
|
|
/**********************************************************/
|
|
/* 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_frame = true;
|
|
// attr rb_snum_t sp_inc = - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0));
|
|
{
|
|
struct rb_calling_info calling;
|
|
|
|
vm_caller_setup_arg_block(ec, reg_cfp, &calling, ci, blockiseq, FALSE);
|
|
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)
|
|
()
|
|
(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);
|
|
}
|
|
}
|
|
|
|
DEFINE_INSN
|
|
opt_str_uminus
|
|
(VALUE str)
|
|
()
|
|
(VALUE val)
|
|
{
|
|
if (BASIC_OP_UNREDEFINED_P(BOP_UMINUS, STRING_REDEFINED_OP_FLAG)) {
|
|
val = str;
|
|
}
|
|
else {
|
|
val = rb_funcall(rb_str_resurrect(str), idUMinus, 0);
|
|
}
|
|
}
|
|
|
|
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_frame = 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_frame = 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.argc = ci->orig_argc;
|
|
|
|
vm_caller_setup_arg_block(ec, reg_cfp, &calling, ci, blockiseq, TRUE);
|
|
calling.recv = GET_SELF();
|
|
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_frame = 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();
|
|
}
|
|
}
|
|
|
|
/* return from this scope. */
|
|
DEFINE_INSN
|
|
leave
|
|
()
|
|
(VALUE val)
|
|
(VALUE val)
|
|
// attr bool handles_frame = 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);
|
|
}
|
|
}
|
|
|
|
/* if val is type, set PC to (PC + dst). */
|
|
DEFINE_INSN
|
|
branchiftype
|
|
(rb_num_t type, OFFSET dst)
|
|
(VALUE val)
|
|
()
|
|
{
|
|
if (TYPE(val) == (int)type) {
|
|
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)
|
|
{
|
|
val = vm_ic_hit_p(ic, GET_EP());
|
|
if (val != Qnil) {
|
|
JUMP(dst);
|
|
}
|
|
}
|
|
|
|
/* 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, IC ic)
|
|
()
|
|
(VALUE val)
|
|
{
|
|
val = vm_once_dispatch(ec, iseq, ic);
|
|
}
|
|
|
|
/* 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) {
|
|
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) {
|
|
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) {
|
|
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) {
|
|
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) {
|
|
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) {
|
|
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(2); /* !!! */
|
|
#endif
|
|
DISPATCH_ORIGINAL_INSN(opt_send_without_block);
|
|
}
|
|
}
|
|
|
|
/* 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) {
|
|
DISPATCH_ORIGINAL_INSN(opt_send_without_block);
|
|
}
|
|
}
|
|
|
|
/* 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) {
|
|
DISPATCH_ORIGINAL_INSN(opt_send_without_block);
|
|
}
|
|
}
|
|
|
|
/* 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) {
|
|
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) {
|
|
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) {
|
|
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) {
|
|
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) {
|
|
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(1); /* !!! */
|
|
#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(1); /* !!! */
|
|
#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) {
|
|
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) {
|
|
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) {
|
|
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) {
|
|
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) {
|
|
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) {
|
|
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_frame = true;
|
|
{
|
|
reg_cfp = (funcptr)(ec, reg_cfp);
|
|
|
|
if (reg_cfp == 0) {
|
|
VALUE err = ec->errinfo;
|
|
ec->errinfo = Qnil;
|
|
THROW_EXCEPTION(err);
|
|
}
|
|
|
|
EXEC_EC_CFP();
|
|
}
|
|
|
|
/* 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);
|
|
}
|