[前][次][番号順一覧][スレッド一覧]

ruby-changes:68725

From: Alan <ko1@a...>
Date: Thu, 21 Oct 2021 08:12:36 +0900 (JST)
Subject: [ruby-changes:68725] 3c7251b41b (master): Tally instructions when taking side exists for --ujit-stats

https://git.ruby-lang.org/ruby.git/commit/?id=3c7251b41b

From 3c7251b41b8850aab7c64c4885669e9c099d2465 Mon Sep 17 00:00:00 2001
From: Alan Wu <XrXr@u...>
Date: Wed, 27 Jan 2021 16:13:27 -0500
Subject: Tally instructions when taking side exists for --ujit-stats

shopify/ruby#29

Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@s...>
---
 tool/runruby.rb |  5 +++
 ujit_asm.c      | 39 +++++++++++++++++++++++
 ujit_asm.h      |  3 ++
 ujit_codegen.c  |  5 +++
 ujit_iface.c    | 95 +++++++++++++++++++++++++++++++++++++++++++++++++--------
 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' https://github.com/ruby/ruby/blob/trunk/tool/runruby.rb#L11
   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 https://github.com/ruby/ruby/blob/trunk/tool/runruby.rb#L166
 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) https://github.com/ruby/ruby/blob/trunk/ujit_asm.c#L54
     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 }}; https://github.com/ruby/ruby/blob/trunk/ujit_asm.h#L216
 // 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) https://github.com/ruby/ruby/blob/trunk/ujit_codegen.c#L66
     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; https://github.com/ruby/ruby/blob/trunk/ujit_iface.c#L27
 
 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) https://github.com/ruby/ruby/blob/trunk/ujit_iface.c#L417
 }
 #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); https://github.com/ruby/ruby/blob/trunk/ujit_iface.h#L32
 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
-- 
cgit v1.2.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]