ruby-changes:69131
From: Kevin <ko1@a...>
Date: Thu, 21 Oct 2021 08:20:57 +0900 (JST)
Subject: [ruby-changes:69131] bfde30c326 (master): Implement expandarray
https://git.ruby-lang.org/ruby.git/commit/?id=bfde30c326 From bfde30c326788c614ea72c76b9ae22cfb5eef950 Mon Sep 17 00:00:00 2001 From: Kevin Deisz <kevin.deisz@g...> Date: Wed, 7 Jul 2021 14:45:48 -0400 Subject: Implement expandarray --- yjit.rb | 1 + yjit_codegen.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yjit_iface.h | 5 +++ 3 files changed, 122 insertions(+) diff --git a/yjit.rb b/yjit.rb index cf5cac5f2f..20668663a3 100644 --- a/yjit.rb +++ b/yjit.rb @@ -151,6 +151,7 @@ module YJIT https://github.com/ruby/ruby/blob/trunk/yjit.rb#L151 print_counters(stats, prefix: 'getivar_', prompt: 'getinstancevariable exit reasons:') print_counters(stats, prefix: 'setivar_', prompt: 'setinstancevariable exit reasons:') print_counters(stats, prefix: 'oaref_', prompt: 'opt_aref exit reasons: ') + print_counters(stats, prefix: 'expandarray_', prompt: 'expandarray exit reasons: ') total_exits = total_exit_count(stats) diff --git a/yjit_codegen.c b/yjit_codegen.c index b9226cf738..4147853f20 100644 --- a/yjit_codegen.c +++ b/yjit_codegen.c @@ -720,6 +720,118 @@ gen_splatarray(jitstate_t* jit, ctx_t* ctx) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L720 return YJIT_KEEP_COMPILING; } +static void +guard_object_is_heap(codeblock_t *cb, ctx_t *ctx, uint8_t *side_exit, x86opnd_t object_opnd) +{ + ADD_COMMENT(cb, "guard object is heap"); + + // Test that the object is not an immediate + test(cb, object_opnd, imm_opnd(RUBY_IMMEDIATE_MASK)); + jnz_ptr(cb, side_exit); + + // Test that the object is not false or nil + cmp(cb, object_opnd, imm_opnd(Qnil)); + RUBY_ASSERT(Qfalse < Qnil); + jbe_ptr(cb, side_exit); +} + +static inline void +guard_object_is_array(codeblock_t *cb, x86opnd_t object_opnd, x86opnd_t flags_opnd, ctx_t *ctx, uint8_t *side_exit) +{ + guard_object_is_heap(cb, ctx, side_exit, object_opnd); + ADD_COMMENT(cb, "guard object is array"); + + // Pull out the type mask + mov(cb, flags_opnd, member_opnd(object_opnd, struct RBasic, flags)); + and(cb, flags_opnd, imm_opnd(RUBY_T_MASK)); + + // Compare the result with T_ARRAY + cmp(cb, flags_opnd, imm_opnd(T_ARRAY)); + jne_ptr(cb, side_exit); +} + +// push enough nils onto the stack to fill out an array +static codegen_status_t +gen_expandarray(jitstate_t* jit, ctx_t* ctx) +{ + int flag = (int) jit_get_arg(jit, 1); + + // If this instruction has the splat flag, then bail out. + if (flag & 0x01) { + GEN_COUNTER_INC(cb, expandarray_splat); + return YJIT_CANT_COMPILE; + } + + // If this instruction has the postarg flag, then bail out. + if (flag & 0x02) { + GEN_COUNTER_INC(cb, expandarray_postarg); + return YJIT_CANT_COMPILE; + } + + // num is the number of requested values. If there aren't enough in the + // array then we're going to push on nils. + rb_num_t num = (rb_num_t) jit_get_arg(jit, 0); + + // If we don't actually want any values, then just return. + if (num == 0) { + return YJIT_KEEP_COMPILING; + } + + uint8_t *side_exit = yjit_side_exit(jit, ctx); + + // Move the array from the stack into REG0 and check that it's an array. + mov(cb, REG0, ctx_stack_pop(ctx, 1)); + guard_object_is_array(cb, REG0, REG1, ctx, COUNTED_EXIT(side_exit, expandarray_not_array)); + + uint32_t embedded_length_label = cb_new_label(cb, "ea_embedded"); + uint32_t push_values_label = cb_new_label(cb, "ea_push_vals"); + + // Pull out the embed flag to check if it's an embedded array. + mov(cb, REG1, member_opnd(REG0, struct RBasic, flags)); + and(cb, REG1, imm_opnd(RARRAY_EMBED_FLAG)); + jmp_label(cb, embedded_length_label); + + // Pull out the length of the heap array and write it to REG1. + mov(cb, REG1, member_opnd(REG0, struct RArray, as.heap.len)); + jmp_label(cb, push_values_label); + + // Pull out the length of the embedded array and write it to REG1. + cb_write_label(cb, embedded_length_label); + mov(cb, REG1, member_opnd(REG0, struct RBasic, flags)); + and(cb, REG1, imm_opnd(RARRAY_EMBED_LEN_MASK)); + shr(cb, REG1, imm_opnd(RARRAY_EMBED_LEN_SHIFT)); + + // Only handle the case where the number of values in the array is greater + // than or equal to the number of values requested. + cb_write_label(cb, push_values_label); + cmp(cb, REG1, imm_opnd(num)); + jl_ptr(cb, COUNTED_EXIT(side_exit, expandarray_not_equal_len)); + + // Once more, compare if it is an embedded array to use for cmovs. + mov(cb, REG1, member_opnd(REG0, struct RBasic, flags)); + test(cb, REG1, imm_opnd(RARRAY_EMBED_FLAG)); + + // If the last comparison was not 0, then we have an embedded array, so + // we're going to get the values from the embedded array. + // (struct RArray *)(obj)->as.ary + lea(cb, REG1, member_opnd(REG0, struct RArray, as.ary)); + + // If the last comparison was 0, then we don't have an embedded array, + // so we're going to get the values off the heap. + // (struct RArray *)(obj)->as.heap.ptr + cmovz(cb, REG1, member_opnd(REG0, struct RArray, as.heap.ptr)); + + // Loop backward through the array and push each element onto the stack. + for (int32_t i = (int32_t) num - 1; i >= 0; i--) { + x86opnd_t top = ctx_stack_push(ctx, TYPE_UNKNOWN); + mov(cb, REG0, mem_opnd(64, REG1, i * SIZEOF_VALUE)); + mov(cb, top, REG0); + } + + cb_link_labels(cb); + return YJIT_KEEP_COMPILING; +} + // new hash initialized from top N values static codegen_status_t gen_newhash(jitstate_t* jit, ctx_t* ctx) @@ -3499,7 +3611,11 @@ yjit_init_codegen(void) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L3611 yjit_reg_op(BIN(adjuststack), gen_adjuststack); yjit_reg_op(BIN(newarray), gen_newarray); yjit_reg_op(BIN(duparray), gen_duparray); +<<<<<<< HEAD yjit_reg_op(BIN(splatarray), gen_splatarray); +======= + yjit_reg_op(BIN(expandarray), gen_expandarray); +>>>>>>> dc01eb5c5e (Implement expandarray) yjit_reg_op(BIN(newhash), gen_newhash); yjit_reg_op(BIN(concatstrings), gen_concatstrings); yjit_reg_op(BIN(putnil), gen_putnil); diff --git a/yjit_iface.h b/yjit_iface.h index cdeef11765..6afedb47fe 100644 --- a/yjit_iface.h +++ b/yjit_iface.h @@ -71,6 +71,11 @@ YJIT_DECLARE_COUNTERS( https://github.com/ruby/ruby/blob/trunk/yjit_iface.h#L71 vm_insns_count, compiled_iseq_count, + expandarray_splat, + expandarray_postarg, + expandarray_not_array, + expandarray_not_equal_len, + // Member with known name for iterating over counters last_member ) -- cgit v1.2.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/