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

ruby-changes:69045

From: Aaron <ko1@a...>
Date: Thu, 21 Oct 2021 08:20:38 +0900 (JST)
Subject: [ruby-changes:69045] 46d5e10279 (master): Add graphviz output

https://git.ruby-lang.org/ruby.git/commit/?id=46d5e10279

From 46d5e10279b6f1cea46682b5a5da3a09c5ce0c07 Mon Sep 17 00:00:00 2001
From: Aaron Patterson <tenderlove@r...>
Date: Thu, 10 Jun 2021 13:16:58 -0700
Subject: Add graphviz output

This adds a method to blocks to get outgoing ids, then uses the outgoing
ids to generate a graphviz graph.  Two methods were added to the Block
object.  One method returns an id for the block, which is just the
address of the underlying block.  The other method returns a list of
outgoing block ids.  We can use Block#id in conjunction with
Block#outgoing_ids to construct a graph of blocks
---
 yjit.rb      | 37 +++++++++++++++++++++++++++++++++++++
 yjit_iface.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 88 insertions(+)

diff --git a/yjit.rb b/yjit.rb
index 6f7fdb41b3..442f9ef1b6 100644
--- a/yjit.rb
+++ b/yjit.rb
@@ -55,6 +55,43 @@ module YJIT https://github.com/ruby/ruby/blob/trunk/yjit.rb#L55
     def self.comments_for(start_address, end_address)
       Primitive.comments_for(start_address, end_address)
     end
+
+    def self.graphviz_for(iseq)
+      iseq = RubyVM::InstructionSequence.of(iseq)
+      buff = ''
+      blocks = blocks_for(iseq)
+      buff << "digraph g {"
+      buff << "  node [shape=record];"
+      blocks.each do |block|
+        buff << "b#{block.id} [label=\"#{disasm_block(block).gsub(/\n/, "\\l\n")}\"];"
+        block.outgoing_ids.each do |id|
+          buff << "b#{block.id} -> b#{id};"
+        end
+      end
+      buff << "}"
+      buff
+    end
+
+    def self.disasm_block(block)
+      cs = YJIT::Disasm.new
+      comments = comments_for(block.address, block.address + block.code.length)
+      comment_idx = 0
+      str = ''
+      cs.disasm(block.code, block.address).each do |i|
+        while (comment = comments[comment_idx]) && comment.address <= i.address
+          str << "  ; #{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
+      str
+    end
   end
 
   # Return a hash for statistics generated for the --yjit-stats command line option.
diff --git a/yjit_iface.c b/yjit_iface.c
index b255a44221..754a2d6529 100644
--- a/yjit_iface.c
+++ b/yjit_iface.c
@@ -1025,6 +1025,55 @@ rb_yjit_call_threshold(void) https://github.com/ruby/ruby/blob/trunk/yjit_iface.c#L1025
     return rb_yjit_opts.call_threshold;
 }
 
+# define PTR2NUM(x)   (LONG2NUM((long)(x)))
+
+/**
+ *  call-seq: block.id -> unique_id
+ *
+ *  Returns a unique integer ID for the block.  For example:
+ *
+ *      blocks = blocks_for(iseq)
+ *      blocks.group_by(&:id)
+ */
+static VALUE
+block_id(VALUE self)
+{
+    block_t * block;
+    TypedData_Get_Struct(self, block_t, &yjit_block_type, block);
+    return PTR2NUM(block);
+}
+
+/**
+ *  call-seq: block.outgoing_ids -> list
+ *
+ *  Returns a list of outgoing ids for the current block.  This list can be used
+ *  in conjunction with Block#id to construct a graph of block objects.
+ */
+static VALUE
+outgoing_ids(VALUE self)
+{
+    block_t * block;
+    TypedData_Get_Struct(self, block_t, &yjit_block_type, block);
+
+    VALUE ids = rb_ary_new();
+
+    rb_darray_for(block->outgoing, branch_idx) {
+        branch_t* out_branch = rb_darray_get(block->outgoing, branch_idx);
+
+        for (size_t succ_idx = 0; succ_idx < 2; succ_idx++) {
+            block_t* succ = out_branch->blocks[succ_idx];
+
+            if (succ == NULL)
+                continue;
+
+            rb_ary_push(ids, PTR2NUM(succ));
+        }
+
+    }
+
+    return ids;
+}
+
 // Can raise RuntimeError
 void
 rb_yjit_init(struct rb_yjit_options *options)
@@ -1069,9 +1118,11 @@ rb_yjit_init(struct rb_yjit_options *options) https://github.com/ruby/ruby/blob/trunk/yjit_iface.c#L1118
     // YJIT::Block (block version, code block)
     cYjitBlock = rb_define_class_under(mYjit, "Block", rb_cObject);
     rb_define_method(cYjitBlock, "address", block_address, 0);
+    rb_define_method(cYjitBlock, "id", block_id, 0);
     rb_define_method(cYjitBlock, "code", block_code, 0);
     rb_define_method(cYjitBlock, "iseq_start_index", iseq_start_index, 0);
     rb_define_method(cYjitBlock, "iseq_end_index", iseq_end_index, 0);
+    rb_define_method(cYjitBlock, "outgoing_ids", outgoing_ids, 0);
 
     // YJIT disassembler interface
 #if HAVE_LIBCAPSTONE
-- 
cgit v1.2.1


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

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