ruby-changes:69138
From: John <ko1@a...>
Date: Thu, 21 Oct 2021 08:20:58 +0900 (JST)
Subject: [ruby-changes:69138] 9ebcd576f3 (master): String and fixnum equality
https://git.ruby-lang.org/ruby.git/commit/?id=9ebcd576f3 From 9ebcd576f367280c60064bc98fe35b1f2fb27e2b Mon Sep 17 00:00:00 2001 From: John Hawthorn <john@h...> Date: Fri, 27 Aug 2021 18:35:34 -0700 Subject: String and fixnum equality --- bootstraptest/test_yjit.rb | 26 ++++++++++++++ yjit_codegen.c | 90 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 114 insertions(+), 2 deletions(-) diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index 866d7e2558..8ddff90050 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -1950,3 +1950,29 @@ assert_equal '42', %q{ https://github.com/ruby/ruby/blob/trunk/bootstraptest/test_yjit.rb#L1950 ractor.take } + +# Test equality with changing types +assert_equal '[true, false, false, false]', %q{ + def eq(a, b) + a == b + end + + [ + eq("foo", "foo"), + eq("foo", "bar"), + eq(:foo, "bar"), + eq("foo", :bar) + ] +} + +# Redefined eq +assert_equal 'true', %q{ + class String + def ==(other) + true + end + end + + "foo" == "bar" + "foo" == "bar" +} diff --git a/yjit_codegen.c b/yjit_codegen.c index 22901ab2ab..23d3a30d6e 100644 --- a/yjit_codegen.c +++ b/yjit_codegen.c @@ -2124,14 +2124,100 @@ gen_opt_gt(jitstate_t* jit, ctx_t* ctx) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L2124 return gen_fixnum_cmp(jit, ctx, cmovg); } -VALUE rb_opt_equality_specialized(VALUE recv, VALUE obj); +// Implements specialized equality for either two fixnum or two strings +// Returns true if code was generated, otherwise false +bool +gen_equality_specialized(jitstate_t* jit, ctx_t* ctx, uint8_t *side_exit) +{ + VALUE comptime_a = jit_peek_at_stack(jit, ctx, 1); + VALUE comptime_b = jit_peek_at_stack(jit, ctx, 0); + + x86opnd_t a_opnd = ctx_stack_opnd(ctx, 1); + x86opnd_t b_opnd = ctx_stack_opnd(ctx, 0); + + if (FIXNUM_P(comptime_a) && FIXNUM_P(comptime_b)) { + if (!assume_bop_not_redefined(jit->block, INTEGER_REDEFINED_OP_FLAG, BOP_EQ)) { + return YJIT_CANT_COMPILE; + } + + guard_two_fixnums(ctx, side_exit); + + mov(cb, REG0, a_opnd); + cmp(cb, REG0, b_opnd); + + mov(cb, REG0, imm_opnd(Qfalse)); + mov(cb, REG1, imm_opnd(Qtrue)); + cmove(cb, REG0, REG1); + + // Push the output on the stack + ctx_stack_pop(ctx, 2); + x86opnd_t dst = ctx_stack_push(ctx, TYPE_IMM); + mov(cb, dst, REG0); + + return true; + } else if (CLASS_OF(comptime_a) == rb_cString && + CLASS_OF(comptime_b) == rb_cString) { + if (!assume_bop_not_redefined(jit->block, STRING_REDEFINED_OP_FLAG, BOP_EQ)) { + return YJIT_CANT_COMPILE; + } + + // Guard that a is a String + mov(cb, REG0, a_opnd); + jit_guard_known_klass(jit, ctx, rb_cString, OPND_STACK(1), comptime_a, SEND_MAX_DEPTH, side_exit); + + uint32_t ret = cb_new_label(cb, "ret"); + + // If they are equal by identity, return true + mov(cb, REG0, b_opnd); + cmp(cb, REG0, a_opnd); + mov(cb, REG0, imm_opnd(Qtrue)); + je_label(cb, ret); + + // Otherwise guard that b is a T_STRING (from type info) or String (from runtime guard) + if (ctx_get_opnd_type(ctx, OPND_STACK(0)).type != ETYPE_STRING) { + mov(cb, REG0, b_opnd); + // Note: any T_STRING is valid here, but we check for a ::String for simplicity + jit_guard_known_klass(jit, ctx, rb_cString, OPND_STACK(0), comptime_b, SEND_MAX_DEPTH, side_exit); + } + + // Call rb_str_eql_internal(a, b) + mov(cb, C_ARG_REGS[0], a_opnd); + mov(cb, C_ARG_REGS[1], b_opnd); + call_ptr(cb, REG0, (void *)rb_str_eql_internal); + + // Push the output on the stack + cb_write_label(cb, ret); + ctx_stack_pop(ctx, 2); + x86opnd_t dst = ctx_stack_push(ctx, TYPE_IMM); + mov(cb, dst, RAX); + cb_link_labels(cb); + + return true; + } else { + return false; + } +} static codegen_status_t gen_opt_send_without_block(jitstate_t *jit, ctx_t *ctx); static codegen_status_t gen_opt_eq(jitstate_t* jit, ctx_t* ctx) { - return gen_opt_send_without_block(jit, ctx); + // Defer compilation so we can specialize base on a runtime receiver + if (!jit_at_current_insn(jit)) { + defer_compilation(jit->block, jit->insn_idx, ctx); + return YJIT_END_BLOCK; + } + + // Create a size-exit to fall back to the interpreter + uint8_t *side_exit = yjit_side_exit(jit, ctx); + + if (gen_equality_specialized(jit, ctx, side_exit)) { + jit_jump_to_next_insn(jit, ctx); + return YJIT_END_BLOCK; + } else { + return gen_opt_send_without_block(jit, ctx); + } } static codegen_status_t gen_send_general(jitstate_t *jit, ctx_t *ctx, struct rb_call_data *cd, rb_iseq_t *block); -- cgit v1.2.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/