diff options
author | Alan Wu <[email protected]> | 2021-01-27 16:13:27 -0500 |
---|---|---|
committer | Alan Wu <[email protected]> | 2021-10-20 18:19:28 -0400 |
commit | 3c7251b41b8850aab7c64c4885669e9c099d2465 (patch) | |
tree | 2a71261442d0d44ae03a8877e721257d6b38c198 | |
parent | 09479c33f54423910674780d1fcb7834e98ef561 (diff) |
Tally instructions when taking side exists for --ujit-stats
shopify/ruby#29
Co-authored-by: Maxime Chevalier-Boisvert <[email protected]>
-rwxr-xr-x | tool/runruby.rb | 5 | ||||
-rw-r--r-- | ujit_asm.c | 39 | ||||
-rw-r--r-- | ujit_asm.h | 3 | ||||
-rw-r--r-- | ujit_codegen.c | 5 | ||||
-rw-r--r-- | ujit_iface.c | 95 | ||||
-rw-r--r-- | ujit_iface.h | 2 |
6 files changed, 137 insertions, 12 deletions
diff --git a/tool/runruby.rb b/tool/runruby.rb index 9562efc00d..835e772735 100755 --- a/tool/runruby.rb +++ b/tool/runruby.rb @@ -11,6 +11,8 @@ when ENV['RUNRUBY_USE_GDB'] == 'true' debugger = :gdb when ENV['RUNRUBY_USE_LLDB'] == 'true' debugger = :lldb +when ENV['RUNRUBY_UJIT_STATS'] + use_ujit_stat = true end while arg = ARGV[0] break ARGV.shift if arg == '--' @@ -164,6 +166,9 @@ if debugger end cmd = [runner || ruby] +if use_ujit_stat + cmd << '--ujit-stats' +end cmd.concat(ARGV) cmd.unshift(*precommand) unless precommand.empty? diff --git a/ujit_asm.c b/ujit_asm.c index 231e83b5c8..a5d7f480bc 100644 --- a/ujit_asm.c +++ b/ujit_asm.c @@ -54,6 +54,45 @@ x86opnd_t mem_opnd(uint32_t num_bits, x86opnd_t base_reg, int32_t disp) return opnd; } +x86opnd_t mem_opnd_sib(uint32_t num_bits, x86opnd_t base_reg, x86opnd_t index_reg, int32_t scale, int32_t disp) +{ + uint8_t scale_exp; + switch (scale) { + case 8: + scale_exp = 3; + break; + case 4: + scale_exp = 2; + break; + case 2: + scale_exp = 1; + break; + case 1: + scale_exp = 0; + break; + default: + assert(false && "scale not one of 1,2,4,8"); + break; + } + + bool is_iprel = base_reg.as.reg.reg_type == REG_IP; + + x86opnd_t opnd = { + OPND_MEM, + num_bits, + .as.mem = { + .base_reg_no = base_reg.as.reg.reg_no, + .idx_reg_no = index_reg.as.reg.reg_no, + .has_idx = 1, + .scale_exp = scale_exp, + .is_iprel = is_iprel, + .disp = disp + } + }; + + return opnd; +} + x86opnd_t resize_opnd(x86opnd_t opnd, uint32_t num_bits) { assert (num_bits % 8 == 0); diff --git a/ujit_asm.h b/ujit_asm.h index 278919bfdb..98f1b9877b 100644 --- a/ujit_asm.h +++ b/ujit_asm.h @@ -216,6 +216,9 @@ static const x86opnd_t R15B = { OPND_REG, 8, .as.reg = { REG_GP, 15 }}; // Memory operand with base register and displacement/offset x86opnd_t mem_opnd(uint32_t num_bits, x86opnd_t base_reg, int32_t disp); +// Scale-index-base memory operand +x86opnd_t mem_opnd_sib(uint32_t num_bits, x86opnd_t base_reg, x86opnd_t index_reg, int32_t scale, int32_t disp); + // Immediate number operand x86opnd_t imm_opnd(int64_t val); diff --git a/ujit_codegen.c b/ujit_codegen.c index e45005a70e..43da3e6473 100644 --- a/ujit_codegen.c +++ b/ujit_codegen.c @@ -66,6 +66,11 @@ ujit_gen_exit(jitstate_t* jit, ctx_t* ctx, codeblock_t* cb, VALUE* exit_pc) mov(cb, RAX, const_ptr_opnd(exit_pc)); mov(cb, member_opnd(REG_CFP, rb_control_frame_t, pc), RAX); +#if RUBY_DEBUG + mov(cb, RDI, const_ptr_opnd(exit_pc)); + call_ptr(cb, RSI, (void *)&rb_ujit_count_side_exit_op); +#endif + // Write the post call bytes cb_write_post_call_bytes(cb); } diff --git a/ujit_iface.c b/ujit_iface.c index 2237d37f5f..39ea11da4f 100644 --- a/ujit_iface.c +++ b/ujit_iface.c @@ -27,6 +27,7 @@ bool rb_ujit_enabled; static int64_t vm_insns_count = 0; int64_t rb_ujit_exec_insns_count = 0; +static int64_t exit_op_count[VM_INSTRUCTION_SIZE] = { 0 }; extern st_table * version_tbl; extern codeblock_t *cb; @@ -416,25 +417,95 @@ ujit_disasm(VALUE self, VALUE code, VALUE from) } #endif -__attribute__((destructor)) + +#if RUBY_DEBUG +// implementation for --ujit-stats + +void +rb_ujit_collect_vm_usage_insn(int insn) +{ + vm_insns_count++; +} + +const VALUE * +rb_ujit_count_side_exit_op(const VALUE *exit_pc) +{ + int insn = rb_vm_insn_addr2opcode((const void *)*exit_pc); + exit_op_count[insn]++; + return exit_pc; // This function must return exit_pc! +} + +struct insn_count { + int64_t insn; + int64_t count; +}; + +static int +insn_count_sort_comp(const void *a, const void *b) +{ + const struct insn_count *count_a = a; + const struct insn_count *count_b = b; + if (count_a->count > count_b->count) { + return -1; + } + else if (count_a->count < count_b->count) { + return 1; + } + return 0; +} + +static struct insn_count insn_sorting_buffer[VM_INSTRUCTION_SIZE]; +static const struct insn_count * +sort_insn_count_array(int64_t *array) +{ + for (int i = 0; i < VM_INSTRUCTION_SIZE; i++) { + insn_sorting_buffer[i] = (struct insn_count) { i, array[i] }; + } + qsort(insn_sorting_buffer, VM_INSTRUCTION_SIZE, sizeof(insn_sorting_buffer[0]), &insn_count_sort_comp); + return insn_sorting_buffer; +} + static void -print_ujit_stats(void) +print_insn_count_buffer(const struct insn_count *buffer, int how_many, int left_pad) { - if (rb_ujit_opts.gen_stats) { - double double_ujit_exec_insns_count = rb_ujit_exec_insns_count; - double total_insns_count = vm_insns_count + rb_ujit_exec_insns_count; - double ratio = double_ujit_exec_insns_count / total_insns_count; - - fprintf(stderr, "vm_insns_count: %10" PRId64 "\n", vm_insns_count); - fprintf(stderr, "ujit_exec_insns_count: %10" PRId64 "\n", rb_ujit_exec_insns_count); - fprintf(stderr, "ratio_in_ujit: %9.1f%%\n", ratio * 100); + size_t longest_insn_len = 0; + for (int i = 0; i < how_many; i++) { + const char *instruction_name = insn_name(buffer[i].insn); + size_t len = strlen(instruction_name); + if (len > longest_insn_len) { + longest_insn_len = len; + } + } + + for (int i = 0; i < how_many; i++) { + const char *instruction_name = insn_name(buffer[i].insn); + size_t padding = left_pad + longest_insn_len - strlen(instruction_name); + for (size_t j = 0; j < padding; j++) { + fputc(' ', stderr); + } + fprintf(stderr, "%s: %10" PRId64 "\n", instruction_name, buffer[i].count); } } -void rb_ujit_collect_vm_usage_insn(int insn) +__attribute__((destructor)) +static void +print_ujit_stats(void) { - vm_insns_count++; + if (!rb_ujit_opts.gen_stats) return; + + const struct insn_count *sorted_exit_ops = sort_insn_count_array(exit_op_count); + + double double_ujit_exec_insns_count = rb_ujit_exec_insns_count; + double total_insns_count = vm_insns_count + rb_ujit_exec_insns_count; + double ratio = double_ujit_exec_insns_count / total_insns_count; + + fprintf(stderr, "vm_insns_count: %10" PRId64 "\n", vm_insns_count); + fprintf(stderr, "ujit_exec_insns_count: %10" PRId64 "\n", rb_ujit_exec_insns_count); + fprintf(stderr, "ratio_in_ujit: %9.1f%%\n", ratio * 100); + fprintf(stderr, "most frequent exit op:\n"); + print_insn_count_buffer(sorted_exit_ops, 5, 4); } +#endif // if RUBY_DEBUG void rb_ujit_init(struct rb_ujit_options *options) diff --git a/ujit_iface.h b/ujit_iface.h index 597b0cbbb5..73b121de61 100644 --- a/ujit_iface.h +++ b/ujit_iface.h @@ -32,5 +32,7 @@ int opcode_at_pc(const rb_iseq_t *iseq, const VALUE *pc); void check_cfunc_dispatch(VALUE receiver, struct rb_call_data *cd, void *callee, rb_callable_method_entry_t *compile_time_cme); bool cfunc_needs_frame(const rb_method_cfunc_t *cfunc); void assume_method_lookup_stable(const struct rb_callcache *cc, const rb_callable_method_entry_t *cme, block_t* block); +// this function *must* return passed exit_pc +const VALUE *rb_ujit_count_side_exit_op(const VALUE *exit_pc); #endif // #ifndef UJIT_IFACE_H |