diff options
Diffstat (limited to 'yjit/src/options.rs')
-rw-r--r-- | yjit/src/options.rs | 211 |
1 files changed, 175 insertions, 36 deletions
diff --git a/yjit/src/options.rs b/yjit/src/options.rs index 78a507ce11..59ec864bf5 100644 --- a/yjit/src/options.rs +++ b/yjit/src/options.rs @@ -1,5 +1,27 @@ -use std::ffi::CStr; -use crate::backend::ir::Assembler; +use std::{ffi::{CStr, CString}, ptr::null, fs::File}; +use crate::{backend::current::TEMP_REGS, stats::Counter}; +use std::os::raw::{c_char, c_int, c_uint}; + +// Call threshold for small deployments and command-line apps +pub static SMALL_CALL_THRESHOLD: u64 = 30; + +// Call threshold for larger deployments and production-sized applications +pub static LARGE_CALL_THRESHOLD: u64 = 120; + +// Number of live ISEQs after which we consider an app to be large +pub static LARGE_ISEQ_COUNT: u64 = 40_000; + +// This option is exposed to the C side in a global variable for performance, see vm.c +// Number of method calls after which to start generating code +// Threshold==1 means compile on first execution +#[no_mangle] +pub static mut rb_yjit_call_threshold: u64 = SMALL_CALL_THRESHOLD; + +// This option is exposed to the C side in a global variable for performance, see vm.c +// Number of execution requests after which a method is no longer +// considered hot. Raising this results in more generated code. +#[no_mangle] +pub static mut rb_yjit_cold_threshold: u64 = 200_000; // Command-line options #[derive(Clone, PartialEq, Eq, Debug)] @@ -9,13 +31,6 @@ pub struct Options { // Note that the command line argument is expressed in MiB and not bytes pub exec_mem_size: usize, - // Number of method calls after which to start generating code - // Threshold==1 means compile on first execution - pub call_threshold: usize, - - // Generate versions greedily until the limit is hit - pub greedy_versioning: bool, - // Disable the propagation of type information pub no_type_prop: bool, @@ -26,18 +41,21 @@ pub struct Options { // The number of registers allocated for stack temps pub num_temp_regs: usize, - // Capture and print out stats + // Capture stats pub gen_stats: bool, + // Print stats on exit (when gen_stats is also true) + pub print_stats: bool, + // Trace locations of exits - pub gen_trace_exits: bool, + pub trace_exits: Option<TraceExits>, // how often to sample exit trace data pub trace_exits_sample_rate: usize, - // Whether to start YJIT in paused state (initialize YJIT but don't - // compile anything) - pub pause: bool, + // Whether to enable YJIT at boot. This option prevents other + // YJIT tuning options from enabling YJIT at boot. + pub disable: bool, /// Dump compiled and executed instructions for debugging pub dump_insns: bool, @@ -50,26 +68,58 @@ pub struct Options { /// Verify context objects (debug mode only) pub verify_ctx: bool, + + /// Enable generating frame pointers (for x86. arm64 always does this) + pub frame_pointer: bool, + + /// Run code GC when exec_mem_size is reached. + pub code_gc: bool, + + /// Enable writing /tmp/perf-{pid}.map for Linux perf + pub perf_map: Option<PerfMap>, } // Initialize the options to default values pub static mut OPTIONS: Options = Options { - exec_mem_size: 64 * 1024 * 1024, - call_threshold: 30, - greedy_versioning: false, + exec_mem_size: 48 * 1024 * 1024, no_type_prop: false, max_versions: 4, num_temp_regs: 5, gen_stats: false, - gen_trace_exits: false, + trace_exits: None, + print_stats: true, trace_exits_sample_rate: 0, - pause: false, + disable: false, dump_insns: false, dump_disasm: None, verify_ctx: false, dump_iseq_disasm: None, + frame_pointer: false, + code_gc: false, + perf_map: None, }; +/// YJIT option descriptions for `ruby --help`. +static YJIT_OPTIONS: [(&str, &str); 9] = [ + ("--yjit-exec-mem-size=num", "Size of executable memory block in MiB (default: 48)."), + ("--yjit-call-threshold=num", "Number of calls to trigger JIT."), + ("--yjit-cold-threshold=num", "Global calls after which ISEQs not compiled (default: 200K)."), + ("--yjit-stats", "Enable collecting YJIT statistics."), + ("--yjit-disable", "Disable YJIT for lazily enabling it with RubyVM::YJIT.enable."), + ("--yjit-code-gc", "Run code GC when the code size reaches the limit."), + ("--yjit-perf", "Enable frame pointers and perf profiling."), + ("--yjit-trace-exits", "Record Ruby source location when exiting from generated code."), + ("--yjit-trace-exits-sample-rate=num", "Trace exit locations only every Nth occurrence."), +]; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum TraceExits { + // Trace all exits + All, + // Trace a specific counted exit + CountedExit(Counter), +} + #[derive(Clone, PartialEq, Eq, Debug)] pub enum DumpDisasm { // Dump to stdout @@ -78,12 +128,26 @@ pub enum DumpDisasm { File(String), } +/// Type of symbols to dump into /tmp/perf-{pid}.map +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum PerfMap { + // Dump ISEQ symbols + ISEQ, + // Dump YJIT codegen symbols + Codegen, +} + /// Macro to get an option value by name macro_rules! get_option { // Unsafe is ok here because options are initialized // once before any Ruby code executes ($option_name:ident) => { - unsafe { OPTIONS.$option_name } + { + // Make this a statement since attributes on expressions are experimental + #[allow(unused_unsafe)] + let ret = unsafe { OPTIONS.$option_name }; + ret + } }; } pub(crate) use get_option; @@ -133,7 +197,14 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> { }, ("call-threshold", _) => match opt_val.parse() { - Ok(n) => unsafe { OPTIONS.call_threshold = n }, + Ok(n) => unsafe { rb_yjit_call_threshold = n }, + Err(_) => { + return None; + } + }, + + ("cold-threshold", _) => match opt_val.parse() { + Ok(n) => unsafe { rb_yjit_cold_threshold = n }, Err(_) => { return None; } @@ -146,13 +217,13 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> { } }, - ("pause", "") => unsafe { - OPTIONS.pause = true; + ("disable", "") => unsafe { + OPTIONS.disable = true; }, ("temp-regs", _) => match opt_val.parse() { Ok(n) => { - assert!(n <= Assembler::TEMP_REGS.len(), "--yjit-temp-regs must be <= {}", Assembler::TEMP_REGS.len()); + assert!(n <= TEMP_REGS.len(), "--yjit-temp-regs must be <= {}", TEMP_REGS.len()); unsafe { OPTIONS.num_temp_regs = n } } Err(_) => { @@ -160,25 +231,78 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> { } }, - ("dump-disasm", _) => match opt_val.to_string().as_str() { - "" => unsafe { OPTIONS.dump_disasm = Some(DumpDisasm::Stdout) }, - directory => { - let pid = std::process::id(); - let path = format!("{directory}/yjit_{pid}.log"); - println!("YJIT disasm dump: {path}"); - unsafe { OPTIONS.dump_disasm = Some(DumpDisasm::File(path)) } - } + ("code-gc", _) => unsafe { + OPTIONS.code_gc = true; + }, + + ("perf", _) => match opt_val { + "" => unsafe { + OPTIONS.frame_pointer = true; + OPTIONS.perf_map = Some(PerfMap::ISEQ); + }, + "fp" => unsafe { OPTIONS.frame_pointer = true }, + "iseq" => unsafe { OPTIONS.perf_map = Some(PerfMap::ISEQ) }, + // Accept --yjit-perf=map for backward compatibility + "codegen" | "map" => unsafe { OPTIONS.perf_map = Some(PerfMap::Codegen) }, + _ => return None, }, + ("dump-disasm", _) => { + if !cfg!(feature = "disasm") { + eprintln!("WARNING: the {} option is only available when YJIT is built in dev mode, i.e. ./configure --enable-yjit=dev", opt_name); + } + + match opt_val { + "" => unsafe { OPTIONS.dump_disasm = Some(DumpDisasm::Stdout) }, + directory => { + let path = format!("{directory}/yjit_{}.log", std::process::id()); + match File::options().create(true).append(true).open(&path) { + Ok(_) => { + eprintln!("YJIT disasm dump: {path}"); + unsafe { OPTIONS.dump_disasm = Some(DumpDisasm::File(path)) } + } + Err(err) => eprintln!("Failed to create {path}: {err}"), + } + } + } + }, + ("dump-iseq-disasm", _) => unsafe { + if !cfg!(feature = "disasm") { + eprintln!("WARNING: the {} option is only available when YJIT is built in dev mode, i.e. ./configure --enable-yjit=dev", opt_name); + } + OPTIONS.dump_iseq_disasm = Some(opt_val.to_string()); }, - ("greedy-versioning", "") => unsafe { OPTIONS.greedy_versioning = true }, ("no-type-prop", "") => unsafe { OPTIONS.no_type_prop = true }, - ("stats", "") => unsafe { OPTIONS.gen_stats = true }, - ("trace-exits", "") => unsafe { OPTIONS.gen_trace_exits = true; OPTIONS.gen_stats = true; OPTIONS.trace_exits_sample_rate = 0 }, - ("trace-exits-sample-rate", sample_rate) => unsafe { OPTIONS.gen_trace_exits = true; OPTIONS.gen_stats = true; OPTIONS.trace_exits_sample_rate = sample_rate.parse().unwrap(); }, + ("stats", _) => match opt_val { + "" => unsafe { OPTIONS.gen_stats = true }, + "quiet" => unsafe { + OPTIONS.gen_stats = true; + OPTIONS.print_stats = false; + }, + _ => { + return None; + } + }, + ("trace-exits", _) => unsafe { + OPTIONS.gen_stats = true; + OPTIONS.trace_exits = match opt_val { + "" => Some(TraceExits::All), + name => match Counter::get(name) { + Some(counter) => Some(TraceExits::CountedExit(counter)), + None => return None, + }, + }; + }, + ("trace-exits-sample-rate", sample_rate) => unsafe { + OPTIONS.gen_stats = true; + if OPTIONS.trace_exits.is_none() { + OPTIONS.trace_exits = Some(TraceExits::All); + } + OPTIONS.trace_exits_sample_rate = sample_rate.parse().unwrap(); + }, ("dump-insns", "") => unsafe { OPTIONS.dump_insns = true }, ("verify-ctx", "") => unsafe { OPTIONS.verify_ctx = true }, @@ -206,3 +330,18 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> { // Option successfully parsed return Some(()); } + +/// Print YJIT options for `ruby --help`. `width` is width of option parts, and +/// `columns` is indent width of descriptions. +#[no_mangle] +pub extern "C" fn rb_yjit_show_usage(help: c_int, highlight: c_int, width: c_uint, columns: c_int) { + for &(name, description) in YJIT_OPTIONS.iter() { + extern "C" { + fn ruby_show_usage_line(name: *const c_char, secondary: *const c_char, description: *const c_char, + help: c_int, highlight: c_int, width: c_uint, columns: c_int); + } + let name = CString::new(name).unwrap(); + let description = CString::new(description).unwrap(); + unsafe { ruby_show_usage_line(name.as_ptr(), null(), description.as_ptr(), help, highlight, width, columns) } + } +} |