diff --git a/yjit.rb b/yjit.rb index 6f7fdb41b3..442f9ef1b6 100644 --- a/yjit.rb +++ b/yjit.rb @@ -55,6 +55,43 @@ module YJIT 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( + " %
08x: %s\t%
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) 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) // 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