Implement Options as part of ZJITState

This commit is contained in:
Takashi Kokubun 2025-02-10 13:31:15 -08:00
parent 464e74f20c
commit 0252ce1cd4
Notes: git 2025-04-18 13:49:13 +00:00
5 changed files with 112 additions and 28 deletions

View File

@ -23,6 +23,9 @@ typedef struct ruby_cmdline_options {
ruby_features_t warn; ruby_features_t warn;
unsigned int dump; unsigned int dump;
long backtrace_length_limit; long backtrace_length_limit;
#if USE_ZJIT
void *zjit;
#endif
const char *crash_report; const char *crash_report;
@ -39,7 +42,6 @@ typedef struct ruby_cmdline_options {
#if USE_YJIT #if USE_YJIT
unsigned int yjit: 1; unsigned int yjit: 1;
#endif #endif
unsigned int zjit: 1;
} ruby_cmdline_options_t; } ruby_cmdline_options_t;
struct ruby_opt_message { struct ruby_opt_message {

33
ruby.c
View File

@ -1184,6 +1184,21 @@ setup_yjit_options(const char *s)
} }
#endif #endif
#if USE_ZJIT
static void
setup_zjit_options(ruby_cmdline_options_t *opt, const char *s)
{
// The option parsing is done in zjit/src/options.rs
extern void *rb_zjit_init_options(void);
extern bool rb_zjit_parse_option(void *options, const char *s);
if (!opt->zjit) opt->zjit = rb_zjit_init_options();
if (!rb_zjit_parse_option(opt->zjit, s)) {
rb_raise(rb_eRuntimeError, "invalid ZJIT option '%s' (--help will show valid zjit options)", s);
}
}
#endif
/* /*
* Following proc_*_option functions are tree kinds: * Following proc_*_option functions are tree kinds:
* *
@ -1454,9 +1469,13 @@ proc_long_options(ruby_cmdline_options_t *opt, const char *s, long argc, char **
#endif #endif
} }
else if (is_option_with_optarg("zjit", '-', true, false, false)) { else if (is_option_with_optarg("zjit", '-', true, false, false)) {
#if USE_ZJIT
FEATURE_SET(opt->features, FEATURE_BIT(zjit)); FEATURE_SET(opt->features, FEATURE_BIT(zjit));
extern bool rb_zjit_parse_option(); setup_zjit_options(opt, s);
rb_zjit_parse_option(); #else
rb_warn("Ruby was built without ZJIT support."
" You may need to install rustc to build Ruby with ZJIT.");
#endif
} }
else if (strcmp("yydebug", s) == 0) { else if (strcmp("yydebug", s) == 0) {
if (envopt) goto noenvopt_long; if (envopt) goto noenvopt_long;
@ -1799,9 +1818,8 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
#endif #endif
#if USE_ZJIT #if USE_ZJIT
if (opt->zjit) { if (opt->zjit) {
fprintf(stderr, "test test\n"); extern void rb_zjit_init(void *options);
extern void rb_zjit_init(); rb_zjit_init(opt->zjit);
rb_zjit_init();
} }
#endif #endif
@ -2339,8 +2357,9 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
} }
#endif #endif
#if USE_ZJIT #if USE_ZJIT
if (FEATURE_SET_P(opt->features, zjit)) { if (FEATURE_SET_P(opt->features, zjit) && !opt->zjit) {
opt->zjit = true; extern void *rb_zjit_init_options(void);
opt->zjit = rb_zjit_init_options();
} }
#endif #endif

View File

@ -1,7 +1,9 @@
use std::mem;
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use crate::cruby::*; use crate::cruby::*;
use crate::virtualmem::*; use crate::virtualmem::*;
use crate::options::Options;
/// Block of memory into which instructions can be assembled /// Block of memory into which instructions can be assembled
pub struct CodeBlock { pub struct CodeBlock {
@ -111,18 +113,20 @@ impl crate::virtualmem::CodePtrBase for CodeBlock {
pub struct ZJITState { pub struct ZJITState {
/// Inline code block (fast path) /// Inline code block (fast path)
code_block: CodeBlock, code_block: CodeBlock,
/// ZJIT command-line options
options: Options,
} }
/// Private singleton instance of the codegen globals /// Private singleton instance of the codegen globals
static mut ZJIT_STATE: Option<ZJITState> = None; static mut ZJIT_STATE: Option<ZJITState> = None;
impl ZJITState { impl ZJITState {
/// Initialize the ZJIT globals /// Initialize the ZJIT globals, given options allocated by rb_zjit_init_options()
pub fn init() { pub fn init(options: *const u8) {
let exec_mem_size: usize = 64 * 1024 * 1024; // TODO: support the option
#[cfg(not(test))] #[cfg(not(test))]
let cb = { let cb = {
let exec_mem_size: usize = 64 * 1024 * 1024; // TODO: implement the option
let virt_block: *mut u8 = unsafe { rb_zjit_reserve_addr_space(64 * 1024 * 1024) }; let virt_block: *mut u8 = unsafe { rb_zjit_reserve_addr_space(64 * 1024 * 1024) };
// Memory protection syscalls need page-aligned addresses, so check it here. Assuming // Memory protection syscalls need page-aligned addresses, so check it here. Assuming
@ -153,16 +157,18 @@ impl ZJITState {
CodeBlock::new(mem_block.clone()) CodeBlock::new(mem_block.clone())
}; };
#[cfg(not(test))] let options = unsafe { Box::from_raw(options as *mut Options) };
let zjit_state = ZJITState { #[cfg(not(test))] // TODO: can we get rid of this #[cfg]?
code_block: cb, {
}; let zjit_state = ZJITState {
code_block: cb,
options: *options,
};
// Initialize the codegen globals instance // Initialize the codegen globals instance
#[cfg(not(test))] unsafe { ZJIT_STATE = Some(zjit_state); }
unsafe {
ZJIT_STATE = Some(zjit_state);
} }
mem::drop(options);
} }
/// Get a mutable reference to the codegen globals instance /// Get a mutable reference to the codegen globals instance
@ -174,4 +180,9 @@ impl ZJITState {
pub fn get_code_block() -> &'static mut CodeBlock { pub fn get_code_block() -> &'static mut CodeBlock {
&mut ZJITState::get_instance().code_block &mut ZJITState::get_instance().code_block
} }
// Get a mutable reference to the options
pub fn get_options() -> &'static mut Options {
&mut ZJITState::get_instance().options
}
} }

View File

@ -10,6 +10,7 @@ mod virtualmem;
mod asm; mod asm;
mod backend; mod backend;
mod disasm; mod disasm;
mod options;
use backend::x86_emit; use backend::x86_emit;
use codegen::ZJITState; use codegen::ZJITState;
@ -19,12 +20,13 @@ use crate::cruby::*;
#[no_mangle] #[no_mangle]
pub static mut rb_zjit_enabled_p: bool = false; pub static mut rb_zjit_enabled_p: bool = false;
/// Initialize ZJIT, given options allocated by rb_zjit_init_options()
#[no_mangle] #[no_mangle]
pub extern "C" fn rb_zjit_init() { pub extern "C" fn rb_zjit_init(options: *const u8) {
// Catch panics to avoid UB for unwinding into C frames. // Catch panics to avoid UB for unwinding into C frames.
// See https://doc.rust-lang.org/nomicon/exception-safety.html // See https://doc.rust-lang.org/nomicon/exception-safety.html
let result = std::panic::catch_unwind(|| { let result = std::panic::catch_unwind(|| {
ZJITState::init(); ZJITState::init(options);
rb_bug_panic_hook(); rb_bug_panic_hook();
@ -71,12 +73,6 @@ fn rb_bug_panic_hook() {
})); }));
} }
#[no_mangle]
pub extern "C" fn rb_zjit_parse_option() -> bool {
println!("parsing zjit options");
false
}
#[no_mangle] #[no_mangle]
pub extern "C" fn rb_zjit_iseq_gen_entry_point(iseq: IseqPtr, _ec: EcPtr) -> *const u8 { pub extern "C" fn rb_zjit_iseq_gen_entry_point(iseq: IseqPtr, _ec: EcPtr) -> *const u8 {
ir::iseq_to_ssa(iseq).unwrap(); ir::iseq_to_ssa(iseq).unwrap();

56
zjit/src/options.rs Normal file
View File

@ -0,0 +1,56 @@
use std::{ffi::CStr, os::raw::c_char};
#[derive(Clone, Copy, Debug)]
pub struct Options {
/// Dump all compiled instructions of target cb.
pub dump_disasm: bool,
}
/// Allocate Options on the heap, initialize it, and return the address of it.
/// The return value will be modified by rb_zjit_parse_option() and then
/// passed to rb_zjit_init() for initialization.
#[no_mangle]
pub extern "C" fn rb_zjit_init_options() -> *const u8 {
let options = Options {
dump_disasm: false,
};
Box::into_raw(Box::new(options)) as *const u8
}
/// Parse a --zjit* command-line flag
#[no_mangle]
pub extern "C" fn rb_zjit_parse_option(options: *const u8, str_ptr: *const c_char) -> bool {
let options = unsafe { &mut *(options as *mut Options) };
parse_option(options, str_ptr).is_some()
}
/// Expected to receive what comes after the third dash in "--zjit-*".
/// Empty string means user passed only "--zjit". C code rejects when
/// they pass exact "--zjit-".
fn parse_option(options: &mut Options, str_ptr: *const std::os::raw::c_char) -> Option<()> {
let c_str: &CStr = unsafe { CStr::from_ptr(str_ptr) };
let opt_str: &str = c_str.to_str().ok()?;
// Split the option name and value strings
// Note that some options do not contain an assignment
let parts = opt_str.split_once('=');
let (opt_name, opt_val) = match parts {
Some((before_eq, after_eq)) => (before_eq, after_eq),
None => (opt_str, ""),
};
// Match on the option name and value strings
match (opt_name, opt_val) {
("", "") => {}, // Simply --zjit
("dump-disasm", "") => options.dump_disasm = true,
// Option name not recognized
_ => {
return None;
}
}
// Option successfully parsed
return Some(());
}