split insns.def into functions

Contemporary C compilers are good at function inlining.  They fold
multiple functions into one.  However they are not yet smart enough to
unfold a function into several ones.  So generally speaking, it is
wiser for a C programmer to manually split C functions whenever
possible.  That should make rooms for compilers to optimize at will.

Before this changeset insns.def was converted into single HUGE
function called vm_exec_core().  By moving each instruction's core
into individual functions, generated C source code is reduced from
3,428 lines to 2,847 lines.  Looking at the generated assembly
however, it seems my compiler (gcc 6.2) is extraordinary smart so that
it inlines almost all functions I introduced in this changeset back
into that vm_exec_core.  On my machine compiled machine binary of the
function does not shrink very much in size (28,432 bytes to 26,816
bytes, according to nm(1)).

I believe this change is zero-cost.  Several benchmarks I exercised
showed no significant difference beyond error mergin.  For instance
3 repeated runs of optcarrot benchmark on my machine resulted in:

   before this: 28.330329285707490, 27.513378371065920, 29.40420215754537
   after  this: 27.107195867280414, 25.549324021385907, 30.31581919050884

in fps (greater==faster).

----

	* internal.h (rb_obj_not_equal): used from vm_insnhelper.c
	* insns.def: move vast majority of lines into vm_insnhelper.c
	* vm_insnhelper.c: moved here.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58390 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
shyouhei 2017-04-18 10:58:49 +00:00
Родитель d9ea26de88
Коммит 4d21d1f04b
3 изменённых файлов: 976 добавлений и 662 удалений

771
insns.def

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1421,6 +1421,7 @@ NORETURN(void rb_undefined_alloc(VALUE klass));
double rb_num_to_dbl(VALUE val);
VALUE rb_obj_dig(int argc, VALUE *argv, VALUE self, VALUE notfound);
VALUE rb_immutable_obj_clone(int, VALUE *, VALUE);
VALUE rb_obj_not_equal(VALUE obj1, VALUE obj2);
struct RBasicRaw {
VALUE flags;

Просмотреть файл

@ -2880,3 +2880,869 @@ vm_defined(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t op_type, VALUE
return Qnil;
}
}
static const VALUE *
vm_get_ep(const VALUE *const reg_ep, rb_num_t lv)
{
rb_num_t i;
const VALUE *ep = reg_ep;
for (i = 0; i < lv; i++) {
ep = GET_PREV_EP(ep);
}
return ep;
}
static VALUE
vm_get_special_object(const VALUE *const reg_ep,
enum vm_special_object_type type)
{
switch (type) {
case VM_SPECIAL_OBJECT_VMCORE:
return rb_mRubyVMFrozenCore;
case VM_SPECIAL_OBJECT_CBASE:
return vm_get_cbase(reg_ep);
case VM_SPECIAL_OBJECT_CONST_BASE:
return vm_get_const_base(reg_ep);
default:
rb_bug("putspecialobject insn: unknown value_type %d", type);
}
}
static void
vm_freezestring(VALUE str, VALUE debug)
{
if (!NIL_P(debug)) {
rb_ivar_set(str, id_debug_created_info, debug);
}
rb_str_freeze(str);
}
static VALUE
vm_concat_array(VALUE ary1, VALUE ary2st)
{
const VALUE ary2 = ary2st;
VALUE tmp1 = rb_check_convert_type(ary1, T_ARRAY, "Array", "to_a");
VALUE tmp2 = rb_check_convert_type(ary2, T_ARRAY, "Array", "to_a");
if (NIL_P(tmp1)) {
tmp1 = rb_ary_new3(1, ary1);
}
if (NIL_P(tmp2)) {
tmp2 = rb_ary_new3(1, ary2);
}
if (tmp1 == ary1) {
tmp1 = rb_ary_dup(ary1);
}
return rb_ary_concat(tmp1, tmp2);
}
static VALUE
vm_splat_array(VALUE flag, VALUE ary)
{
VALUE tmp = rb_check_convert_type(ary, T_ARRAY, "Array", "to_a");
if (NIL_P(tmp)) {
return rb_ary_new3(1, ary);
}
else if (RTEST(flag)) {
return rb_ary_dup(tmp);
}
else {
return tmp;
}
}
static VALUE
vm_check_match(VALUE target, VALUE pattern, rb_num_t flag)
{
enum vm_check_match_type type = ((int)flag) & VM_CHECKMATCH_TYPE_MASK;
if (flag & VM_CHECKMATCH_ARRAY) {
long i;
const long n = RARRAY_LEN(pattern);
for (i = 0; i < n; i++) {
VALUE v = RARRAY_AREF(pattern, i);
VALUE c = check_match(v, target, type);
if (RTEST(c)) {
return c;
}
}
return Qfalse;
}
else {
return check_match(pattern, target, type);
}
}
static VALUE
vm_check_keyword(lindex_t bits, lindex_t idx, const VALUE *ep)
{
const VALUE kw_bits = *(ep - bits);
if (FIXNUM_P(kw_bits)) {
int b = FIX2INT(kw_bits);
return (b & (0x01 << idx)) ? Qfalse : Qtrue;
}
else {
VM_ASSERT(RB_TYPE_P(kw_bits, T_HASH));
return rb_hash_has_key(kw_bits, INT2FIX(idx));
}
}
static void
vm_dtrace(rb_event_flag_t flag, rb_thread_t *th)
{
if (RUBY_DTRACE_METHOD_ENTRY_ENABLED() ||
RUBY_DTRACE_METHOD_RETURN_ENABLED() ||
RUBY_DTRACE_CMETHOD_ENTRY_ENABLED() ||
RUBY_DTRACE_CMETHOD_RETURN_ENABLED()) {
switch (flag) {
case RUBY_EVENT_CALL:
RUBY_DTRACE_METHOD_ENTRY_HOOK(th, 0, 0);
return;
case RUBY_EVENT_C_CALL:
RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, 0, 0);
return;
case RUBY_EVENT_RETURN:
RUBY_DTRACE_METHOD_RETURN_HOOK(th, 0, 0);
return;
case RUBY_EVENT_C_RETURN:
RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, 0, 0);
return;
}
}
}
static VALUE
vm_const_get_under(ID id, rb_num_t flags, VALUE cbase)
{
VALUE ns;
if ((ns = vm_search_const_defined_class(cbase, id)) == 0) {
return ns;
}
else if (VM_DEFINECLASS_SCOPED_P(flags)) {
return rb_public_const_get_at(ns, id);
}
else {
return rb_const_get_at(ns, id);
}
}
static VALUE
vm_check_if_class(ID id, rb_num_t flags, VALUE super, VALUE klass)
{
if (!RB_TYPE_P(klass, T_CLASS)) {
rb_raise(rb_eTypeError, "%"PRIsVALUE" is not a class", rb_id2str(id));
}
else if (VM_DEFINECLASS_HAS_SUPERCLASS_P(flags)) {
VALUE tmp = rb_class_real(RCLASS_SUPER(klass));
if (tmp != super) {
rb_raise(rb_eTypeError,
"superclass mismatch for class %"PRIsVALUE"",
rb_id2str(id));
}
else {
return klass;
}
}
else {
return klass;
}
}
static VALUE
vm_check_if_module(ID id, VALUE mod)
{
if (!RB_TYPE_P(mod, T_MODULE)) {
rb_raise(rb_eTypeError, "%"PRIsVALUE" is not a module", rb_id2str(id));
}
else {
return mod;
}
}
static VALUE
vm_declare_class(ID id, rb_num_t flags, VALUE cbase, VALUE super)
{
/* new class declaration */
VALUE s = VM_DEFINECLASS_HAS_SUPERCLASS_P(flags) ? super : rb_cObject;
VALUE c = rb_define_class_id(id, s);
rb_set_class_path_string(c, cbase, rb_id2str(id));
rb_const_set(cbase, id, c);
rb_class_inherited(s, c);
return c;
}
static VALUE
vm_declare_module(ID id, VALUE cbase)
{
/* new module declaration */
VALUE mod = rb_define_module_id(id);
rb_set_class_path_string(mod, cbase, rb_id2str(id));
rb_const_set(cbase, id, mod);
return mod;
}
static VALUE
vm_define_class(ID id, rb_num_t flags, VALUE cbase, VALUE super)
{
VALUE klass;
if (VM_DEFINECLASS_HAS_SUPERCLASS_P(flags) && !RB_TYPE_P(super, T_CLASS)) {
rb_raise(rb_eTypeError,
"superclass must be a Class (%"PRIsVALUE" given)",
rb_obj_class(super));
}
vm_check_if_namespace(cbase);
/* find klass */
rb_autoload_load(cbase, id);
if ((klass = vm_const_get_under(id, flags, cbase)) != 0) {
return vm_check_if_class(id, flags, super, klass);
}
else {
return vm_declare_class(id, flags, cbase, super);
}
}
static VALUE
vm_define_module(ID id, rb_num_t flags, VALUE cbase)
{
VALUE mod;
vm_check_if_namespace(cbase);
if ((mod = vm_const_get_under(id, flags, cbase)) != 0) {
return vm_check_if_module(id, mod);
}
else {
return vm_declare_module(id, cbase);
}
}
static VALUE
vm_find_or_create_class_by_id(ID id,
rb_num_t flags,
VALUE cbase,
VALUE super)
{
rb_vm_defineclass_type_t type = VM_DEFINECLASS_TYPE(flags);
switch (type) {
case VM_DEFINECLASS_TYPE_CLASS:
/* classdef returns class scope value */
return vm_define_class(id, flags, cbase, super);
case VM_DEFINECLASS_TYPE_SINGLETON_CLASS:
/* classdef returns class scope value */
return rb_singleton_class(cbase);
case VM_DEFINECLASS_TYPE_MODULE:
/* classdef returns class scope value */
return vm_define_module(id, flags, cbase);
default:
rb_bug("unknown defineclass type: %d", (int)type);
}
}
/* this macro is mandatory to use OPTIMIZED_CMP. What a design! */
#define id_cmp idCmp
static VALUE
vm_opt_newarray_max(rb_num_t num, const VALUE *ptr)
{
if (BASIC_OP_UNREDEFINED_P(BOP_MAX, ARRAY_REDEFINED_OP_FLAG)) {
if (num == 0) {
return Qnil;
}
else {
struct cmp_opt_data cmp_opt = { 0, 0 };
VALUE result = Qundef;
rb_num_t i = num - 1;
result = ptr[i];
while (i-- > 0) {
const VALUE v = ptr[i];
if (result == Qundef || OPTIMIZED_CMP(v, result, cmp_opt) > 0) {
result = v;
}
}
return result == Qundef ? Qnil : result;
}
}
else {
VALUE ary = rb_ary_new4(num, ptr);
return rb_funcall(ary, idMax, 0);
}
}
static VALUE
vm_opt_newarray_min(rb_num_t num, const VALUE *ptr)
{
if (BASIC_OP_UNREDEFINED_P(BOP_MIN, ARRAY_REDEFINED_OP_FLAG)) {
if (num == 0) {
return Qnil;
}
else {
struct cmp_opt_data cmp_opt = { 0, 0 };
VALUE result = Qundef;
rb_num_t i = num - 1;
result = ptr[i];
while (i-- > 0) {
const VALUE v = ptr[i];
if (result == Qundef || OPTIMIZED_CMP(v, result, cmp_opt) < 0) {
result = v;
}
}
return result == Qundef ? Qnil : result;
}
}
else {
VALUE ary = rb_ary_new4(num, ptr);
return rb_funcall(ary, idMin, 0);
}
}
#undef id_cmp
static VALUE
vm_ic_hit_p(IC ic, const VALUE *reg_ep)
{
if (ic->ic_serial == GET_GLOBAL_CONSTANT_STATE() &&
(ic->ic_cref == NULL || ic->ic_cref == rb_vm_get_cref(reg_ep))) {
return ic->ic_value.value;
}
else {
return Qnil;
}
}
static void
vm_ic_update(IC ic, VALUE val, const VALUE *reg_ep)
{
VM_ASSERT(ic->ic_value.value != Qundef);
ic->ic_value.value = val;
ic->ic_serial = GET_GLOBAL_CONSTANT_STATE() - ruby_vm_const_missing_count;
ic->ic_cref = vm_get_const_key_cref(reg_ep);
ruby_vm_const_missing_count = 0;
}
static VALUE
vm_once_dispatch(ISEQ iseq, IC ic, rb_thread_t *th)
{
rb_thread_t *const RUNNING_THREAD_ONCE_DONE = (rb_thread_t *)(0x1);
union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)ic;
if (is->once.running_thread == RUNNING_THREAD_ONCE_DONE) {
return is->once.value;
}
else if (is->once.running_thread == NULL) {
VALUE val;
is->once.running_thread = th;
val = is->once.value = rb_ensure(vm_once_exec, (VALUE)iseq, vm_once_clear, (VALUE)is);
/* is->once.running_thread is cleared by vm_once_clear() */
is->once.running_thread = RUNNING_THREAD_ONCE_DONE; /* success */
rb_iseq_add_mark_object(th->cfp->iseq, val);
return val;
}
else if (is->once.running_thread == th) {
/* recursive once */
return vm_once_exec((VALUE)iseq);
}
else {
/* waiting for finish */
RUBY_VM_CHECK_INTS(th);
rb_thread_schedule();
return vm_once_dispatch(iseq, ic, th);
}
}
static OFFSET
vm_case_dispatch(CDHASH hash, OFFSET else_offset, VALUE key)
{
switch (OBJ_BUILTIN_TYPE(key)) {
case -1:
case T_FLOAT:
case T_SYMBOL:
case T_BIGNUM:
case T_STRING:
if (BASIC_OP_UNREDEFINED_P(BOP_EQQ,
SYMBOL_REDEFINED_OP_FLAG |
INTEGER_REDEFINED_OP_FLAG |
FLOAT_REDEFINED_OP_FLAG |
NIL_REDEFINED_OP_FLAG |
TRUE_REDEFINED_OP_FLAG |
FALSE_REDEFINED_OP_FLAG |
STRING_REDEFINED_OP_FLAG)) {
st_data_t val;
if (RB_FLOAT_TYPE_P(key)) {
double kval = RFLOAT_VALUE(key);
if (!isinf(kval) && modf(kval, &kval) == 0.0) {
key = FIXABLE(kval) ? LONG2FIX((long)kval) : rb_dbl2big(kval);
}
}
if (st_lookup(RHASH_TBL_RAW(hash), key, &val)) {
return FIX2INT((VALUE)val);
}
else {
return else_offset;
}
}
}
return 0;
}
static VALUE
vm_opt_plus(VALUE recv, VALUE obj)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS,INTEGER_REDEFINED_OP_FLAG)) {
return rb_fix_plus_fix(recv, obj);
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS, FLOAT_REDEFINED_OP_FLAG)) {
return DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj));
}
else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS, FLOAT_REDEFINED_OP_FLAG)) {
return DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj));
}
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
if (RBASIC_CLASS(recv) == rb_cString && RBASIC_CLASS(obj) == rb_cString &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS, STRING_REDEFINED_OP_FLAG)) {
return rb_str_plus(recv, obj);
}
else if (RBASIC_CLASS(recv) == rb_cArray &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS, ARRAY_REDEFINED_OP_FLAG)) {
return rb_ary_plus(recv, obj);
}
else {
return Qundef;
}
}
else {
return Qundef;
}
}
static VALUE
vm_opt_minus(VALUE recv, VALUE obj)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_MINUS, INTEGER_REDEFINED_OP_FLAG)) {
return rb_fix_minus_fix(recv, obj);
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_MINUS, FLOAT_REDEFINED_OP_FLAG)) {
return DBL2NUM(RFLOAT_VALUE(recv) - RFLOAT_VALUE(obj));
}
else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) &&
BASIC_OP_UNREDEFINED_P(BOP_MINUS, FLOAT_REDEFINED_OP_FLAG)) {
return DBL2NUM(RFLOAT_VALUE(recv) - RFLOAT_VALUE(obj));
}
else {
return Qundef;
}
}
static VALUE
vm_opt_mult(VALUE recv, VALUE obj)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_MULT, INTEGER_REDEFINED_OP_FLAG)) {
return rb_fix_mul_fix(recv, obj);
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) {
return DBL2NUM(RFLOAT_VALUE(recv) * RFLOAT_VALUE(obj));
}
else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) &&
BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) {
return DBL2NUM(RFLOAT_VALUE(recv) * RFLOAT_VALUE(obj));
}
else {
return Qundef;
}
}
static VALUE
vm_opt_div(VALUE recv, VALUE obj)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_DIV, INTEGER_REDEFINED_OP_FLAG)) {
if (FIX2LONG(obj) == 0) {
return Qundef;
}
else {
return rb_fix_div_fix(recv, obj);
}
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_DIV, FLOAT_REDEFINED_OP_FLAG)) {
return DBL2NUM(RFLOAT_VALUE(recv) / RFLOAT_VALUE(obj));
}
else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) &&
BASIC_OP_UNREDEFINED_P(BOP_DIV, FLOAT_REDEFINED_OP_FLAG)) {
return DBL2NUM(RFLOAT_VALUE(recv) / RFLOAT_VALUE(obj));
}
else {
return Qundef;
}
}
static VALUE
vm_opt_mod(VALUE recv, VALUE obj)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_MOD, INTEGER_REDEFINED_OP_FLAG )) {
if (FIX2LONG(obj) == 0) {
return Qundef;
}
else {
return rb_fix_mod_fix(recv, obj);
}
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_MOD, FLOAT_REDEFINED_OP_FLAG)) {
return DBL2NUM(ruby_float_mod(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)));
}
else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) &&
BASIC_OP_UNREDEFINED_P(BOP_MOD, FLOAT_REDEFINED_OP_FLAG)) {
return DBL2NUM(ruby_float_mod(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)));
}
else {
return Qundef;
}
}
static inline int
vm_method_cfunc_is(CALL_INFO ci, CALL_CACHE cc,
VALUE recv, VALUE (*func)())
{
vm_search_method(ci, cc, recv);
return check_cfunc(cc->me, func);
}
static VALUE
vm_opt_neq(CALL_INFO ci, CALL_CACHE cc,
CALL_INFO ci_eq, CALL_CACHE cc_eq,
VALUE recv, VALUE obj)
{
if (vm_method_cfunc_is(ci, cc, recv, rb_obj_not_equal)) {
VALUE val = opt_eq_func(recv, obj, ci_eq, cc_eq);
if (val != Qundef) {
return RTEST(val) ? Qfalse : Qtrue;
}
}
return Qundef;
}
static VALUE
vm_opt_lt(VALUE recv, VALUE obj)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_LT, INTEGER_REDEFINED_OP_FLAG)) {
SIGNED_VALUE a = recv, b = obj;
if (a < b) {
return Qtrue;
}
else {
return Qfalse;
}
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_LT, FLOAT_REDEFINED_OP_FLAG)) {
/* flonum is not NaN */
return RFLOAT_VALUE(recv) < RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
}
else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) &&
BASIC_OP_UNREDEFINED_P(BOP_LT, FLOAT_REDEFINED_OP_FLAG)) {
return double_cmp_lt(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj));
}
else {
return Qundef;
}
}
static VALUE
vm_opt_le(VALUE recv, VALUE obj)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_LE, INTEGER_REDEFINED_OP_FLAG)) {
SIGNED_VALUE a = recv, b = obj;
if (a <= b) {
return Qtrue;
}
else {
return Qfalse;
}
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_LE, FLOAT_REDEFINED_OP_FLAG)) {
/* flonum is not NaN */
return RFLOAT_VALUE(recv) <= RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
}
else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) &&
BASIC_OP_UNREDEFINED_P(BOP_LT, FLOAT_REDEFINED_OP_FLAG)) {
return double_cmp_le(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj));
}
else {
return Qundef;
}
}
static VALUE
vm_opt_gt(VALUE recv, VALUE obj)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_GT, INTEGER_REDEFINED_OP_FLAG)) {
SIGNED_VALUE a = recv, b = obj;
if (a > b) {
return Qtrue;
}
else {
return Qfalse;
}
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) {
/* flonum is not NaN */
return RFLOAT_VALUE(recv) > RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
}
else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) &&
BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) {
return double_cmp_gt(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj));
}
else {
return Qundef;
}
}
static VALUE
vm_opt_ge(VALUE recv, VALUE obj)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_GE, INTEGER_REDEFINED_OP_FLAG)) {
SIGNED_VALUE a = recv, b = obj;
if (a >= b) {
return Qtrue;
}
else {
return Qfalse;
}
}
else if (FLONUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_GE, FLOAT_REDEFINED_OP_FLAG)) {
/* flonum is not NaN */
return RFLOAT_VALUE(recv) >= RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
}
else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) &&
BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) {
return double_cmp_ge(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj));
}
else {
return Qundef;
}
}
static VALUE
vm_opt_ltlt(VALUE recv, VALUE obj)
{
if (SPECIAL_CONST_P(recv)) {
return Qundef;
}
else if (RBASIC_CLASS(recv) == rb_cString &&
BASIC_OP_UNREDEFINED_P(BOP_LTLT, STRING_REDEFINED_OP_FLAG)) {
return rb_str_concat(recv, obj);
}
else if (RBASIC_CLASS(recv) == rb_cArray &&
BASIC_OP_UNREDEFINED_P(BOP_LTLT, ARRAY_REDEFINED_OP_FLAG)) {
return rb_ary_push(recv, obj);
}
else {
return Qundef;
}
}
static VALUE
vm_opt_aref(VALUE recv, VALUE obj)
{
if (SPECIAL_CONST_P(recv)) {
return Qundef;
}
else if (RBASIC_CLASS(recv) == rb_cArray &&
BASIC_OP_UNREDEFINED_P(BOP_AREF, ARRAY_REDEFINED_OP_FLAG) &&
FIXNUM_P(obj)) {
return rb_ary_entry(recv, FIX2LONG(obj));
}
else if (RBASIC_CLASS(recv) == rb_cHash &&
BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG)) {
return rb_hash_aref(recv, obj);
}
else {
return Qundef;
}
}
static VALUE
vm_opt_aset(VALUE recv, VALUE obj, VALUE set)
{
if (SPECIAL_CONST_P(recv)) {
return Qundef;
}
else if (RBASIC_CLASS(recv) == rb_cArray &&
BASIC_OP_UNREDEFINED_P(BOP_ASET, ARRAY_REDEFINED_OP_FLAG) &&
FIXNUM_P(obj)) {
rb_ary_store(recv, FIX2LONG(obj), set);
return set;
}
else if (RBASIC_CLASS(recv) == rb_cHash &&
BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG)) {
rb_hash_aset(recv, obj, set);
return set;
}
else {
return Qundef;
}
}
static VALUE
vm_opt_aref_with(VALUE recv, VALUE key)
{
if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash &&
BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG) &&
rb_hash_compare_by_id_p(recv) == Qfalse) {
return rb_hash_aref(recv, key);
}
else {
return Qundef;
}
}
static VALUE
vm_opt_aset_with(VALUE recv, VALUE key, VALUE val)
{
if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash &&
BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG) &&
rb_hash_compare_by_id_p(recv) == Qfalse) {
return rb_hash_aset(recv, key, val);
}
else {
return Qundef;
}
}
static VALUE
vm_opt_length(VALUE recv, int bop)
{
if (SPECIAL_CONST_P(recv)) {
return Qundef;
}
else if (RBASIC_CLASS(recv) == rb_cString &&
BASIC_OP_UNREDEFINED_P(bop, STRING_REDEFINED_OP_FLAG)) {
if (bop == BOP_EMPTY_P) {
return LONG2NUM(RSTRING_LEN(recv));
}
else {
return rb_str_length(recv);
}
}
else if (RBASIC_CLASS(recv) == rb_cArray &&
BASIC_OP_UNREDEFINED_P(bop, ARRAY_REDEFINED_OP_FLAG)) {
return LONG2NUM(RARRAY_LEN(recv));
}
else if (RBASIC_CLASS(recv) == rb_cHash &&
BASIC_OP_UNREDEFINED_P(bop, HASH_REDEFINED_OP_FLAG)) {
return INT2FIX(RHASH_SIZE(recv));
}
else {
return Qundef;
}
}
static VALUE
vm_opt_empty_p(VALUE recv)
{
switch (vm_opt_length(recv, BOP_EMPTY_P)) {
case Qundef: return Qundef;
case INT2FIX(0): return Qtrue;
default: return Qfalse;
}
}
static VALUE
vm_opt_succ(VALUE recv)
{
if (SPECIAL_CONST_P(recv)) {
if (FIXNUM_P(recv) &&
BASIC_OP_UNREDEFINED_P(BOP_SUCC, INTEGER_REDEFINED_OP_FLAG)) {
/* fixnum + INT2FIX(1) */
if (recv == LONG2FIX(FIXNUM_MAX)) {
return LONG2NUM(FIXNUM_MAX + 1);
}
else {
return recv - 1 + INT2FIX(1);
}
}
else {
return Qundef;
}
}
else {
if (RBASIC_CLASS(recv) == rb_cString &&
BASIC_OP_UNREDEFINED_P(BOP_SUCC, STRING_REDEFINED_OP_FLAG)) {
return rb_str_succ(recv);
}
else {
return Qundef;
}
}
}
static VALUE
vm_opt_not(CALL_INFO ci, CALL_CACHE cc, VALUE recv)
{
if (vm_method_cfunc_is(ci, cc, recv, rb_obj_not)) {
return RTEST(recv) ? Qfalse : Qtrue;
}
else {
return Qundef;
}
}
static VALUE
vm_opt_regexpmatch1(VALUE recv, VALUE obj)
{
if (BASIC_OP_UNREDEFINED_P(BOP_MATCH, REGEXP_REDEFINED_OP_FLAG)) {
return rb_reg_match(recv, obj);
}
else {
return rb_funcall(recv, idEqTilde, 1, obj);
}
}
static VALUE
vm_opt_regexpmatch2(VALUE recv, VALUE obj)
{
if (CLASS_OF(obj) == rb_cString &&
BASIC_OP_UNREDEFINED_P(BOP_MATCH, STRING_REDEFINED_OP_FLAG)) {
return rb_reg_match(recv, obj);
}
else {
return Qundef;
}
}