mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
8952964976
Directly marking iseq operands allows us to eliminate the "mark array" stored on ISEQ objects, which will reduce the amount of memory ISEQ objects consume. This patch changes the iseq mark function to: * Directly marks ISEQ operands * Iterate over and mark child ISEQs It also introduces two flags on the ISEQ object. In order to mark instruction operands, we have to disassemble the instructions and find the instruction parameters and types. Instructions may also be translated to jump addresses. Instruction sequences may get marked by the GC *while* they're mid flight (being compiled). The `ISEQ_TRANSLATED` flag is used to indicate whether or not the instructions have been translated to jump addresses so that when we decode the instructions we know whether or not we need to go from jump location back to original instruction or not. Not all ISEQ objects have any markable objects embedded in their instructions. We can detect whether or not an ISEQ has markable objects in the instructions at compile time. If the instructions contain markable objects, we set a flag `ISEQ_MARKABLE_ISEQ` on the ISEQ object. This means that during the mark phase, we can skip decompilation if the flag is *not* set. In other words, we can avoid decompilation of we know in advance there is nothing to mark. `once` instructions have an operand that contains the result of a one-time compilation of a regex. Before this patch, that operand was called an "inline cache", even though the struct was actually an "inline storage". This patch changes the operand to be an "inline storage" so that we can differentiate between caches that need marking (the inline storage) and caches that don't need marking (inline cache). [ruby-core:84909] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62706 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
1370 lines
26 KiB
C
1370 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_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; 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);
|
|
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_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(val);
|
|
}
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
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) {
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|