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@gmail.com> Co-authored-by: Aaron Patterson <aaron.patterson@shopify.com>
This commit is contained in:
parent
543bdde6c2
commit
515fb988fe
79
yjit.rb
79
yjit.rb
@ -1,40 +1,61 @@
|
|||||||
module YJIT
|
module YJIT
|
||||||
def self.disasm(iseq)
|
if defined?(Disasm)
|
||||||
iseq = RubyVM::InstructionSequence.of(iseq)
|
def self.disasm(iseq, tty: $stdout && $stdout.tty?)
|
||||||
|
iseq = RubyVM::InstructionSequence.of(iseq)
|
||||||
|
|
||||||
blocks = YJIT.blocks_for(iseq)
|
blocks = YJIT.blocks_for(iseq)
|
||||||
return if blocks.empty?
|
return if blocks.empty?
|
||||||
|
|
||||||
str = ""
|
str = ""
|
||||||
|
str << iseq.disasm
|
||||||
cs = YJIT::Disasm.new
|
|
||||||
|
|
||||||
str << iseq.disasm
|
|
||||||
str << "\n"
|
|
||||||
|
|
||||||
# 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, "=")
|
|
||||||
str << "\n"
|
str << "\n"
|
||||||
|
|
||||||
cs.disasm(block.code, block.address).each do |i|
|
# Sort the blocks by increasing addresses
|
||||||
str << sprintf(
|
sorted_blocks = blocks.sort_by(&:address)
|
||||||
" %<address>08x: %<instruction>s\t%<details>s\n",
|
|
||||||
address: i.address,
|
highlight = ->(str) {
|
||||||
instruction: i.mnemonic,
|
if tty
|
||||||
details: i.op_str
|
"\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
|
end
|
||||||
|
|
||||||
|
block_sizes = blocks.map { |block| block.code.length }
|
||||||
|
total_bytes = block_sizes.sum
|
||||||
|
str << "\n"
|
||||||
|
str << "Total code size: #{total_bytes} bytes"
|
||||||
|
str << "\n"
|
||||||
|
|
||||||
|
str
|
||||||
end
|
end
|
||||||
|
|
||||||
block_sizes = blocks.map { |block| block.code.length }
|
def self.comments_for(start_address, end_address)
|
||||||
total_bytes = block_sizes.reduce(0, :+)
|
Primitive.comments_for(start_address, end_address)
|
||||||
str << "\n"
|
end
|
||||||
str << "Total code size: #{total_bytes} bytes"
|
end
|
||||||
str << "\n"
|
|
||||||
|
|
||||||
str
|
|
||||||
end if defined?(Disasm)
|
|
||||||
|
|
||||||
# Return a hash for statistics generated for the --yjit-stats command line option.
|
# Return a hash for statistics generated for the --yjit-stats command line option.
|
||||||
# Return nil when option is not passed or unavailable.
|
# Return nil when option is not passed or unavailable.
|
||||||
|
@ -216,9 +216,16 @@ _counted_side_exit(uint8_t *existing_side_exit, int64_t *counter)
|
|||||||
return start;
|
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
|
#else
|
||||||
|
|
||||||
#define GEN_COUNTER_INC(cb, counter_name) ((void)0)
|
#define GEN_COUNTER_INC(cb, counter_name) ((void)0)
|
||||||
#define COUNTED_EXIT(side_exit, counter_name) side_exit
|
#define COUNTED_EXIT(side_exit, counter_name) side_exit
|
||||||
|
#define ADD_COMMENT(cb, comment) ((void)0)
|
||||||
|
|
||||||
#endif // if RUBY_DEBUG
|
#endif // if RUBY_DEBUG
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -323,6 +330,9 @@ yjit_gen_block(ctx_t* ctx, block_t* block, rb_execution_context_t* ec)
|
|||||||
// Note that the increment happens even when the output takes side exit.
|
// Note that the increment happens even when the output takes side exit.
|
||||||
GEN_COUNTER_INC(cb, exec_instruction);
|
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
|
// Call the code generation function
|
||||||
bool continue_generating = p_desc->gen_fn(&jit, ctx);
|
bool continue_generating = p_desc->gen_fn(&jit, ctx);
|
||||||
|
|
||||||
@ -751,6 +761,7 @@ gen_getinstancevariable(jitstate_t* jit, ctx_t* ctx)
|
|||||||
|
|
||||||
// Guard that self is embedded
|
// Guard that self is embedded
|
||||||
// TODO: BT and JC is shorter
|
// TODO: BT and JC is shorter
|
||||||
|
ADD_COMMENT(cb, "guard embedded getivar");
|
||||||
x86opnd_t flags_opnd = member_opnd(REG0, struct RBasic, flags);
|
x86opnd_t flags_opnd = member_opnd(REG0, struct RBasic, flags);
|
||||||
test(cb, flags_opnd, imm_opnd(ROBJECT_EMBED));
|
test(cb, flags_opnd, imm_opnd(ROBJECT_EMBED));
|
||||||
jit_chain_guard(JCC_JZ, jit, ctx, GETIVAR_MAX_DEPTH, side_exit);
|
jit_chain_guard(JCC_JZ, jit, ctx, GETIVAR_MAX_DEPTH, side_exit);
|
||||||
@ -772,6 +783,7 @@ gen_getinstancevariable(jitstate_t* jit, ctx_t* ctx)
|
|||||||
|
|
||||||
// Guard that self is *not* embedded
|
// Guard that self is *not* embedded
|
||||||
// See ROBJECT_IVPTR() from include/ruby/internal/core/robject.h
|
// 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);
|
x86opnd_t flags_opnd = member_opnd(REG0, struct RBasic, flags);
|
||||||
test(cb, flags_opnd, imm_opnd(ROBJECT_EMBED));
|
test(cb, flags_opnd, imm_opnd(ROBJECT_EMBED));
|
||||||
jit_chain_guard(JCC_JNZ, jit, ctx, GETIVAR_MAX_DEPTH, side_exit);
|
jit_chain_guard(JCC_JNZ, jit, ctx, GETIVAR_MAX_DEPTH, side_exit);
|
||||||
|
34
yjit_iface.c
34
yjit_iface.c
@ -30,6 +30,7 @@ static int64_t vm_insns_count = 0;
|
|||||||
static int64_t exit_op_count[VM_INSTRUCTION_SIZE] = { 0 };
|
static int64_t exit_op_count[VM_INSTRUCTION_SIZE] = { 0 };
|
||||||
int64_t rb_compiled_iseq_count = 0;
|
int64_t rb_compiled_iseq_count = 0;
|
||||||
struct rb_yjit_runtime_counters yjit_runtime_counters = { 0 };
|
struct rb_yjit_runtime_counters yjit_runtime_counters = { 0 };
|
||||||
|
static VALUE cYjitCodeComment;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Machine code blocks (executable memory)
|
// Machine code blocks (executable memory)
|
||||||
@ -521,6 +522,7 @@ yjit_blocks_for(VALUE mod, VALUE rb_iseq)
|
|||||||
rb_darray_for(versions, block_idx) {
|
rb_darray_for(versions, block_idx) {
|
||||||
block_t *block = rb_darray_get(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);
|
VALUE rb_block = TypedData_Wrap_Struct(cYjitBlock, &yjit_block_type, block);
|
||||||
rb_ary_push(all_versions, rb_block);
|
rb_ary_push(all_versions, rb_block);
|
||||||
}
|
}
|
||||||
@ -655,6 +657,37 @@ at_exit_print_stats(RB_BLOCK_CALL_FUNC_ARGLIST(yieldarg, data))
|
|||||||
return Qnil;
|
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.
|
// Primitive called in yjit.rb. Export all runtime counters as a Ruby hash.
|
||||||
static VALUE
|
static VALUE
|
||||||
get_stat_counters(rb_execution_context_t *ec, VALUE self)
|
get_stat_counters(rb_execution_context_t *ec, VALUE self)
|
||||||
@ -962,6 +995,7 @@ rb_yjit_init(struct rb_yjit_options *options)
|
|||||||
rb_define_alloc_func(cYjitDisasm, yjit_disasm_init);
|
rb_define_alloc_func(cYjitDisasm, yjit_disasm_init);
|
||||||
rb_define_method(cYjitDisasm, "disasm", yjit_disasm, 2);
|
rb_define_method(cYjitDisasm, "disasm", yjit_disasm, 2);
|
||||||
cYjitDisasmInsn = rb_struct_define_under(cYjitDisasm, "Insn", "address", "mnemonic", "op_str", NULL);
|
cYjitDisasmInsn = rb_struct_define_under(cYjitDisasm, "Insn", "address", "mnemonic", "op_str", NULL);
|
||||||
|
cYjitCodeComment = rb_struct_define_under(cYjitDisasm, "Comment", "address", "comment");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (RUBY_DEBUG && rb_yjit_opts.gen_stats) {
|
if (RUBY_DEBUG && rb_yjit_opts.gen_stats) {
|
||||||
|
@ -67,6 +67,15 @@ YJIT_DECLARE_COUNTERS(
|
|||||||
|
|
||||||
#undef YJIT_DECLARE_COUNTERS
|
#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
|
#endif // if RUBY_DEBUG
|
||||||
|
|
||||||
RUBY_EXTERN struct rb_yjit_options rb_yjit_opts;
|
RUBY_EXTERN struct rb_yjit_options rb_yjit_opts;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user