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;
unsigned int dump;
long backtrace_length_limit;
#if USE_ZJIT
void *zjit;
#endif
const char *crash_report;
@ -39,7 +42,6 @@ typedef struct ruby_cmdline_options {
#if USE_YJIT
unsigned int yjit: 1;
#endif
unsigned int zjit: 1;
} ruby_cmdline_options_t;
struct ruby_opt_message {

33
ruby.c
View File

@ -1184,6 +1184,21 @@ setup_yjit_options(const char *s)
}
#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:
*
@ -1454,9 +1469,13 @@ proc_long_options(ruby_cmdline_options_t *opt, const char *s, long argc, char **
#endif
}
else if (is_option_with_optarg("zjit", '-', true, false, false)) {
#if USE_ZJIT
FEATURE_SET(opt->features, FEATURE_BIT(zjit));
extern bool rb_zjit_parse_option();
rb_zjit_parse_option();
setup_zjit_options(opt, s);
#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) {
if (envopt) goto noenvopt_long;
@ -1799,9 +1818,8 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
#endif
#if USE_ZJIT
if (opt->zjit) {
fprintf(stderr, "test test\n");
extern void rb_zjit_init();
rb_zjit_init();
extern void rb_zjit_init(void *options);
rb_zjit_init(opt->zjit);
}
#endif
@ -2339,8 +2357,9 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
}
#endif
#if USE_ZJIT
if (FEATURE_SET_P(opt->features, zjit)) {
opt->zjit = true;
if (FEATURE_SET_P(opt->features, zjit) && !opt->zjit) {
extern void *rb_zjit_init_options(void);
opt->zjit = rb_zjit_init_options();
}
#endif

View File

@ -1,7 +1,9 @@
use std::mem;
use std::rc::Rc;
use std::cell::RefCell;
use crate::cruby::*;
use crate::virtualmem::*;
use crate::options::Options;
/// Block of memory into which instructions can be assembled
pub struct CodeBlock {
@ -111,18 +113,20 @@ impl crate::virtualmem::CodePtrBase for CodeBlock {
pub struct ZJITState {
/// Inline code block (fast path)
code_block: CodeBlock,
/// ZJIT command-line options
options: Options,
}
/// Private singleton instance of the codegen globals
static mut ZJIT_STATE: Option<ZJITState> = None;
impl ZJITState {
/// Initialize the ZJIT globals
pub fn init() {
let exec_mem_size: usize = 64 * 1024 * 1024; // TODO: support the option
/// Initialize the ZJIT globals, given options allocated by rb_zjit_init_options()
pub fn init(options: *const u8) {
#[cfg(not(test))]
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) };
// Memory protection syscalls need page-aligned addresses, so check it here. Assuming
@ -153,16 +157,18 @@ impl ZJITState {
CodeBlock::new(mem_block.clone())
};
#[cfg(not(test))]
let options = unsafe { Box::from_raw(options as *mut Options) };
#[cfg(not(test))] // TODO: can we get rid of this #[cfg]?
{
let zjit_state = ZJITState {
code_block: cb,
options: *options,
};
// 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
@ -174,4 +180,9 @@ impl ZJITState {
pub fn get_code_block() -> &'static mut CodeBlock {
&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 backend;
mod disasm;
mod options;
use backend::x86_emit;
use codegen::ZJITState;
@ -19,12 +20,13 @@ use crate::cruby::*;
#[no_mangle]
pub static mut rb_zjit_enabled_p: bool = false;
/// Initialize ZJIT, given options allocated by rb_zjit_init_options()
#[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.
// See https://doc.rust-lang.org/nomicon/exception-safety.html
let result = std::panic::catch_unwind(|| {
ZJITState::init();
ZJITState::init(options);
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]
pub extern "C" fn rb_zjit_iseq_gen_entry_point(iseq: IseqPtr, _ec: EcPtr) -> *const u8 {
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(());
}