mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
Profile instructions for fixnum arithmetic (https://github.com/Shopify/zjit/pull/24)
* Profile instructions for fixnum arithmetic * Drop PartialEq from Type * Do not push PatchPoint onto the stack * Avoid pushing the output of the guards * Pop operands after guards * Test HIR from profiled runs * Implement Display for new instructions * Drop unused FIXNUM_BITS * Use a Rust function to split lines * Use Display for GuardType operands Co-authored-by: Max Bernstein <max@bernsteinbear.com> * Fix tests with Display-ed values --------- Co-authored-by: Max Bernstein <max@bernsteinbear.com>
This commit is contained in:
parent
9e31d29e0e
commit
8b2a4625cb
Notes:
git
2025-04-18 13:48:26 +00:00
6 changed files with 679 additions and 176 deletions
10
insns.def
10
insns.def
|
@ -1280,6 +1280,7 @@ opt_minus
|
|||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
// attr bool zjit_profile = true;
|
||||
{
|
||||
val = vm_opt_minus(recv, obj);
|
||||
|
||||
|
@ -1294,6 +1295,7 @@ opt_mult
|
|||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
// attr bool zjit_profile = true;
|
||||
{
|
||||
val = vm_opt_mult(recv, obj);
|
||||
|
||||
|
@ -1311,6 +1313,7 @@ opt_div
|
|||
/* In case of division by zero, it raises. Thus
|
||||
* ZeroDivisionError#initialize is called. */
|
||||
// attr bool leaf = false;
|
||||
// attr bool zjit_profile = true;
|
||||
{
|
||||
val = vm_opt_div(recv, obj);
|
||||
|
||||
|
@ -1327,6 +1330,7 @@ opt_mod
|
|||
(VALUE val)
|
||||
/* Same discussion as opt_div. */
|
||||
// attr bool leaf = false;
|
||||
// attr bool zjit_profile = true;
|
||||
{
|
||||
val = vm_opt_mod(recv, obj);
|
||||
|
||||
|
@ -1341,6 +1345,7 @@ opt_eq
|
|||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
// attr bool zjit_profile = true;
|
||||
{
|
||||
val = opt_equality(GET_ISEQ(), recv, obj, cd);
|
||||
|
||||
|
@ -1355,6 +1360,7 @@ opt_neq
|
|||
(CALL_DATA cd_eq, CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
// attr bool zjit_profile = true;
|
||||
{
|
||||
val = vm_opt_neq(GET_ISEQ(), cd, cd_eq, recv, obj);
|
||||
|
||||
|
@ -1369,6 +1375,7 @@ opt_lt
|
|||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
// attr bool zjit_profile = true;
|
||||
{
|
||||
val = vm_opt_lt(recv, obj);
|
||||
|
||||
|
@ -1383,6 +1390,7 @@ opt_le
|
|||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
// attr bool zjit_profile = true;
|
||||
{
|
||||
val = vm_opt_le(recv, obj);
|
||||
|
||||
|
@ -1397,6 +1405,7 @@ opt_gt
|
|||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
// attr bool zjit_profile = true;
|
||||
{
|
||||
val = vm_opt_gt(recv, obj);
|
||||
|
||||
|
@ -1411,6 +1420,7 @@ opt_ge
|
|||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
// attr bool zjit_profile = true;
|
||||
{
|
||||
val = vm_opt_ge(recv, obj);
|
||||
|
||||
|
|
|
@ -853,7 +853,9 @@ pub use manual_defs::*;
|
|||
|
||||
#[cfg(test)]
|
||||
pub mod test_utils {
|
||||
use crate::{options::init_options, state::ZJITState};
|
||||
use std::ptr::null;
|
||||
|
||||
use crate::{options::init_options, rb_zjit_enabled_p, state::ZJITState};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -879,6 +881,9 @@ pub mod test_utils {
|
|||
// Set up globals for convenience
|
||||
ZJITState::init(init_options());
|
||||
|
||||
// Enable zjit_* instructions
|
||||
unsafe { rb_zjit_enabled_p = true; }
|
||||
|
||||
let mut state: c_int = 0;
|
||||
unsafe { super::rb_protect(Some(callback_wrapper), VALUE((&mut data) as *mut _ as usize), &mut state) };
|
||||
// TODO(alan): there should be a way to print the exception instead of swallowing it
|
||||
|
@ -887,18 +892,66 @@ pub mod test_utils {
|
|||
|
||||
/// Compile an ISeq via `RubyVM::InstructionSequence.compile`.
|
||||
pub fn compile_to_iseq(program: &str) -> *const rb_iseq_t {
|
||||
let bytes = program.as_bytes().as_ptr() as *const c_char;
|
||||
unsafe {
|
||||
let program_str = rb_utf8_str_new(bytes, program.len().try_into().unwrap());
|
||||
let wrapped_iseq = rb_funcallv(rb_cISeq, ID!(compile), 1, &program_str);
|
||||
rb_iseqw_to_iseq(wrapped_iseq)
|
||||
}
|
||||
let wrapped_iseq = compile_to_wrapped_iseq(program);
|
||||
unsafe { rb_iseqw_to_iseq(wrapped_iseq) }
|
||||
}
|
||||
|
||||
pub fn define_class(name: &str, superclass: VALUE) -> VALUE {
|
||||
let name = CString::new(name).unwrap();
|
||||
unsafe { rb_define_class(name.as_ptr(), superclass) }
|
||||
}
|
||||
|
||||
/// Evaluate a given Ruby program
|
||||
pub fn eval(program: &str) -> VALUE {
|
||||
let wrapped_iseq = compile_to_wrapped_iseq(&unindent(program, false));
|
||||
unsafe { rb_funcallv(wrapped_iseq, ID!(eval), 0, null()) }
|
||||
}
|
||||
|
||||
/// Get the ISeq of a specified method
|
||||
pub fn get_method_iseq(name: &str) -> *const rb_iseq_t {
|
||||
let wrapped_iseq = eval(&format!("RubyVM::InstructionSequence.of(method(:{}))", name));
|
||||
unsafe { rb_iseqw_to_iseq(wrapped_iseq) }
|
||||
}
|
||||
|
||||
/// Remove the minimum indent from every line, skipping the first and last lines if `trim_lines`.
|
||||
pub fn unindent(string: &str, trim_lines: bool) -> String {
|
||||
// Break up a string into multiple lines
|
||||
let mut lines: Vec<String> = string.split_inclusive("\n").map(|s| s.to_string()).collect();
|
||||
if trim_lines { // raw string literals come with extra lines
|
||||
lines.remove(0);
|
||||
lines.remove(lines.len() - 1);
|
||||
}
|
||||
|
||||
// Count the minimum number of spaces
|
||||
let spaces = lines.iter().filter_map(|line| {
|
||||
for (i, ch) in line.as_bytes().iter().enumerate() {
|
||||
if *ch != b' ' {
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
None
|
||||
}).min().unwrap_or(0);
|
||||
|
||||
// Join lines, removing spaces
|
||||
let mut unindented: Vec<u8> = vec![];
|
||||
for line in lines.iter() {
|
||||
if line.len() > spaces {
|
||||
unindented.extend_from_slice(&line.as_bytes()[spaces..]);
|
||||
} else {
|
||||
unindented.extend_from_slice(&line.as_bytes());
|
||||
}
|
||||
}
|
||||
String::from_utf8(unindented).unwrap()
|
||||
}
|
||||
|
||||
/// Compile a program into a RubyVM::InstructionSequence object
|
||||
fn compile_to_wrapped_iseq(program: &str) -> VALUE {
|
||||
let bytes = program.as_bytes().as_ptr() as *const c_char;
|
||||
unsafe {
|
||||
let program_str = rb_utf8_str_new(bytes, program.len().try_into().unwrap());
|
||||
rb_funcallv(rb_cISeq, ID!(compile), 1, &program_str)
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
pub use test_utils::*;
|
||||
|
@ -941,6 +994,7 @@ pub(crate) mod ids {
|
|||
name: eq content: b"=="
|
||||
name: include_p content: b"include?"
|
||||
name: compile content: b"compile"
|
||||
name: eval content: b"eval"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
244
zjit/src/cruby_bindings.inc.rs
generated
244
zjit/src/cruby_bindings.inc.rs
generated
|
@ -598,118 +598,138 @@ pub const YARVINSN_setlocal_WC_1: ruby_vminsn_type = 107;
|
|||
pub const YARVINSN_putobject_INT2FIX_0_: ruby_vminsn_type = 108;
|
||||
pub const YARVINSN_putobject_INT2FIX_1_: ruby_vminsn_type = 109;
|
||||
pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 110;
|
||||
pub const YARVINSN_trace_nop: ruby_vminsn_type = 111;
|
||||
pub const YARVINSN_trace_getlocal: ruby_vminsn_type = 112;
|
||||
pub const YARVINSN_trace_setlocal: ruby_vminsn_type = 113;
|
||||
pub const YARVINSN_trace_getblockparam: ruby_vminsn_type = 114;
|
||||
pub const YARVINSN_trace_setblockparam: ruby_vminsn_type = 115;
|
||||
pub const YARVINSN_trace_getblockparamproxy: ruby_vminsn_type = 116;
|
||||
pub const YARVINSN_trace_getspecial: ruby_vminsn_type = 117;
|
||||
pub const YARVINSN_trace_setspecial: ruby_vminsn_type = 118;
|
||||
pub const YARVINSN_trace_getinstancevariable: ruby_vminsn_type = 119;
|
||||
pub const YARVINSN_trace_setinstancevariable: ruby_vminsn_type = 120;
|
||||
pub const YARVINSN_trace_getclassvariable: ruby_vminsn_type = 121;
|
||||
pub const YARVINSN_trace_setclassvariable: ruby_vminsn_type = 122;
|
||||
pub const YARVINSN_trace_opt_getconstant_path: ruby_vminsn_type = 123;
|
||||
pub const YARVINSN_trace_getconstant: ruby_vminsn_type = 124;
|
||||
pub const YARVINSN_trace_setconstant: ruby_vminsn_type = 125;
|
||||
pub const YARVINSN_trace_getglobal: ruby_vminsn_type = 126;
|
||||
pub const YARVINSN_trace_setglobal: ruby_vminsn_type = 127;
|
||||
pub const YARVINSN_trace_putnil: ruby_vminsn_type = 128;
|
||||
pub const YARVINSN_trace_putself: ruby_vminsn_type = 129;
|
||||
pub const YARVINSN_trace_putobject: ruby_vminsn_type = 130;
|
||||
pub const YARVINSN_trace_putspecialobject: ruby_vminsn_type = 131;
|
||||
pub const YARVINSN_trace_putstring: ruby_vminsn_type = 132;
|
||||
pub const YARVINSN_trace_putchilledstring: ruby_vminsn_type = 133;
|
||||
pub const YARVINSN_trace_concatstrings: ruby_vminsn_type = 134;
|
||||
pub const YARVINSN_trace_anytostring: ruby_vminsn_type = 135;
|
||||
pub const YARVINSN_trace_toregexp: ruby_vminsn_type = 136;
|
||||
pub const YARVINSN_trace_intern: ruby_vminsn_type = 137;
|
||||
pub const YARVINSN_trace_newarray: ruby_vminsn_type = 138;
|
||||
pub const YARVINSN_trace_pushtoarraykwsplat: ruby_vminsn_type = 139;
|
||||
pub const YARVINSN_trace_duparray: ruby_vminsn_type = 140;
|
||||
pub const YARVINSN_trace_duphash: ruby_vminsn_type = 141;
|
||||
pub const YARVINSN_trace_expandarray: ruby_vminsn_type = 142;
|
||||
pub const YARVINSN_trace_concatarray: ruby_vminsn_type = 143;
|
||||
pub const YARVINSN_trace_concattoarray: ruby_vminsn_type = 144;
|
||||
pub const YARVINSN_trace_pushtoarray: ruby_vminsn_type = 145;
|
||||
pub const YARVINSN_trace_splatarray: ruby_vminsn_type = 146;
|
||||
pub const YARVINSN_trace_splatkw: ruby_vminsn_type = 147;
|
||||
pub const YARVINSN_trace_newhash: ruby_vminsn_type = 148;
|
||||
pub const YARVINSN_trace_newrange: ruby_vminsn_type = 149;
|
||||
pub const YARVINSN_trace_pop: ruby_vminsn_type = 150;
|
||||
pub const YARVINSN_trace_dup: ruby_vminsn_type = 151;
|
||||
pub const YARVINSN_trace_dupn: ruby_vminsn_type = 152;
|
||||
pub const YARVINSN_trace_swap: ruby_vminsn_type = 153;
|
||||
pub const YARVINSN_trace_opt_reverse: ruby_vminsn_type = 154;
|
||||
pub const YARVINSN_trace_topn: ruby_vminsn_type = 155;
|
||||
pub const YARVINSN_trace_setn: ruby_vminsn_type = 156;
|
||||
pub const YARVINSN_trace_adjuststack: ruby_vminsn_type = 157;
|
||||
pub const YARVINSN_trace_defined: ruby_vminsn_type = 158;
|
||||
pub const YARVINSN_trace_definedivar: ruby_vminsn_type = 159;
|
||||
pub const YARVINSN_trace_checkmatch: ruby_vminsn_type = 160;
|
||||
pub const YARVINSN_trace_checkkeyword: ruby_vminsn_type = 161;
|
||||
pub const YARVINSN_trace_checktype: ruby_vminsn_type = 162;
|
||||
pub const YARVINSN_trace_defineclass: ruby_vminsn_type = 163;
|
||||
pub const YARVINSN_trace_definemethod: ruby_vminsn_type = 164;
|
||||
pub const YARVINSN_trace_definesmethod: ruby_vminsn_type = 165;
|
||||
pub const YARVINSN_trace_send: ruby_vminsn_type = 166;
|
||||
pub const YARVINSN_trace_sendforward: ruby_vminsn_type = 167;
|
||||
pub const YARVINSN_trace_opt_send_without_block: ruby_vminsn_type = 168;
|
||||
pub const YARVINSN_trace_objtostring: ruby_vminsn_type = 169;
|
||||
pub const YARVINSN_trace_opt_ary_freeze: ruby_vminsn_type = 170;
|
||||
pub const YARVINSN_trace_opt_hash_freeze: ruby_vminsn_type = 171;
|
||||
pub const YARVINSN_trace_opt_str_freeze: ruby_vminsn_type = 172;
|
||||
pub const YARVINSN_trace_opt_nil_p: ruby_vminsn_type = 173;
|
||||
pub const YARVINSN_trace_opt_str_uminus: ruby_vminsn_type = 174;
|
||||
pub const YARVINSN_trace_opt_duparray_send: ruby_vminsn_type = 175;
|
||||
pub const YARVINSN_trace_opt_newarray_send: ruby_vminsn_type = 176;
|
||||
pub const YARVINSN_trace_invokesuper: ruby_vminsn_type = 177;
|
||||
pub const YARVINSN_trace_invokesuperforward: ruby_vminsn_type = 178;
|
||||
pub const YARVINSN_trace_invokeblock: ruby_vminsn_type = 179;
|
||||
pub const YARVINSN_trace_leave: ruby_vminsn_type = 180;
|
||||
pub const YARVINSN_trace_throw: ruby_vminsn_type = 181;
|
||||
pub const YARVINSN_trace_jump: ruby_vminsn_type = 182;
|
||||
pub const YARVINSN_trace_branchif: ruby_vminsn_type = 183;
|
||||
pub const YARVINSN_trace_branchunless: ruby_vminsn_type = 184;
|
||||
pub const YARVINSN_trace_branchnil: ruby_vminsn_type = 185;
|
||||
pub const YARVINSN_trace_once: ruby_vminsn_type = 186;
|
||||
pub const YARVINSN_trace_opt_case_dispatch: ruby_vminsn_type = 187;
|
||||
pub const YARVINSN_trace_opt_plus: ruby_vminsn_type = 188;
|
||||
pub const YARVINSN_trace_opt_minus: ruby_vminsn_type = 189;
|
||||
pub const YARVINSN_trace_opt_mult: ruby_vminsn_type = 190;
|
||||
pub const YARVINSN_trace_opt_div: ruby_vminsn_type = 191;
|
||||
pub const YARVINSN_trace_opt_mod: ruby_vminsn_type = 192;
|
||||
pub const YARVINSN_trace_opt_eq: ruby_vminsn_type = 193;
|
||||
pub const YARVINSN_trace_opt_neq: ruby_vminsn_type = 194;
|
||||
pub const YARVINSN_trace_opt_lt: ruby_vminsn_type = 195;
|
||||
pub const YARVINSN_trace_opt_le: ruby_vminsn_type = 196;
|
||||
pub const YARVINSN_trace_opt_gt: ruby_vminsn_type = 197;
|
||||
pub const YARVINSN_trace_opt_ge: ruby_vminsn_type = 198;
|
||||
pub const YARVINSN_trace_opt_ltlt: ruby_vminsn_type = 199;
|
||||
pub const YARVINSN_trace_opt_and: ruby_vminsn_type = 200;
|
||||
pub const YARVINSN_trace_opt_or: ruby_vminsn_type = 201;
|
||||
pub const YARVINSN_trace_opt_aref: ruby_vminsn_type = 202;
|
||||
pub const YARVINSN_trace_opt_aset: ruby_vminsn_type = 203;
|
||||
pub const YARVINSN_trace_opt_aset_with: ruby_vminsn_type = 204;
|
||||
pub const YARVINSN_trace_opt_aref_with: ruby_vminsn_type = 205;
|
||||
pub const YARVINSN_trace_opt_length: ruby_vminsn_type = 206;
|
||||
pub const YARVINSN_trace_opt_size: ruby_vminsn_type = 207;
|
||||
pub const YARVINSN_trace_opt_empty_p: ruby_vminsn_type = 208;
|
||||
pub const YARVINSN_trace_opt_succ: ruby_vminsn_type = 209;
|
||||
pub const YARVINSN_trace_opt_not: ruby_vminsn_type = 210;
|
||||
pub const YARVINSN_trace_opt_regexpmatch2: ruby_vminsn_type = 211;
|
||||
pub const YARVINSN_trace_invokebuiltin: ruby_vminsn_type = 212;
|
||||
pub const YARVINSN_trace_opt_invokebuiltin_delegate: ruby_vminsn_type = 213;
|
||||
pub const YARVINSN_trace_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 214;
|
||||
pub const YARVINSN_trace_getlocal_WC_0: ruby_vminsn_type = 215;
|
||||
pub const YARVINSN_trace_getlocal_WC_1: ruby_vminsn_type = 216;
|
||||
pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 217;
|
||||
pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 218;
|
||||
pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 219;
|
||||
pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 220;
|
||||
pub const YARVINSN_trace_zjit_opt_plus: ruby_vminsn_type = 221;
|
||||
pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 222;
|
||||
pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 111;
|
||||
pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 112;
|
||||
pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 113;
|
||||
pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 114;
|
||||
pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 115;
|
||||
pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 116;
|
||||
pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 117;
|
||||
pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 118;
|
||||
pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 119;
|
||||
pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 120;
|
||||
pub const YARVINSN_trace_nop: ruby_vminsn_type = 121;
|
||||
pub const YARVINSN_trace_getlocal: ruby_vminsn_type = 122;
|
||||
pub const YARVINSN_trace_setlocal: ruby_vminsn_type = 123;
|
||||
pub const YARVINSN_trace_getblockparam: ruby_vminsn_type = 124;
|
||||
pub const YARVINSN_trace_setblockparam: ruby_vminsn_type = 125;
|
||||
pub const YARVINSN_trace_getblockparamproxy: ruby_vminsn_type = 126;
|
||||
pub const YARVINSN_trace_getspecial: ruby_vminsn_type = 127;
|
||||
pub const YARVINSN_trace_setspecial: ruby_vminsn_type = 128;
|
||||
pub const YARVINSN_trace_getinstancevariable: ruby_vminsn_type = 129;
|
||||
pub const YARVINSN_trace_setinstancevariable: ruby_vminsn_type = 130;
|
||||
pub const YARVINSN_trace_getclassvariable: ruby_vminsn_type = 131;
|
||||
pub const YARVINSN_trace_setclassvariable: ruby_vminsn_type = 132;
|
||||
pub const YARVINSN_trace_opt_getconstant_path: ruby_vminsn_type = 133;
|
||||
pub const YARVINSN_trace_getconstant: ruby_vminsn_type = 134;
|
||||
pub const YARVINSN_trace_setconstant: ruby_vminsn_type = 135;
|
||||
pub const YARVINSN_trace_getglobal: ruby_vminsn_type = 136;
|
||||
pub const YARVINSN_trace_setglobal: ruby_vminsn_type = 137;
|
||||
pub const YARVINSN_trace_putnil: ruby_vminsn_type = 138;
|
||||
pub const YARVINSN_trace_putself: ruby_vminsn_type = 139;
|
||||
pub const YARVINSN_trace_putobject: ruby_vminsn_type = 140;
|
||||
pub const YARVINSN_trace_putspecialobject: ruby_vminsn_type = 141;
|
||||
pub const YARVINSN_trace_putstring: ruby_vminsn_type = 142;
|
||||
pub const YARVINSN_trace_putchilledstring: ruby_vminsn_type = 143;
|
||||
pub const YARVINSN_trace_concatstrings: ruby_vminsn_type = 144;
|
||||
pub const YARVINSN_trace_anytostring: ruby_vminsn_type = 145;
|
||||
pub const YARVINSN_trace_toregexp: ruby_vminsn_type = 146;
|
||||
pub const YARVINSN_trace_intern: ruby_vminsn_type = 147;
|
||||
pub const YARVINSN_trace_newarray: ruby_vminsn_type = 148;
|
||||
pub const YARVINSN_trace_pushtoarraykwsplat: ruby_vminsn_type = 149;
|
||||
pub const YARVINSN_trace_duparray: ruby_vminsn_type = 150;
|
||||
pub const YARVINSN_trace_duphash: ruby_vminsn_type = 151;
|
||||
pub const YARVINSN_trace_expandarray: ruby_vminsn_type = 152;
|
||||
pub const YARVINSN_trace_concatarray: ruby_vminsn_type = 153;
|
||||
pub const YARVINSN_trace_concattoarray: ruby_vminsn_type = 154;
|
||||
pub const YARVINSN_trace_pushtoarray: ruby_vminsn_type = 155;
|
||||
pub const YARVINSN_trace_splatarray: ruby_vminsn_type = 156;
|
||||
pub const YARVINSN_trace_splatkw: ruby_vminsn_type = 157;
|
||||
pub const YARVINSN_trace_newhash: ruby_vminsn_type = 158;
|
||||
pub const YARVINSN_trace_newrange: ruby_vminsn_type = 159;
|
||||
pub const YARVINSN_trace_pop: ruby_vminsn_type = 160;
|
||||
pub const YARVINSN_trace_dup: ruby_vminsn_type = 161;
|
||||
pub const YARVINSN_trace_dupn: ruby_vminsn_type = 162;
|
||||
pub const YARVINSN_trace_swap: ruby_vminsn_type = 163;
|
||||
pub const YARVINSN_trace_opt_reverse: ruby_vminsn_type = 164;
|
||||
pub const YARVINSN_trace_topn: ruby_vminsn_type = 165;
|
||||
pub const YARVINSN_trace_setn: ruby_vminsn_type = 166;
|
||||
pub const YARVINSN_trace_adjuststack: ruby_vminsn_type = 167;
|
||||
pub const YARVINSN_trace_defined: ruby_vminsn_type = 168;
|
||||
pub const YARVINSN_trace_definedivar: ruby_vminsn_type = 169;
|
||||
pub const YARVINSN_trace_checkmatch: ruby_vminsn_type = 170;
|
||||
pub const YARVINSN_trace_checkkeyword: ruby_vminsn_type = 171;
|
||||
pub const YARVINSN_trace_checktype: ruby_vminsn_type = 172;
|
||||
pub const YARVINSN_trace_defineclass: ruby_vminsn_type = 173;
|
||||
pub const YARVINSN_trace_definemethod: ruby_vminsn_type = 174;
|
||||
pub const YARVINSN_trace_definesmethod: ruby_vminsn_type = 175;
|
||||
pub const YARVINSN_trace_send: ruby_vminsn_type = 176;
|
||||
pub const YARVINSN_trace_sendforward: ruby_vminsn_type = 177;
|
||||
pub const YARVINSN_trace_opt_send_without_block: ruby_vminsn_type = 178;
|
||||
pub const YARVINSN_trace_objtostring: ruby_vminsn_type = 179;
|
||||
pub const YARVINSN_trace_opt_ary_freeze: ruby_vminsn_type = 180;
|
||||
pub const YARVINSN_trace_opt_hash_freeze: ruby_vminsn_type = 181;
|
||||
pub const YARVINSN_trace_opt_str_freeze: ruby_vminsn_type = 182;
|
||||
pub const YARVINSN_trace_opt_nil_p: ruby_vminsn_type = 183;
|
||||
pub const YARVINSN_trace_opt_str_uminus: ruby_vminsn_type = 184;
|
||||
pub const YARVINSN_trace_opt_duparray_send: ruby_vminsn_type = 185;
|
||||
pub const YARVINSN_trace_opt_newarray_send: ruby_vminsn_type = 186;
|
||||
pub const YARVINSN_trace_invokesuper: ruby_vminsn_type = 187;
|
||||
pub const YARVINSN_trace_invokesuperforward: ruby_vminsn_type = 188;
|
||||
pub const YARVINSN_trace_invokeblock: ruby_vminsn_type = 189;
|
||||
pub const YARVINSN_trace_leave: ruby_vminsn_type = 190;
|
||||
pub const YARVINSN_trace_throw: ruby_vminsn_type = 191;
|
||||
pub const YARVINSN_trace_jump: ruby_vminsn_type = 192;
|
||||
pub const YARVINSN_trace_branchif: ruby_vminsn_type = 193;
|
||||
pub const YARVINSN_trace_branchunless: ruby_vminsn_type = 194;
|
||||
pub const YARVINSN_trace_branchnil: ruby_vminsn_type = 195;
|
||||
pub const YARVINSN_trace_once: ruby_vminsn_type = 196;
|
||||
pub const YARVINSN_trace_opt_case_dispatch: ruby_vminsn_type = 197;
|
||||
pub const YARVINSN_trace_opt_plus: ruby_vminsn_type = 198;
|
||||
pub const YARVINSN_trace_opt_minus: ruby_vminsn_type = 199;
|
||||
pub const YARVINSN_trace_opt_mult: ruby_vminsn_type = 200;
|
||||
pub const YARVINSN_trace_opt_div: ruby_vminsn_type = 201;
|
||||
pub const YARVINSN_trace_opt_mod: ruby_vminsn_type = 202;
|
||||
pub const YARVINSN_trace_opt_eq: ruby_vminsn_type = 203;
|
||||
pub const YARVINSN_trace_opt_neq: ruby_vminsn_type = 204;
|
||||
pub const YARVINSN_trace_opt_lt: ruby_vminsn_type = 205;
|
||||
pub const YARVINSN_trace_opt_le: ruby_vminsn_type = 206;
|
||||
pub const YARVINSN_trace_opt_gt: ruby_vminsn_type = 207;
|
||||
pub const YARVINSN_trace_opt_ge: ruby_vminsn_type = 208;
|
||||
pub const YARVINSN_trace_opt_ltlt: ruby_vminsn_type = 209;
|
||||
pub const YARVINSN_trace_opt_and: ruby_vminsn_type = 210;
|
||||
pub const YARVINSN_trace_opt_or: ruby_vminsn_type = 211;
|
||||
pub const YARVINSN_trace_opt_aref: ruby_vminsn_type = 212;
|
||||
pub const YARVINSN_trace_opt_aset: ruby_vminsn_type = 213;
|
||||
pub const YARVINSN_trace_opt_aset_with: ruby_vminsn_type = 214;
|
||||
pub const YARVINSN_trace_opt_aref_with: ruby_vminsn_type = 215;
|
||||
pub const YARVINSN_trace_opt_length: ruby_vminsn_type = 216;
|
||||
pub const YARVINSN_trace_opt_size: ruby_vminsn_type = 217;
|
||||
pub const YARVINSN_trace_opt_empty_p: ruby_vminsn_type = 218;
|
||||
pub const YARVINSN_trace_opt_succ: ruby_vminsn_type = 219;
|
||||
pub const YARVINSN_trace_opt_not: ruby_vminsn_type = 220;
|
||||
pub const YARVINSN_trace_opt_regexpmatch2: ruby_vminsn_type = 221;
|
||||
pub const YARVINSN_trace_invokebuiltin: ruby_vminsn_type = 222;
|
||||
pub const YARVINSN_trace_opt_invokebuiltin_delegate: ruby_vminsn_type = 223;
|
||||
pub const YARVINSN_trace_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 224;
|
||||
pub const YARVINSN_trace_getlocal_WC_0: ruby_vminsn_type = 225;
|
||||
pub const YARVINSN_trace_getlocal_WC_1: ruby_vminsn_type = 226;
|
||||
pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 227;
|
||||
pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 228;
|
||||
pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 229;
|
||||
pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 230;
|
||||
pub const YARVINSN_trace_zjit_opt_plus: ruby_vminsn_type = 231;
|
||||
pub const YARVINSN_trace_zjit_opt_minus: ruby_vminsn_type = 232;
|
||||
pub const YARVINSN_trace_zjit_opt_mult: ruby_vminsn_type = 233;
|
||||
pub const YARVINSN_trace_zjit_opt_div: ruby_vminsn_type = 234;
|
||||
pub const YARVINSN_trace_zjit_opt_mod: ruby_vminsn_type = 235;
|
||||
pub const YARVINSN_trace_zjit_opt_eq: ruby_vminsn_type = 236;
|
||||
pub const YARVINSN_trace_zjit_opt_neq: ruby_vminsn_type = 237;
|
||||
pub const YARVINSN_trace_zjit_opt_lt: ruby_vminsn_type = 238;
|
||||
pub const YARVINSN_trace_zjit_opt_le: ruby_vminsn_type = 239;
|
||||
pub const YARVINSN_trace_zjit_opt_gt: ruby_vminsn_type = 240;
|
||||
pub const YARVINSN_trace_zjit_opt_ge: ruby_vminsn_type = 241;
|
||||
pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 242;
|
||||
pub type ruby_vminsn_type = u32;
|
||||
pub type rb_iseq_callback = ::std::option::Option<
|
||||
unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void),
|
||||
|
|
466
zjit/src/hir.rs
466
zjit/src/hir.rs
|
@ -2,12 +2,12 @@
|
|||
#![allow(non_upper_case_globals)]
|
||||
|
||||
use crate::{
|
||||
cruby::*,
|
||||
get_option,
|
||||
options::DumpHIR, profile::{get_or_create_iseq_payload, InsnProfile}
|
||||
cruby::*, get_option, hir_type::types::Fixnum, options::DumpHIR, profile::get_or_create_iseq_payload
|
||||
};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use crate::hir_type::Type;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct InsnId(pub usize);
|
||||
|
||||
|
@ -89,6 +89,36 @@ pub enum Invariant {
|
|||
},
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Invariant {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::BOPRedefined { klass, bop } => {
|
||||
write!(f, "BOPRedefined(")?;
|
||||
match *klass {
|
||||
INTEGER_REDEFINED_OP_FLAG => write!(f, "INTEGER_REDEFINED_OP_FLAG")?,
|
||||
_ => write!(f, "{klass}")?,
|
||||
}
|
||||
write!(f, ", ")?;
|
||||
match *bop {
|
||||
BOP_PLUS => write!(f, "BOP_PLUS")?,
|
||||
BOP_MINUS => write!(f, "BOP_MINUS")?,
|
||||
BOP_MULT => write!(f, "BOP_MULT")?,
|
||||
BOP_DIV => write!(f, "BOP_DIV")?,
|
||||
BOP_MOD => write!(f, "BOP_MOD")?,
|
||||
BOP_EQ => write!(f, "BOP_EQ")?,
|
||||
BOP_NEQ => write!(f, "BOP_NEQ")?,
|
||||
BOP_LT => write!(f, "BOP_LT")?,
|
||||
BOP_LE => write!(f, "BOP_LE")?,
|
||||
BOP_GT => write!(f, "BOP_GT")?,
|
||||
BOP_GE => write!(f, "BOP_GE")?,
|
||||
_ => write!(f, "{bop}")?,
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Const {
|
||||
Value(VALUE),
|
||||
|
@ -161,12 +191,22 @@ pub enum Insn {
|
|||
// Control flow instructions
|
||||
Return { val: InsnId },
|
||||
|
||||
/// Fixnum + Fixnum
|
||||
FixnumAdd { recv: InsnId, obj: InsnId },
|
||||
/// Fixnum +, -, *, /, %, ==, !=, <, <=, >, >=
|
||||
FixnumAdd { left: InsnId, right: InsnId },
|
||||
FixnumSub { left: InsnId, right: InsnId },
|
||||
FixnumMult { left: InsnId, right: InsnId },
|
||||
FixnumDiv { left: InsnId, right: InsnId },
|
||||
FixnumMod { left: InsnId, right: InsnId },
|
||||
FixnumEq { left: InsnId, right: InsnId },
|
||||
FixnumNeq { left: InsnId, right: InsnId },
|
||||
FixnumLt { left: InsnId, right: InsnId },
|
||||
FixnumLe { left: InsnId, right: InsnId },
|
||||
FixnumGt { left: InsnId, right: InsnId },
|
||||
FixnumGe { left: InsnId, right: InsnId },
|
||||
|
||||
/// Side-exist if val doesn't have the expected type.
|
||||
// TODO: Replace is_fixnum with the type lattice
|
||||
GuardType { val: InsnId, is_fixnum: bool },
|
||||
GuardType { val: InsnId, guard_type: Type },
|
||||
|
||||
/// Generate no code (or padding if necessary) and insert a patch point
|
||||
/// that can be rewritten to a side exit when the Invariant is broken.
|
||||
|
@ -365,23 +405,36 @@ impl<'a> std::fmt::Display for FunctionPrinter<'a> {
|
|||
}
|
||||
write!(f, " {insn_id} = ")?;
|
||||
match &fun.insns[insn_id.0] {
|
||||
Insn::Param { idx } => { write!(f, "Param {idx}")?; }
|
||||
Insn::Const { val } => { write!(f, "Const {val}")?; }
|
||||
Insn::IfTrue { val, target } => { write!(f, "IfTrue {val}, {target}")?; }
|
||||
Insn::IfFalse { val, target } => { write!(f, "IfFalse {val}, {target}")?; }
|
||||
Insn::Jump(target) => { write!(f, "Jump {target}")?; }
|
||||
Insn::Return { val } => { write!(f, "Return {val}")?; }
|
||||
Insn::Param { idx } => { write!(f, "Param {idx}")?; }
|
||||
Insn::NewArray { count } => { write!(f, "NewArray {count}")?; }
|
||||
Insn::ArraySet { idx, val } => { write!(f, "ArraySet {idx}, {val}")?; }
|
||||
Insn::ArrayDup { val } => { write!(f, "ArrayDup {val}")?; }
|
||||
Insn::Test { val } => { write!(f, "Test {val}")?; }
|
||||
Insn::Snapshot { state } => { write!(f, "Snapshot {state}")?; }
|
||||
Insn::Jump(target) => { write!(f, "Jump {target}")?; }
|
||||
Insn::IfTrue { val, target } => { write!(f, "IfTrue {val}, {target}")?; }
|
||||
Insn::IfFalse { val, target } => { write!(f, "IfFalse {val}, {target}")?; }
|
||||
Insn::Send { self_val, call_info, args } => {
|
||||
write!(f, "Send {self_val}, :{}", call_info.name)?;
|
||||
for arg in args {
|
||||
write!(f, ", {arg}")?;
|
||||
}
|
||||
}
|
||||
Insn::Test { val } => { write!(f, "Test {val}")?; }
|
||||
Insn::Snapshot { state } => { write!(f, "Snapshot {state}")?; }
|
||||
Insn::Return { val } => { write!(f, "Return {val}")?; }
|
||||
Insn::FixnumAdd { left, right } => { write!(f, "FixnumAdd {left}, {right}")?; },
|
||||
Insn::FixnumSub { left, right } => { write!(f, "FixnumSub {left}, {right}")?; },
|
||||
Insn::FixnumMult { left, right } => { write!(f, "FixnumMult {left}, {right}")?; },
|
||||
Insn::FixnumDiv { left, right } => { write!(f, "FixnumDiv {left}, {right}")?; },
|
||||
Insn::FixnumMod { left, right } => { write!(f, "FixnumMod {left}, {right}")?; },
|
||||
Insn::FixnumEq { left, right } => { write!(f, "FixnumEq {left}, {right}")?; },
|
||||
Insn::FixnumNeq { left, right } => { write!(f, "FixnumNeq {left}, {right}")?; },
|
||||
Insn::FixnumLt { left, right } => { write!(f, "FixnumLt {left}, {right}")?; },
|
||||
Insn::FixnumLe { left, right } => { write!(f, "FixnumLe {left}, {right}")?; },
|
||||
Insn::FixnumGt { left, right } => { write!(f, "FixnumGt {left}, {right}")?; },
|
||||
Insn::FixnumGe { left, right } => { write!(f, "FixnumGe {left}, {right}")?; },
|
||||
Insn::GuardType { val, guard_type } => { write!(f, "GuardType {val}, {guard_type}")?; },
|
||||
Insn::PatchPoint(invariant) => { write!(f, "PatchPoint {invariant:}")?; },
|
||||
insn => { write!(f, "{insn:?}")?; }
|
||||
}
|
||||
writeln!(f, "")?;
|
||||
|
@ -440,6 +493,13 @@ impl FrameState {
|
|||
self.stack.last().ok_or_else(|| ParseError::StackUnderflow(self.clone())).copied()
|
||||
}
|
||||
|
||||
fn stack_opnd(&self, idx: usize) -> Result<InsnId, ParseError> {
|
||||
match self.stack.get(self.stack.len() - idx - 1) {
|
||||
Some(&opnd) => Ok(opnd),
|
||||
_ => Err(ParseError::StackUnderflow(self.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Result<InsnId, ParseError> {
|
||||
self.stack.pop().ok_or_else(|| ParseError::StackUnderflow(self.clone()))
|
||||
}
|
||||
|
@ -586,8 +646,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||
|
||||
// try_into() call below is unfortunate. Maybe pick i32 instead of usize for opcodes.
|
||||
let opcode: u32 = unsafe { rb_iseq_opcode_at_pc(iseq, pc) }
|
||||
.try_into()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
// Preserve the actual index for the instruction being compiled
|
||||
let current_insn_idx = insn_idx;
|
||||
// Move to the next instruction to compile
|
||||
insn_idx += insn_len(opcode as usize);
|
||||
|
||||
|
@ -705,29 +767,126 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||
}
|
||||
|
||||
YARVINSN_opt_plus | YARVINSN_zjit_opt_plus => {
|
||||
let right = state.pop()?;
|
||||
let left = state.pop()?;
|
||||
if let Some(InsnProfile::OptPlus { recv_is_fixnum: true, obj_is_fixnum: true }) = payload.get_insn_profile(insn_idx as usize) {
|
||||
state.push(fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_PLUS })));
|
||||
let left_fixnum = fun.push_insn(block, Insn::GuardType { val: left, is_fixnum: true });
|
||||
state.push(left_fixnum);
|
||||
let right_fixnum = fun.push_insn(block, Insn::GuardType { val: right, is_fixnum: true });
|
||||
state.push(right_fixnum);
|
||||
state.push(fun.push_insn(block, Insn::FixnumAdd { recv: left_fixnum, obj: right_fixnum }));
|
||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_PLUS }));
|
||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||
state.push(fun.push_insn(block, Insn::FixnumAdd { left, right }));
|
||||
} else {
|
||||
let right = state.pop()?;
|
||||
let left = state.pop()?;
|
||||
state.push(fun.push_insn(block, Insn::Send { self_val: left, call_info: CallInfo { name: "+".into() }, args: vec![right] }));
|
||||
}
|
||||
}
|
||||
YARVINSN_opt_div => {
|
||||
let right = state.pop()?;
|
||||
let left = state.pop()?;
|
||||
state.push(fun.push_insn(block, Insn::Send { self_val: left, call_info: CallInfo { name: "/".into() }, args: vec![right] }));
|
||||
YARVINSN_opt_minus | YARVINSN_zjit_opt_minus => {
|
||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_MINUS }));
|
||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||
state.push(fun.push_insn(block, Insn::FixnumSub { left, right }));
|
||||
} else {
|
||||
let right = state.pop()?;
|
||||
let left = state.pop()?;
|
||||
state.push(fun.push_insn(block, Insn::Send { self_val: left, call_info: CallInfo { name: "-".into() }, args: vec![right] }));
|
||||
}
|
||||
}
|
||||
YARVINSN_opt_mult | YARVINSN_zjit_opt_mult => {
|
||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_MULT }));
|
||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||
state.push(fun.push_insn(block, Insn::FixnumMult { left, right }));
|
||||
} else {
|
||||
let right = state.pop()?;
|
||||
let left = state.pop()?;
|
||||
state.push(fun.push_insn(block, Insn::Send { self_val: left, call_info: CallInfo { name: "*".into() }, args: vec![right] }));
|
||||
}
|
||||
}
|
||||
YARVINSN_opt_div | YARVINSN_zjit_opt_div => {
|
||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_DIV }));
|
||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||
state.push(fun.push_insn(block, Insn::FixnumDiv { left, right }));
|
||||
} else {
|
||||
let right = state.pop()?;
|
||||
let left = state.pop()?;
|
||||
state.push(fun.push_insn(block, Insn::Send { self_val: left, call_info: CallInfo { name: "/".into() }, args: vec![right] }));
|
||||
}
|
||||
}
|
||||
YARVINSN_opt_mod | YARVINSN_zjit_opt_mod => {
|
||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_MOD }));
|
||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||
state.push(fun.push_insn(block, Insn::FixnumMod { left, right }));
|
||||
} else {
|
||||
let right = state.pop()?;
|
||||
let left = state.pop()?;
|
||||
state.push(fun.push_insn(block, Insn::Send { self_val: left, call_info: CallInfo { name: "%".into() }, args: vec![right] }));
|
||||
}
|
||||
}
|
||||
|
||||
YARVINSN_opt_lt => {
|
||||
let right = state.pop()?;
|
||||
let left = state.pop()?;
|
||||
state.push(fun.push_insn(block, Insn::Send { self_val: left, call_info: CallInfo { name: "<".into() }, args: vec![right] }));
|
||||
YARVINSN_opt_eq | YARVINSN_zjit_opt_eq => {
|
||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_EQ }));
|
||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||
state.push(fun.push_insn(block, Insn::FixnumEq { left, right }));
|
||||
} else {
|
||||
let right = state.pop()?;
|
||||
let left = state.pop()?;
|
||||
state.push(fun.push_insn(block, Insn::Send { self_val: left, call_info: CallInfo { name: "==".into() }, args: vec![right] }));
|
||||
}
|
||||
}
|
||||
YARVINSN_opt_neq | YARVINSN_zjit_opt_neq => {
|
||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_NEQ }));
|
||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||
state.push(fun.push_insn(block, Insn::FixnumNeq { left, right }));
|
||||
} else {
|
||||
let right = state.pop()?;
|
||||
let left = state.pop()?;
|
||||
state.push(fun.push_insn(block, Insn::Send { self_val: left, call_info: CallInfo { name: "!=".into() }, args: vec![right] }));
|
||||
}
|
||||
}
|
||||
YARVINSN_opt_lt | YARVINSN_zjit_opt_lt => {
|
||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_LT }));
|
||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||
state.push(fun.push_insn(block, Insn::FixnumLt { left, right }));
|
||||
} else {
|
||||
let right = state.pop()?;
|
||||
let left = state.pop()?;
|
||||
state.push(fun.push_insn(block, Insn::Send { self_val: left, call_info: CallInfo { name: "<".into() }, args: vec![right] }));
|
||||
}
|
||||
}
|
||||
YARVINSN_opt_le | YARVINSN_zjit_opt_le => {
|
||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_LE }));
|
||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||
state.push(fun.push_insn(block, Insn::FixnumLe { left, right }));
|
||||
} else {
|
||||
let right = state.pop()?;
|
||||
let left = state.pop()?;
|
||||
state.push(fun.push_insn(block, Insn::Send { self_val: left, call_info: CallInfo { name: "<=".into() }, args: vec![right] }));
|
||||
}
|
||||
}
|
||||
YARVINSN_opt_gt | YARVINSN_zjit_opt_gt => {
|
||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_GT }));
|
||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||
state.push(fun.push_insn(block, Insn::FixnumGt { left, right }));
|
||||
} else {
|
||||
let right = state.pop()?;
|
||||
let left = state.pop()?;
|
||||
state.push(fun.push_insn(block, Insn::Send { self_val: left, call_info: CallInfo { name: "<".into() }, args: vec![right] }));
|
||||
}
|
||||
}
|
||||
YARVINSN_opt_ge | YARVINSN_zjit_opt_ge => {
|
||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_GE }));
|
||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||
state.push(fun.push_insn(block, Insn::FixnumGe { left, right }));
|
||||
} else {
|
||||
let right = state.pop()?;
|
||||
let left = state.pop()?;
|
||||
state.push(fun.push_insn(block, Insn::Send { self_val: left, call_info: CallInfo { name: "<=".into() }, args: vec![right] }));
|
||||
}
|
||||
}
|
||||
YARVINSN_opt_ltlt => {
|
||||
let right = state.pop()?;
|
||||
|
@ -788,6 +947,18 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||
Ok(fun)
|
||||
}
|
||||
|
||||
/// Generate guards for two fixnum outputs
|
||||
fn guard_two_fixnums(state: &mut FrameState, fun: &mut Function, block: BlockId) -> Result<(InsnId, InsnId), ParseError> {
|
||||
let left = fun.push_insn(block, Insn::GuardType { val: state.stack_opnd(1)?, guard_type: Fixnum });
|
||||
let right = fun.push_insn(block, Insn::GuardType { val: state.stack_opnd(0)?, guard_type: Fixnum });
|
||||
|
||||
// Pop operands after guards for side exits
|
||||
state.pop()?;
|
||||
state.pop()?;
|
||||
|
||||
Ok((left, right))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod union_find_tests {
|
||||
use super::UnionFind;
|
||||
|
@ -842,6 +1013,19 @@ mod tests {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_hir {
|
||||
($method:expr, $hir:expr) => {
|
||||
{
|
||||
let iseq = get_method_iseq($method);
|
||||
let function = iseq_to_hir(iseq).unwrap();
|
||||
|
||||
let actual_hir = format!("{}", FunctionPrinter::without_snapshot(&function));
|
||||
let expected_hir = unindent($hir, true);
|
||||
assert_eq!(actual_hir, expected_hir);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boot_vm() {
|
||||
crate::cruby::with_rubyvm(|| {
|
||||
|
@ -923,4 +1107,224 @@ mod tests {
|
|||
assert_matches!(function.insns.get(16), Some(Insn::Return { val: InsnId(14) }));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opt_plus_fixnum() {
|
||||
crate::cruby::with_rubyvm(|| {
|
||||
eval("
|
||||
def test(a, b) = a + b
|
||||
test(1, 2); test(1, 2)
|
||||
");
|
||||
assert_hir!("test", "
|
||||
bb0:
|
||||
v0 = Param 0
|
||||
v1 = Param 1
|
||||
v5 = PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS)
|
||||
v6 = GuardType v0, Fixnum
|
||||
v7 = GuardType v1, Fixnum
|
||||
v8 = FixnumAdd v6, v7
|
||||
v10 = Return v8
|
||||
");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opt_minus_fixnum() {
|
||||
crate::cruby::with_rubyvm(|| {
|
||||
eval("
|
||||
def test(a, b) = a - b
|
||||
test(1, 2); test(1, 2)
|
||||
");
|
||||
assert_hir!("test", "
|
||||
bb0:
|
||||
v0 = Param 0
|
||||
v1 = Param 1
|
||||
v5 = PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MINUS)
|
||||
v6 = GuardType v0, Fixnum
|
||||
v7 = GuardType v1, Fixnum
|
||||
v8 = FixnumSub v6, v7
|
||||
v10 = Return v8
|
||||
");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opt_mult_fixnum() {
|
||||
crate::cruby::with_rubyvm(|| {
|
||||
eval("
|
||||
def test(a, b) = a * b
|
||||
test(1, 2); test(1, 2)
|
||||
");
|
||||
assert_hir!("test", "
|
||||
bb0:
|
||||
v0 = Param 0
|
||||
v1 = Param 1
|
||||
v5 = PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MULT)
|
||||
v6 = GuardType v0, Fixnum
|
||||
v7 = GuardType v1, Fixnum
|
||||
v8 = FixnumMult v6, v7
|
||||
v10 = Return v8
|
||||
");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opt_div_fixnum() {
|
||||
crate::cruby::with_rubyvm(|| {
|
||||
eval("
|
||||
def test(a, b) = a / b
|
||||
test(1, 2); test(1, 2)
|
||||
");
|
||||
assert_hir!("test", "
|
||||
bb0:
|
||||
v0 = Param 0
|
||||
v1 = Param 1
|
||||
v5 = PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_DIV)
|
||||
v6 = GuardType v0, Fixnum
|
||||
v7 = GuardType v1, Fixnum
|
||||
v8 = FixnumDiv v6, v7
|
||||
v10 = Return v8
|
||||
");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opt_mod_fixnum() {
|
||||
crate::cruby::with_rubyvm(|| {
|
||||
eval("
|
||||
def test(a, b) = a % b
|
||||
test(1, 2); test(1, 2)
|
||||
");
|
||||
assert_hir!("test", "
|
||||
bb0:
|
||||
v0 = Param 0
|
||||
v1 = Param 1
|
||||
v5 = PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MOD)
|
||||
v6 = GuardType v0, Fixnum
|
||||
v7 = GuardType v1, Fixnum
|
||||
v8 = FixnumMod v6, v7
|
||||
v10 = Return v8
|
||||
");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opt_eq_fixnum() {
|
||||
crate::cruby::with_rubyvm(|| {
|
||||
eval("
|
||||
def test(a, b) = a == b
|
||||
test(1, 2); test(1, 2)
|
||||
");
|
||||
assert_hir!("test", "
|
||||
bb0:
|
||||
v0 = Param 0
|
||||
v1 = Param 1
|
||||
v5 = PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_EQ)
|
||||
v6 = GuardType v0, Fixnum
|
||||
v7 = GuardType v1, Fixnum
|
||||
v8 = FixnumEq v6, v7
|
||||
v10 = Return v8
|
||||
");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opt_neq_fixnum() {
|
||||
crate::cruby::with_rubyvm(|| {
|
||||
eval("
|
||||
def test(a, b) = a != b
|
||||
test(1, 2); test(1, 2)
|
||||
");
|
||||
assert_hir!("test", "
|
||||
bb0:
|
||||
v0 = Param 0
|
||||
v1 = Param 1
|
||||
v5 = PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_NEQ)
|
||||
v6 = GuardType v0, Fixnum
|
||||
v7 = GuardType v1, Fixnum
|
||||
v8 = FixnumNeq v6, v7
|
||||
v10 = Return v8
|
||||
");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opt_lt_fixnum() {
|
||||
crate::cruby::with_rubyvm(|| {
|
||||
eval("
|
||||
def test(a, b) = a < b
|
||||
test(1, 2); test(1, 2)
|
||||
");
|
||||
assert_hir!("test", "
|
||||
bb0:
|
||||
v0 = Param 0
|
||||
v1 = Param 1
|
||||
v5 = PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LT)
|
||||
v6 = GuardType v0, Fixnum
|
||||
v7 = GuardType v1, Fixnum
|
||||
v8 = FixnumLt v6, v7
|
||||
v10 = Return v8
|
||||
");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opt_le_fixnum() {
|
||||
crate::cruby::with_rubyvm(|| {
|
||||
eval("
|
||||
def test(a, b) = a <= b
|
||||
test(1, 2); test(1, 2)
|
||||
");
|
||||
assert_hir!("test", "
|
||||
bb0:
|
||||
v0 = Param 0
|
||||
v1 = Param 1
|
||||
v5 = PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LE)
|
||||
v6 = GuardType v0, Fixnum
|
||||
v7 = GuardType v1, Fixnum
|
||||
v8 = FixnumLe v6, v7
|
||||
v10 = Return v8
|
||||
");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opt_gt_fixnum() {
|
||||
crate::cruby::with_rubyvm(|| {
|
||||
eval("
|
||||
def test(a, b) = a > b
|
||||
test(1, 2); test(1, 2)
|
||||
");
|
||||
assert_hir!("test", "
|
||||
bb0:
|
||||
v0 = Param 0
|
||||
v1 = Param 1
|
||||
v5 = PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_GT)
|
||||
v6 = GuardType v0, Fixnum
|
||||
v7 = GuardType v1, Fixnum
|
||||
v8 = FixnumGt v6, v7
|
||||
v10 = Return v8
|
||||
");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opt_ge_fixnum() {
|
||||
crate::cruby::with_rubyvm(|| {
|
||||
eval("
|
||||
def test(a, b) = a >= b
|
||||
test(1, 2); test(1, 2)
|
||||
");
|
||||
assert_hir!("test", "
|
||||
bb0:
|
||||
v0 = Param 0
|
||||
v1 = Param 1
|
||||
v5 = PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_GE)
|
||||
v6 = GuardType v0, Fixnum
|
||||
v7 = GuardType v1, Fixnum
|
||||
v8 = FixnumGe v6, v7
|
||||
v10 = Return v8
|
||||
");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
use core::ffi::c_void;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::cruby::*;
|
||||
use crate::{cruby::*, hir_type::{types::{Bottom, Fixnum}, Type}};
|
||||
|
||||
/// Ephemeral state for profiling runtime information
|
||||
struct Profiler {
|
||||
|
@ -46,42 +46,57 @@ pub extern "C" fn rb_zjit_profile_insn(opcode: ruby_vminsn_type, ec: EcPtr) {
|
|||
/// Profile a YARV instruction
|
||||
fn profile_insn(profiler: &mut Profiler, opcode: ruby_vminsn_type) {
|
||||
match opcode {
|
||||
YARVINSN_opt_plus => profile_opt_plus(profiler),
|
||||
YARVINSN_opt_plus => profile_operands(profiler, 2),
|
||||
YARVINSN_opt_minus => profile_operands(profiler, 2),
|
||||
YARVINSN_opt_mult => profile_operands(profiler, 2),
|
||||
YARVINSN_opt_div => profile_operands(profiler, 2),
|
||||
YARVINSN_opt_mod => profile_operands(profiler, 2),
|
||||
YARVINSN_opt_eq => profile_operands(profiler, 2),
|
||||
YARVINSN_opt_neq => profile_operands(profiler, 2),
|
||||
YARVINSN_opt_lt => profile_operands(profiler, 2),
|
||||
YARVINSN_opt_le => profile_operands(profiler, 2),
|
||||
YARVINSN_opt_gt => profile_operands(profiler, 2),
|
||||
YARVINSN_opt_ge => profile_operands(profiler, 2),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Profile opt_plus instruction
|
||||
fn profile_opt_plus(profiler: &mut Profiler) {
|
||||
let recv = profiler.peek_at_stack(1);
|
||||
let obj = profiler.peek_at_stack(0);
|
||||
|
||||
/// Profile the Type of top-`n` stack operands
|
||||
fn profile_operands(profiler: &mut Profiler, n: usize) {
|
||||
let payload = get_or_create_iseq_payload(profiler.iseq);
|
||||
payload.insns.insert(profiler.insn_idx, InsnProfile::OptPlus {
|
||||
// TODO: profile the type and union it with past results
|
||||
recv_is_fixnum: recv.fixnum_p(),
|
||||
obj_is_fixnum: obj.fixnum_p(),
|
||||
});
|
||||
let mut types = if let Some(types) = payload.opnd_types.get(&profiler.insn_idx) {
|
||||
types.clone()
|
||||
} else {
|
||||
vec![Bottom; n]
|
||||
};
|
||||
|
||||
for i in 0..n {
|
||||
let opnd_type = Type::from_value(profiler.peek_at_stack((n - i - 1) as isize));
|
||||
types[i] = types[i].union(opnd_type);
|
||||
}
|
||||
|
||||
payload.opnd_types.insert(profiler.insn_idx, types);
|
||||
}
|
||||
|
||||
/// Profiling information for each YARV instruction
|
||||
pub enum InsnProfile {
|
||||
// TODO: Change it to { recv: Type, obj: Type } once the type lattice is merged
|
||||
OptPlus { recv_is_fixnum: bool, obj_is_fixnum: bool },
|
||||
}
|
||||
|
||||
/// This is all the data YJIT stores on an iseq. This will be dynamically allocated by C code
|
||||
/// This is all the data ZJIT stores on an iseq. This will be dynamically allocated by C code
|
||||
/// C code should pass an &mut IseqPayload to us when calling into ZJIT.
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct IseqPayload {
|
||||
/// Profiling information for each YARV instruction, indexed by the instruction index
|
||||
insns: HashMap<usize, InsnProfile>,
|
||||
/// Type information of YARV instruction operands, indexed by the instruction index
|
||||
opnd_types: HashMap<usize, Vec<Type>>,
|
||||
}
|
||||
|
||||
impl IseqPayload {
|
||||
/// Get the instruction profile for a given instruction index
|
||||
pub fn get_insn_profile(&self, insn_idx: usize) -> Option<&InsnProfile> {
|
||||
self.insns.get(&insn_idx)
|
||||
/// Get profiled operand types for a given instruction index
|
||||
pub fn get_operand_types(&self, insn_idx: usize) -> Option<&[Type]> {
|
||||
self.opnd_types.get(&insn_idx).map(|types| types.as_slice())
|
||||
}
|
||||
|
||||
pub fn have_two_fixnums(&self, insn_idx: usize) -> bool {
|
||||
match self.get_operand_types(insn_idx) {
|
||||
Some([left, right]) => left.is_subtype(Fixnum) && right.is_subtype(Fixnum),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ zjit-test: libminiruby.a
|
|||
RUBY_BUILD_DIR='$(TOP_BUILD_DIR)' \
|
||||
RUBY_LD_FLAGS='$(LDFLAGS) $(XLDFLAGS) $(MAINLIBS)' \
|
||||
CARGO_TARGET_DIR='$(ZJIT_CARGO_TARGET_DIR)' \
|
||||
$(CARGO) nextest run --manifest-path '$(top_srcdir)/zjit/Cargo.toml'
|
||||
$(CARGO) nextest run --manifest-path '$(top_srcdir)/zjit/Cargo.toml' $(ZJIT_TESTS)
|
||||
|
||||
# A library for booting miniruby in tests.
|
||||
# Why not use libruby-static.a for this?
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue