Tally instructions when taking side exists for --ujit-stats
shopify/ruby#29 Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>
This commit is contained in:
parent
09479c33f5
commit
3c7251b41b
@ -11,6 +11,8 @@ when ENV['RUNRUBY_USE_GDB'] == 'true'
|
|||||||
debugger = :gdb
|
debugger = :gdb
|
||||||
when ENV['RUNRUBY_USE_LLDB'] == 'true'
|
when ENV['RUNRUBY_USE_LLDB'] == 'true'
|
||||||
debugger = :lldb
|
debugger = :lldb
|
||||||
|
when ENV['RUNRUBY_UJIT_STATS']
|
||||||
|
use_ujit_stat = true
|
||||||
end
|
end
|
||||||
while arg = ARGV[0]
|
while arg = ARGV[0]
|
||||||
break ARGV.shift if arg == '--'
|
break ARGV.shift if arg == '--'
|
||||||
@ -164,6 +166,9 @@ if debugger
|
|||||||
end
|
end
|
||||||
|
|
||||||
cmd = [runner || ruby]
|
cmd = [runner || ruby]
|
||||||
|
if use_ujit_stat
|
||||||
|
cmd << '--ujit-stats'
|
||||||
|
end
|
||||||
cmd.concat(ARGV)
|
cmd.concat(ARGV)
|
||||||
cmd.unshift(*precommand) unless precommand.empty?
|
cmd.unshift(*precommand) unless precommand.empty?
|
||||||
|
|
||||||
|
39
ujit_asm.c
39
ujit_asm.c
@ -54,6 +54,45 @@ x86opnd_t mem_opnd(uint32_t num_bits, x86opnd_t base_reg, int32_t disp)
|
|||||||
return opnd;
|
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)
|
x86opnd_t resize_opnd(x86opnd_t opnd, uint32_t num_bits)
|
||||||
{
|
{
|
||||||
assert (num_bits % 8 == 0);
|
assert (num_bits % 8 == 0);
|
||||||
|
@ -216,6 +216,9 @@ static const x86opnd_t R15B = { OPND_REG, 8, .as.reg = { REG_GP, 15 }};
|
|||||||
// Memory operand with base register and displacement/offset
|
// Memory operand with base register and displacement/offset
|
||||||
x86opnd_t mem_opnd(uint32_t num_bits, x86opnd_t base_reg, int32_t disp);
|
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
|
// Immediate number operand
|
||||||
x86opnd_t imm_opnd(int64_t val);
|
x86opnd_t imm_opnd(int64_t val);
|
||||||
|
|
||||||
|
@ -66,6 +66,11 @@ ujit_gen_exit(jitstate_t* jit, ctx_t* ctx, codeblock_t* cb, VALUE* exit_pc)
|
|||||||
mov(cb, RAX, const_ptr_opnd(exit_pc));
|
mov(cb, RAX, const_ptr_opnd(exit_pc));
|
||||||
mov(cb, member_opnd(REG_CFP, rb_control_frame_t, pc), RAX);
|
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
|
// Write the post call bytes
|
||||||
cb_write_post_call_bytes(cb);
|
cb_write_post_call_bytes(cb);
|
||||||
}
|
}
|
||||||
|
95
ujit_iface.c
95
ujit_iface.c
@ -27,6 +27,7 @@ bool rb_ujit_enabled;
|
|||||||
|
|
||||||
static int64_t vm_insns_count = 0;
|
static int64_t vm_insns_count = 0;
|
||||||
int64_t rb_ujit_exec_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 st_table * version_tbl;
|
||||||
extern codeblock_t *cb;
|
extern codeblock_t *cb;
|
||||||
@ -416,25 +417,95 @@ ujit_disasm(VALUE self, VALUE code, VALUE from)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#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_insn_count_buffer(const struct insn_count *buffer, int how_many, int left_pad)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
__attribute__((destructor))
|
__attribute__((destructor))
|
||||||
static void
|
static void
|
||||||
print_ujit_stats(void)
|
print_ujit_stats(void)
|
||||||
{
|
{
|
||||||
if (rb_ujit_opts.gen_stats) {
|
if (!rb_ujit_opts.gen_stats) return;
|
||||||
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);
|
const struct insn_count *sorted_exit_ops = sort_insn_count_array(exit_op_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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void rb_ujit_collect_vm_usage_insn(int insn)
|
double double_ujit_exec_insns_count = rb_ujit_exec_insns_count;
|
||||||
{
|
double total_insns_count = vm_insns_count + rb_ujit_exec_insns_count;
|
||||||
vm_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
|
void
|
||||||
rb_ujit_init(struct rb_ujit_options *options)
|
rb_ujit_init(struct rb_ujit_options *options)
|
||||||
|
@ -32,5 +32,7 @@ int opcode_at_pc(const rb_iseq_t *iseq, const VALUE *pc);
|
|||||||
void check_cfunc_dispatch(VALUE receiver, struct rb_call_data *cd, void *callee, rb_callable_method_entry_t *compile_time_cme);
|
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);
|
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);
|
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
|
#endif // #ifndef UJIT_IFACE_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user