From 2195c95247c4dd617f5b9a1c476b697845a00b37 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Tue, 26 Jan 2021 15:21:47 -0500 Subject: [PATCH] Implement --ujit-stats and instructoin counting VM and ujit instruction counting in debug builds. shopify/ruby#19 --- ruby.c | 27 ++++++++++++++++++++++----- ujit.h | 7 ++++++- ujit_codegen.c | 5 +++++ ujit_iface.c | 38 ++++++++++++++++++++++++++++++++------ ujit_iface.h | 3 +++ vm.c | 13 +++++++++++++ vm_insnhelper.h | 5 +++++ 7 files changed, 86 insertions(+), 12 deletions(-) diff --git a/ruby.c b/ruby.c index e8a4d4735b..55d5bf090c 100644 --- a/ruby.c +++ b/ruby.c @@ -189,6 +189,8 @@ struct ruby_cmdline_options { #if USE_MJIT struct mjit_options mjit; #endif + struct rb_ujit_options ujit; + int sflag, xflag; unsigned int warning: 1; unsigned int verbose: 1; @@ -1018,10 +1020,6 @@ set_option_encoding_once(const char *type, VALUE *name, const char *e, long elen #define set_source_encoding_once(opt, e, elen) \ set_option_encoding_once("source", &(opt)->src.enc.name, (e), (elen)) -#if USE_MJIT -static void -setup_mjit_options(const char *s, struct mjit_options *mjit_opt) -{ #define opt_match(s, l, name) \ ((((l) > rb_strlen_lit(name)) ? (s)[rb_strlen_lit(name)] == '=' : \ (l) == rb_strlen_lit(name)) && \ @@ -1031,6 +1029,24 @@ setup_mjit_options(const char *s, struct mjit_options *mjit_opt) opt_match(s, l, name) && (*(s) ? (rb_warn("argument to --jit-" name " is ignored"), 1) : 1) #define opt_match_arg(s, l, name) \ opt_match(s, l, name) && (*(s) ? 1 : (rb_raise(rb_eRuntimeError, "--jit-" name " needs an argument"), 0)) + +static void +setup_ujit_options(const char *s, struct rb_ujit_options *ujit_opt) +{ + *ujit_opt = (struct rb_ujit_options) { 0 }; + + if (*s != '-') return; + const size_t l = strlen(++s); + + if (opt_match_noarg(s, l, "stats")) { + ujit_opt->gen_stats = true; + } +} + +#if USE_MJIT +static void +setup_mjit_options(const char *s, struct mjit_options *mjit_opt) +{ if (*s != '-') return; const size_t l = strlen(++s); if (*s == 0) return; @@ -1442,6 +1458,7 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt) } else if (strncmp("ujit", s, 4) == 0) { FEATURE_SET(opt->features, FEATURE_BIT(ujit)); + setup_ujit_options(s + 4, &opt->ujit); } else if (strcmp("yydebug", s) == 0) { if (envopt) goto noenvopt_long; @@ -1804,7 +1821,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt) rb_warning("-K is specified; it is for 1.8 compatibility and may cause odd behavior"); if (opt->features.set & FEATURE_BIT(ujit)) - rb_ujit_init(); + rb_ujit_init(&opt->ujit); #if USE_MJIT if (opt->features.set & FEATURE_BIT(jit)) { opt->mjit.on = TRUE; /* set mjit.on for ruby_show_version() API and check to call mjit_init() */ diff --git a/ujit.h b/ujit.h index 04c8dcae29..9186714387 100644 --- a/ujit.h +++ b/ujit.h @@ -31,6 +31,10 @@ typedef struct rb_iseq_struct rb_iseq_t; #define rb_iseq_t rb_iseq_t #endif +struct rb_ujit_options { + bool gen_stats; +}; + RUBY_SYMBOL_EXPORT_BEGIN RUBY_EXTERN bool rb_ujit_enabled; RUBY_SYMBOL_EXPORT_END @@ -44,8 +48,9 @@ bool rb_ujit_enabled_p(void) // Threshold==1 means compile on first execution #define UJIT_CALL_THRESHOLD (2u) +void rb_ujit_collect_vm_usage_insn(int insn); void rb_ujit_method_lookup_change(VALUE cme_or_cc); void rb_ujit_compile_iseq(const rb_iseq_t *iseq); -void rb_ujit_init(void); +void rb_ujit_init(struct rb_ujit_options *options); #endif // #ifndef UJIT_H diff --git a/ujit_codegen.c b/ujit_codegen.c index 7c7866f403..010e98ae86 100644 --- a/ujit_codegen.c +++ b/ujit_codegen.c @@ -179,6 +179,11 @@ ujit_gen_block(ctx_t* ctx, block_t* block) break; } +#if RUBY_DEBUG + mov(cb, REG0, const_ptr_opnd((void *)&rb_ujit_exec_insns_count)); + add(cb, mem_opnd(64, REG0, 0), imm_opnd(1)); +#endif + //fprintf(stderr, "compiling %d: %s\n", insn_idx, insn_name(opcode)); //print_str(cb, insn_name(opcode)); diff --git a/ujit_iface.c b/ujit_iface.c index 3eb134a3bc..2237d37f5f 100644 --- a/ujit_iface.c +++ b/ujit_iface.c @@ -23,8 +23,17 @@ VALUE cUjitBlock; VALUE cUjitDisasm; VALUE cUjitDisasmInsn; +bool rb_ujit_enabled; + +static int64_t vm_insns_count = 0; +int64_t rb_ujit_exec_insns_count = 0; + extern st_table * version_tbl; extern codeblock_t *cb; +// Hash table of encoded instructions +extern st_table *rb_encoded_insn_data; + +struct rb_ujit_options rb_ujit_opts; static const rb_data_type_t ujit_block_type = { "UJIT/Block", @@ -32,11 +41,6 @@ static const rb_data_type_t ujit_block_type = { 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; -bool rb_ujit_enabled; - -// Hash table of encoded instructions -extern st_table *rb_encoded_insn_data; - // Write the uJIT entry point pre-call bytes void cb_write_pre_call_bytes(codeblock_t* cb) @@ -412,14 +416,36 @@ ujit_disasm(VALUE self, VALUE code, VALUE from) } #endif +__attribute__((destructor)) +static void +print_ujit_stats(void) +{ + if (rb_ujit_opts.gen_stats) { + 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); + 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) +{ + vm_insns_count++; +} + void -rb_ujit_init(void) +rb_ujit_init(struct rb_ujit_options *options) { if (!ujit_scrape_successful || !PLATFORM_SUPPORTED_P) { return; } + rb_ujit_opts = *options; + rb_ujit_enabled = true; ujit_init_core(); diff --git a/ujit_iface.h b/ujit_iface.h index aa335dbf19..597b0cbbb5 100644 --- a/ujit_iface.h +++ b/ujit_iface.h @@ -20,6 +20,9 @@ struct rb_callcache; #define rb_callcache rb_callcache #endif +RUBY_EXTERN struct rb_ujit_options rb_ujit_opts; +RUBY_EXTERN int64_t rb_ujit_exec_insns_count; + void cb_write_pre_call_bytes(codeblock_t* cb); void cb_write_post_call_bytes(codeblock_t* cb); diff --git a/vm.c b/vm.c index 03813db448..1fb366f882 100644 --- a/vm.c +++ b/vm.c @@ -37,6 +37,7 @@ #include "vm_insnhelper.h" #include "ractor_core.h" #include "vm_sync.h" +#include "ujit.h" #include "builtin.h" @@ -344,6 +345,10 @@ static void vm_collect_usage_insn(int insn); static void vm_collect_usage_register(int reg, int isset); #endif +#if RUBY_DEBUG +static void vm_ujit_collect_usage_insn(int insn); +#endif + static VALUE vm_make_env_object(const rb_execution_context_t *ec, rb_control_frame_t *cfp); extern VALUE rb_vm_invoke_bmethod(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self, int argc, const VALUE *argv, int kw_splat, VALUE block_handler, @@ -4054,6 +4059,14 @@ MAYBE_UNUSED(static void (*ruby_vm_collect_usage_func_register)(int reg, int iss #endif +#if RUBY_DEBUG +static void +vm_ujit_collect_usage_insn(int insn) +{ + rb_ujit_collect_vm_usage_insn(insn); +} +#endif + #if VM_COLLECT_USAGE_DETAILS /* @param insn instruction number */ static void diff --git a/vm_insnhelper.h b/vm_insnhelper.h index 0d90eb9434..9c610304f8 100644 --- a/vm_insnhelper.h +++ b/vm_insnhelper.h @@ -25,6 +25,11 @@ MJIT_SYMBOL_EXPORT_END #define COLLECT_USAGE_OPERAND(insn, n, op) vm_collect_usage_operand((insn), (n), ((VALUE)(op))) #define COLLECT_USAGE_REGISTER(reg, s) vm_collect_usage_register((reg), (s)) +#elif RUBY_DEBUG +/* for --ujit-stats */ +#define COLLECT_USAGE_INSN(insn) vm_ujit_collect_usage_insn(insn) +#define COLLECT_USAGE_OPERAND(insn, n, op) /* none */ +#define COLLECT_USAGE_REGISTER(reg, s) /* none */ #else #define COLLECT_USAGE_INSN(insn) /* none */ #define COLLECT_USAGE_OPERAND(insn, n, op) /* none */