aboutsummaryrefslogtreecommitdiffstats
path: root/vm_insnhelper.c
diff options
context:
space:
mode:
authorshyouhei <shyouhei@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-04-18 10:58:49 +0000
committershyouhei <shyouhei@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-04-18 10:58:49 +0000
commit7c3f93686f9b3898effc17402cc7077c3b930bc6 (patch)
treecd4b2970543ff3b3740dbd1db50b46fc2202c891 /vm_insnhelper.c
parent730d4f18143d644684cab86e1bd3be1b288a4f55 (diff)
downloadruby-7c3f93686f9b3898effc17402cc7077c3b930bc6.tar.gz
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
Diffstat (limited to 'vm_insnhelper.c')
-rw-r--r--vm_insnhelper.c866
1 files changed, 866 insertions, 0 deletions
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index fd259fd428..a2367876dc 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -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;
+ }
+}
+