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

ruby-changes:68882

From: Alan <ko1@a...>
Date: Thu, 21 Oct 2021 08:15:01 +0900 (JST)
Subject: [ruby-changes:68882] 515fb988fe (master): YJIT: add comments to disassembly

https://git.ruby-lang.org/ruby.git/commit/?id=515fb988fe

From 515fb988fe3c3ad28fdcaea4f043ea6a445c5213 Mon Sep 17 00:00:00 2001
From: Alan Wu <XrXr@u...>
Date: Wed, 7 Apr 2021 15:27:05 -0400
Subject: YJIT: add comments to disassembly

Introduce a new macro `ADD_COMMENT(cb, comment)` that records a comment
for the current write position in the code block.

Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@g...>
Co-authored-by: Aaron Patterson <aaron.patterson@s...>
---
 yjit.rb        | 77 +++++++++++++++++++++++++++++++++++++---------------------
 yjit_codegen.c | 12 +++++++++
 yjit_iface.c   | 34 ++++++++++++++++++++++++++
 yjit_iface.h   |  9 +++++++
 4 files changed, 104 insertions(+), 28 deletions(-)

diff --git a/yjit.rb b/yjit.rb
index b513e1b0dc..e1d9c16417 100644
--- a/yjit.rb
+++ b/yjit.rb
@@ -1,40 +1,61 @@ https://github.com/ruby/ruby/blob/trunk/yjit.rb#L1
 module YJIT
-  def self.disasm(iseq)
-    iseq = RubyVM::InstructionSequence.of(iseq)
+  if defined?(Disasm)
+    def self.disasm(iseq, tty: $stdout && $stdout.tty?)
+      iseq = RubyVM::InstructionSequence.of(iseq)
 
-    blocks = YJIT.blocks_for(iseq)
-    return if blocks.empty?
+      blocks = YJIT.blocks_for(iseq)
+      return if blocks.empty?
 
-    str = ""
-
-    cs = YJIT::Disasm.new
+      str = ""
+      str << iseq.disasm
+      str << "\n"
 
-    str << iseq.disasm
-    str << "\n"
+      # Sort the blocks by increasing addresses
+      sorted_blocks = blocks.sort_by(&:address)
+
+      highlight = ->(str) {
+        if tty
+          "\x1b[1m#{str}\x1b[0m"
+        else
+          str
+        end
+      }
+
+      cs = YJIT::Disasm.new
+      sorted_blocks.each_with_index do |block, i|
+        str << "== BLOCK #{i+1}/#{blocks.length}: #{block.code.length} BYTES, ISEQ RANGE [#{block.iseq_start_index},#{block.iseq_end_index}) ".ljust(80, "=")
+        str << "\n"
+
+        comments = comments_for(block.address, block.address + block.code.length)
+        comment_idx = 0
+        cs.disasm(block.code, block.address).each do |i|
+          while (comment = comments[comment_idx]) && comment.address <= i.address
+            str << "  ;#{highlight.call(comment.comment)}\n"
+            comment_idx += 1
+          end
+
+          str << sprintf(
+            "  %<address>08x:  %<instruction>s\t%<details>s\n",
+            address: i.address,
+            instruction: i.mnemonic,
+            details: i.op_str
+          )
+        end
+      end
 
-    # Sort the blocks by increasing addresses
-    blocks.sort_by(&:address).each_with_index do |block, i|
-      str << "== BLOCK #{i+1}/#{blocks.length}: #{block.code.length} BYTES, ISEQ RANGE [#{block.iseq_start_index},#{block.iseq_end_index}) ".ljust(80, "=")
+      block_sizes = blocks.map { |block| block.code.length }
+      total_bytes = block_sizes.sum
+      str << "\n"
+      str << "Total code size: #{total_bytes} bytes"
       str << "\n"
 
-      cs.disasm(block.code, block.address).each do |i|
-        str << sprintf(
-          "  %<address>08x:  %<instruction>s\t%<details>s\n",
-          address: i.address,
-          instruction: i.mnemonic,
-          details: i.op_str
-        )
-      end
+      str
     end
 
-    block_sizes = blocks.map { |block| block.code.length }
-    total_bytes = block_sizes.reduce(0, :+)
-    str << "\n"
-    str << "Total code size: #{total_bytes} bytes"
-    str << "\n"
-
-    str
-  end if defined?(Disasm)
+    def self.comments_for(start_address, end_address)
+      Primitive.comments_for(start_address, end_address)
+    end
+  end
 
   # Return a hash for statistics generated for the --yjit-stats command line option.
   # Return nil when option is not passed or unavailable.
diff --git a/yjit_codegen.c b/yjit_codegen.c
index fd029b3287..7ff59b94a1 100644
--- a/yjit_codegen.c
+++ b/yjit_codegen.c
@@ -216,9 +216,16 @@ _counted_side_exit(uint8_t *existing_side_exit, int64_t *counter) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L216
     return start;
 }
 
+// Comments for generated machine code
+#define ADD_COMMENT(cb, comment) rb_darray_append(&yjit_code_comments, ((struct yjit_comment){(cb)->write_pos, (comment)}))
+yjit_comment_array_t yjit_code_comments;
+
 #else
+
 #define GEN_COUNTER_INC(cb, counter_name) ((void)0)
 #define COUNTED_EXIT(side_exit, counter_name) side_exit
+#define ADD_COMMENT(cb, comment) ((void)0)
+
 #endif // if RUBY_DEBUG
 
 /*
@@ -323,6 +330,9 @@ yjit_gen_block(ctx_t* ctx, block_t* block, rb_execution_context_t* ec) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L330
         // Note that the increment happens even when the output takes side exit.
         GEN_COUNTER_INC(cb, exec_instruction);
 
+        // Add a comment for the name of the YARV instruction
+        ADD_COMMENT(cb, insn_name(opcode));
+
         // Call the code generation function
         bool continue_generating = p_desc->gen_fn(&jit, ctx);
 
@@ -751,6 +761,7 @@ gen_getinstancevariable(jitstate_t* jit, ctx_t* ctx) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L761
 
             // Guard that self is embedded
             // TODO: BT and JC is shorter
+            ADD_COMMENT(cb, "guard embedded getivar");
             x86opnd_t flags_opnd = member_opnd(REG0, struct RBasic, flags);
             test(cb, flags_opnd, imm_opnd(ROBJECT_EMBED));
             jit_chain_guard(JCC_JZ, jit, ctx, GETIVAR_MAX_DEPTH, side_exit);
@@ -772,6 +783,7 @@ gen_getinstancevariable(jitstate_t* jit, ctx_t* ctx) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L783
 
             // Guard that self is *not* embedded
             // See ROBJECT_IVPTR() from include/ruby/internal/core/robject.h
+            ADD_COMMENT(cb, "guard extended getivar");
             x86opnd_t flags_opnd = member_opnd(REG0, struct RBasic, flags);
             test(cb, flags_opnd, imm_opnd(ROBJECT_EMBED));
             jit_chain_guard(JCC_JNZ, jit, ctx, GETIVAR_MAX_DEPTH, side_exit);
diff --git a/yjit_iface.c b/yjit_iface.c
index 9e708b5bb8..3d1a617757 100644
--- a/yjit_iface.c
+++ b/yjit_iface.c
@@ -30,6 +30,7 @@ static int64_t vm_insns_count = 0; https://github.com/ruby/ruby/blob/trunk/yjit_iface.c#L30
 static int64_t exit_op_count[VM_INSTRUCTION_SIZE] = { 0 };
 int64_t rb_compiled_iseq_count = 0;
 struct rb_yjit_runtime_counters yjit_runtime_counters = { 0 };
+static VALUE cYjitCodeComment;
 #endif
 
 // Machine code blocks (executable memory)
@@ -521,6 +522,7 @@ yjit_blocks_for(VALUE mod, VALUE rb_iseq) https://github.com/ruby/ruby/blob/trunk/yjit_iface.c#L522
         rb_darray_for(versions, block_idx) {
             block_t *block = rb_darray_get(versions, block_idx);
 
+            // FIXME: The object craeted here can outlive the block itself
             VALUE rb_block = TypedData_Wrap_Struct(cYjitBlock, &yjit_block_type, block);
             rb_ary_push(all_versions, rb_block);
         }
@@ -655,6 +657,37 @@ at_exit_print_stats(RB_BLOCK_CALL_FUNC_ARGLIST(yieldarg, data)) https://github.com/ruby/ruby/blob/trunk/yjit_iface.c#L657
     return Qnil;
 }
 
+// Primitive called in yjit.rb. Export all machine code comments as a Ruby array.
+static VALUE
+comments_for(rb_execution_context_t *ec, VALUE self, VALUE start_address, VALUE end_address)
+{
+    VALUE comment_array = rb_ary_new();
+#if RUBY_DEBUG
+    uint8_t *start = (void *)NUM2ULL(start_address);
+    uint8_t *end = (void *)NUM2ULL(end_address);
+
+    rb_darray_for(yjit_code_comments, i) {
+        struct yjit_comment comment = rb_darray_get(yjit_code_comments, i);
+        uint8_t *comment_pos = cb_get_ptr(cb, comment.offset);
+
+        if (comment_pos >= end) {
+            break;
+        }
+        if (comment_pos >= start) {
+            VALUE vals = rb_ary_new_from_args(
+                2,
+                LL2NUM((long long) comment_pos),
+                rb_str_new_cstr(comment.comment)
+            );
+            rb_ary_push(comment_array, rb_struct_alloc(cYjitCodeComment, vals));
+        }
+    }
+
+#endif // if RUBY_DEBUG
+
+    return comment_array;
+}
+
 // Primitive called in yjit.rb. Export all runtime counters as a Ruby hash.
 static VALUE
 get_stat_counters(rb_execution_context_t *ec, VALUE self)
@@ -962,6 +995,7 @@ rb_yjit_init(struct rb_yjit_options *options) https://github.com/ruby/ruby/blob/trunk/yjit_iface.c#L995
     rb_define_alloc_func(cYjitDisasm, yjit_disasm_init);
     rb_define_method(cYjitDisasm, "disasm", yjit_disasm, 2);
     cYjitDisasmInsn = rb_struct_define_under(cYjitDisasm, "Insn", "address", "mnemonic", "op_str", NULL);
+    cYjitCodeComment = rb_struct_define_under(cYjitDisasm, "Comment", "address", "comment");
 #endif
 
     if (RUBY_DEBUG && rb_yjit_opts.gen_stats) {
diff --git a/yjit_iface.h b/yjit_iface.h
index 6428906b28..63871d24c2 100644
--- a/yjit_iface.h
+++ b/yjit_iface.h
@@ -67,6 +67,15 @@ YJIT_DECLARE_COUNTERS( https://github.com/ruby/ruby/blob/trunk/yjit_iface.h#L67
 
 #undef YJIT_DECLARE_COUNTERS
 
+struct yjit_comment {
+    int32_t offset;
+    const char *comment;
+};
+
+typedef rb_darray(struct yjit_comment) yjit_comment_array_t;
+
+extern yjit_comment_array_t yjit_code_comments;
+
 #endif // if RUBY_DEBUG
 
 RUBY_EXTERN struct rb_yjit_options rb_yjit_opts;
-- 
cgit v1.2.1


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

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