vm_insnhelper.c: merge opt_eq_func / opt_eql_func

These two function were almost identical, except in case of
T_STRING/T_FLOAT. Why not merge them into one, and let the difference be
handled in normal method calls (slowpath).  This does not improve
runtime performance for me, but at least reduces for instance rb_eql_opt
from 653 bytes to 86 bytes on my machine, according to nm(1).
This commit is contained in:
卜部昌平 2020-05-29 17:42:23 +09:00
parent 27bef64862
commit 40ced763b4
Notes: git 2020-06-02 12:38:25 +09:00
2 changed files with 49 additions and 79 deletions

View File

@ -1168,7 +1168,7 @@ opt_eq
(VALUE recv, VALUE obj) (VALUE recv, VALUE obj)
(VALUE val) (VALUE val)
{ {
val = opt_eq_func(GET_ISEQ(), recv, obj, cd); val = opt_equality(GET_ISEQ(), recv, obj, cd);
if (val == Qundef) { if (val == Qundef) {
CALL_SIMPLE_METHOD(); CALL_SIMPLE_METHOD();

View File

@ -1669,17 +1669,6 @@ vm_method_cfunc_is(const rb_iseq_t *iseq, CALL_DATA cd, VALUE recv, VALUE (*func
return check_cfunc(vm_cc_cme(cd->cc), func); return check_cfunc(vm_cc_cme(cd->cc), func);
} }
static VALUE
opt_equal_fallback(const rb_iseq_t *iseq, VALUE recv, VALUE obj, CALL_DATA cd)
{
if (vm_method_cfunc_is(iseq, cd, recv, rb_obj_equal)) {
return recv == obj ? Qtrue : Qfalse;
}
return Qundef;
}
#define BUILTIN_CLASS_P(x, k) (!SPECIAL_CONST_P(x) && RBASIC_CLASS(x) == k)
#define EQ_UNREDEFINED_P(t) BASIC_OP_UNREDEFINED_P(BOP_EQ, t##_REDEFINED_OP_FLAG) #define EQ_UNREDEFINED_P(t) BASIC_OP_UNREDEFINED_P(BOP_EQ, t##_REDEFINED_OP_FLAG)
static inline bool static inline bool
@ -1711,83 +1700,64 @@ FLONUM_2_P(VALUE a, VALUE b)
#endif #endif
} }
/* 1: compare by identity, 0: not applicable, -1: redefined */ static VALUE
static inline int opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd)
comparable_by_identity(VALUE recv, VALUE obj)
{ {
if (FIXNUM_2_P(recv, obj)) { if (FIXNUM_2_P(recv, obj) && EQ_UNREDEFINED_P(INTEGER)) {
return (EQ_UNREDEFINED_P(INTEGER) != 0) * 2 - 1; goto compare_by_identity;
} }
if (FLONUM_2_P(recv, obj)) { else if (FLONUM_2_P(recv, obj) && EQ_UNREDEFINED_P(FLOAT)) {
return (EQ_UNREDEFINED_P(FLOAT) != 0) * 2 - 1; goto compare_by_identity;
} }
if (SYMBOL_P(recv) && SYMBOL_P(obj)) { else if (STATIC_SYM_P(recv) && STATIC_SYM_P(obj) && EQ_UNREDEFINED_P(SYMBOL)) {
return (EQ_UNREDEFINED_P(SYMBOL) != 0) * 2 - 1; goto compare_by_identity;
} }
return 0; else if (SPECIAL_CONST_P(recv)) {
} goto compare_by_funcall;
}
else if (RBASIC_CLASS(recv) == rb_cFloat && RB_FLOAT_TYPE_P(obj) && EQ_UNREDEFINED_P(FLOAT)) {
double a = RFLOAT_VALUE(recv);
double b = RFLOAT_VALUE(obj);
static #if MSC_VERSION_BEFORE(1300)
#ifndef NO_BIG_INLINE if (isnan(a)) {
inline return Qfalse;
}
else if (isnan(b)) {
return Qfalse;
}
else
#endif #endif
VALUE if (a == b) {
opt_eq_func(const rb_iseq_t *iseq, VALUE recv, VALUE obj, CALL_DATA cd) return Qtrue;
{ }
switch (comparable_by_identity(recv, obj)) { else {
case 1: return Qfalse;
return (recv == obj) ? Qtrue : Qfalse; }
case -1:
goto fallback;
} }
if (0) { else if (RBASIC_CLASS(recv) == rb_cString && EQ_UNREDEFINED_P(STRING)) {
} if (recv == obj) {
else if (BUILTIN_CLASS_P(recv, rb_cFloat)) { return Qtrue;
if (EQ_UNREDEFINED_P(FLOAT)) { }
return rb_float_equal(recv, obj); else if (RB_TYPE_P(obj, T_STRING)) {
} return rb_str_eql_internal(obj, recv);
}
else if (BUILTIN_CLASS_P(recv, rb_cString) && EQ_UNREDEFINED_P(STRING)) {
if (recv == obj) return Qtrue;
if (RB_TYPE_P(obj, T_STRING)) {
return rb_str_eql_internal(recv, obj);
} }
} }
fallback: compare_by_funcall:
return opt_equal_fallback(iseq, recv, obj, cd); if (! vm_method_cfunc_is(cd_owner, cd, recv, rb_obj_equal)) {
} return Qundef;
static
#ifndef NO_BIG_INLINE
inline
#endif
VALUE
opt_eql_func(VALUE recv, VALUE obj, CALL_DATA cd)
{
switch (comparable_by_identity(recv, obj)) {
case 1:
return (recv == obj) ? Qtrue : Qfalse;
case -1:
goto fallback;
}
if (0) {
}
else if (BUILTIN_CLASS_P(recv, rb_cFloat)) {
if (EQ_UNREDEFINED_P(FLOAT)) {
return rb_float_eql(recv, obj);
}
}
else if (BUILTIN_CLASS_P(recv, rb_cString)) {
if (EQ_UNREDEFINED_P(STRING)) {
return rb_str_eql(recv, obj);
}
} }
fallback: compare_by_identity:
return opt_equal_fallback(NULL, recv, obj, cd); if (recv == obj) {
return Qtrue;
}
else {
return Qfalse;
}
} }
#undef BUILTIN_CLASS_P
#undef EQ_UNREDEFINED_P #undef EQ_UNREDEFINED_P
#define vm_ci_new_id(mid) vm_ci_new_runtime(mid, 0, 0, NULL) #define vm_ci_new_id(mid) vm_ci_new_runtime(mid, 0, 0, NULL)
@ -1802,14 +1772,14 @@ rb_equal_opt(VALUE obj1, VALUE obj2)
} }
struct rb_call_data cd = { .ci = ci, .cc = vm_cc_empty() }; struct rb_call_data cd = { .ci = ci, .cc = vm_cc_empty() };
return opt_eq_func(NULL, obj1, obj2, &cd); return opt_equality(NULL, obj1, obj2, &cd);
} }
VALUE VALUE
rb_eql_opt(VALUE obj1, VALUE obj2) rb_eql_opt(VALUE obj1, VALUE obj2)
{ {
struct rb_call_data cd = { .ci = vm_ci_new_id(idEqlP), .cc = vm_cc_empty() }; struct rb_call_data cd = { .ci = vm_ci_new_id(idEqlP), .cc = vm_cc_empty() };
return opt_eql_func(obj1, obj2, &cd); return opt_equality(NULL, obj1, obj2, &cd);
} }
extern VALUE rb_vm_call0(rb_execution_context_t *ec, VALUE, ID, int, const VALUE*, const rb_callable_method_entry_t *, int kw_splat); extern VALUE rb_vm_call0(rb_execution_context_t *ec, VALUE, ID, int, const VALUE*, const rb_callable_method_entry_t *, int kw_splat);
@ -4448,7 +4418,7 @@ static VALUE
vm_opt_neq(const rb_iseq_t *iseq, CALL_DATA cd, CALL_DATA cd_eq, VALUE recv, VALUE obj) vm_opt_neq(const rb_iseq_t *iseq, CALL_DATA cd, CALL_DATA cd_eq, VALUE recv, VALUE obj)
{ {
if (vm_method_cfunc_is(iseq, cd, recv, rb_obj_not_equal)) { if (vm_method_cfunc_is(iseq, cd, recv, rb_obj_not_equal)) {
VALUE val = opt_eq_func(iseq, recv, obj, cd_eq); VALUE val = opt_equality(iseq, recv, obj, cd_eq);
if (val != Qundef) { if (val != Qundef) {
return RTEST(val) ? Qfalse : Qtrue; return RTEST(val) ? Qfalse : Qtrue;