summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStan Lo <[email protected]>2025-06-16 20:24:33 +0100
committer<[email protected]>2025-06-16 12:24:33 -0700
commitcce4bfdca9e001ccac38b4f3125627b5c0d0e9f2 ()
tree752075b9c05a9a198c90bbb70422ef8beb331c3c
parent260ac23a53e8db93087216d115aa4b054e9cf35b (diff)
ZJIT: Add support for putspecialobject (#13565)
* ZJIT: Add support for putspecialobject * Address feedback * Update tests * Adjust the indentation of a Ruby test --------- Co-authored-by: Takashi Kokubun <[email protected]>
Notes: Merged-By: k0kubun <[email protected]>
-rw-r--r--test/ruby/test_zjit.rb26
-rw-r--r--vm_insnhelper.c8
-rw-r--r--zjit/src/codegen.rs17
-rw-r--r--zjit/src/cruby.rs2
-rw-r--r--zjit/src/hir.rs77
5 files changed, 128 insertions, 2 deletions
@@ -652,6 +652,32 @@ class TestZJIT < Test::Unit::TestCase
}, call_threshold: 2
end
# tool/ruby_vm/views/*.erb relies on the zjit instructions a) being contiguous and
# b) being reliably ordered after all the other instructions.
def test_instruction_order
@@ -5555,6 +5555,14 @@ vm_get_special_object(const VALUE *const reg_ep,
}
}
static VALUE
vm_concat_array(VALUE ary1, VALUE ary2st)
{
@@ -7,7 +7,7 @@ use crate::state::ZJITState;
use crate::{asm::CodeBlock, cruby::*, options::debug, virtualmem::CodePtr};
use crate::invariants::{iseq_escapes_ep, track_no_ep_escape_assumption};
use crate::backend::lir::{self, asm_comment, Assembler, Opnd, Target, CFP, C_ARG_OPNDS, C_RET_OPND, EC, SP};
-use crate::hir::{iseq_to_hir, Block, BlockId, BranchEdge, CallInfo, RangeType, SELF_PARAM_IDX};
use crate::hir::{Const, FrameState, Function, Insn, InsnId};
use crate::hir_type::{types::Fixnum, Type};
use crate::options::get_option;
@@ -279,6 +279,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
Insn::GetGlobal { id, state: _ } => gen_getglobal(asm, *id),
Insn::SetIvar { self_val, id, val, state: _ } => return gen_setivar(asm, opnd!(self_val), *id, opnd!(val)),
Insn::SideExit { state } => return gen_side_exit(jit, asm, &function.frame_state(*state)),
_ => {
debug!("ZJIT: gen_function: unexpected insn {:?}", insn);
return None;
@@ -347,6 +348,20 @@ fn gen_side_exit(jit: &mut JITState, asm: &mut Assembler, state: &FrameState) ->
Some(())
}
/// Compile an interpreter entry block to be inserted into an ISEQ
fn gen_entry_prologue(asm: &mut Assembler, iseq: IseqPtr) {
asm_comment!(asm, "ZJIT entry point: {}", iseq_get_location(iseq, 0));
@@ -133,6 +133,7 @@ unsafe extern "C" {
pub fn rb_str_setbyte(str: VALUE, index: VALUE, value: VALUE) -> VALUE;
pub fn rb_vm_splat_array(flag: VALUE, ary: VALUE) -> VALUE;
pub fn rb_vm_concat_array(ary1: VALUE, ary2st: VALUE) -> VALUE;
pub fn rb_vm_concat_to_array(ary1: VALUE, ary2st: VALUE) -> VALUE;
pub fn rb_vm_defined(
ec: EcPtr,
@@ -213,6 +214,7 @@ pub use rb_vm_ci_flag as vm_ci_flag;
pub use rb_vm_ci_kwarg as vm_ci_kwarg;
pub use rb_METHOD_ENTRY_VISI as METHOD_ENTRY_VISI;
pub use rb_RCLASS_ORIGIN as RCLASS_ORIGIN;
/// Helper so we can get a Rust string for insn_name()
pub fn insn_name(opcode: usize) -> String {
@@ -138,6 +138,40 @@ impl Invariant {
}
}
/// Print adaptor for [`Invariant`]. See [`PtrPrintMap`].
pub struct InvariantPrinter<'a> {
inner: Invariant,
@@ -365,6 +399,9 @@ pub enum Insn {
StringCopy { val: InsnId },
StringIntern { val: InsnId },
/// Call `to_a` on `val` if the method is defined, or make a new array `[val]` otherwise.
ToArray { val: InsnId, state: InsnId },
/// Call `to_a` on `val` if the method is defined, or make a new array `[val]` otherwise. If we
@@ -645,6 +682,9 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
Insn::ArrayExtend { left, right, .. } => write!(f, "ArrayExtend {left}, {right}"),
Insn::ArrayPush { array, val, .. } => write!(f, "ArrayPush {array}, {val}"),
Insn::SideExit { .. } => write!(f, "SideExit"),
insn => { write!(f, "{insn:?}") }
}
}
@@ -958,6 +998,7 @@ impl Function {
FixnumGe { left, right } => FixnumGe { left: find!(*left), right: find!(*right) },
FixnumLt { left, right } => FixnumLt { left: find!(*left), right: find!(*right) },
FixnumLe { left, right } => FixnumLe { left: find!(*left), right: find!(*right) },
SendWithoutBlock { self_val, call_info, cd, args, state } => SendWithoutBlock {
self_val: find!(*self_val),
call_info: call_info.clone(),
@@ -1074,6 +1115,7 @@ impl Function {
Insn::FixnumLe { .. } => types::BoolExact,
Insn::FixnumGt { .. } => types::BoolExact,
Insn::FixnumGe { .. } => types::BoolExact,
Insn::SendWithoutBlock { .. } => types::BasicObject,
Insn::SendWithoutBlockDirect { .. } => types::BasicObject,
Insn::Send { .. } => types::BasicObject,
@@ -1561,7 +1603,8 @@ impl Function {
necessary[insn_id.0] = true;
match self.find(insn_id) {
Insn::Const { .. } | Insn::Param { .. }
- | Insn::Point(..) | Insn::GetConstantPath { .. } =>
{}
Insn::ArrayMax { elements, state }
| Insn::NewArray { elements, state } => {
@@ -2095,6 +2138,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
YARVINSN_nop => {},
YARVINSN_putnil => { state.stack_push(fun.push_insn(block, Insn::Const { val: Const::Value(Qnil) })); },
YARVINSN_putobject => { state.stack_push(fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) })); },
YARVINSN_putstring | YARVINSN_putchilledstring => {
// TODO(max): Do something different for chilled string
let val = fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) });
@@ -3523,6 +3570,13 @@ mod tests {
assert_method_hir("test", expect![[r#"
fn test:
bb0(v0:BasicObject, v1:BasicObject):
SideExit
"#]]);
}
@@ -3957,6 +4011,27 @@ mod tests {
}
#[test]
fn test_branchnil() {
eval("
def test(x) = x&.itself