summaryrefslogtreecommitdiff
path: root/zjit/src
diff options
context:
space:
mode:
Diffstat (limited to 'zjit/src')
-rw-r--r--zjit/src/backend/arm64/mod.rs1
-rw-r--r--zjit/src/codegen.rs24
-rw-r--r--zjit/src/hir.rs58
-rw-r--r--zjit/src/hir/tests.rs76
4 files changed, 156 insertions, 3 deletions
diff --git a/zjit/src/backend/arm64/mod.rs b/zjit/src/backend/arm64/mod.rs
index 6ed855ddf9..ee15627d89 100644
--- a/zjit/src/backend/arm64/mod.rs
+++ b/zjit/src/backend/arm64/mod.rs
@@ -1717,7 +1717,6 @@ mod tests {
use super::*;
use insta::assert_snapshot;
- use crate::hir;
static TEMP_REGS: [Reg; 5] = [X1_REG, X9_REG, X10_REG, X14_REG, X15_REG];
diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs
index 8714518866..a77bd7debd 100644
--- a/zjit/src/codegen.rs
+++ b/zjit/src/codegen.rs
@@ -481,6 +481,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
&Insn::SendWithoutBlock { cd, state, reason, .. } => gen_send_without_block(jit, asm, cd, &function.frame_state(state), reason),
Insn::SendWithoutBlockDirect { cme, iseq, recv, args, kw_bits, state, .. } => gen_send_iseq_direct(cb, jit, asm, *cme, *iseq, opnd!(recv), opnds!(args), *kw_bits, &function.frame_state(*state), None),
&Insn::InvokeSuper { cd, blockiseq, state, reason, .. } => gen_invokesuper(jit, asm, cd, blockiseq, &function.frame_state(state), reason),
+ &Insn::InvokeSuperForward { cd, blockiseq, state, reason, .. } => gen_invokesuperforward(jit, asm, cd, blockiseq, &function.frame_state(state), reason),
&Insn::InvokeBlock { cd, state, reason, .. } => gen_invokeblock(jit, asm, cd, &function.frame_state(state), reason),
Insn::InvokeProc { recv, args, state, kw_splat } => gen_invokeproc(jit, asm, opnd!(recv), opnds!(args), *kw_splat, &function.frame_state(*state)),
// Ensure we have enough room fit ec, self, and arguments
@@ -1638,6 +1639,29 @@ fn gen_invokesuper(
)
}
+/// Compile a dynamic dispatch for `super` with `...`
+fn gen_invokesuperforward(
+ jit: &mut JITState,
+ asm: &mut Assembler,
+ cd: *const rb_call_data,
+ blockiseq: IseqPtr,
+ state: &FrameState,
+ reason: SendFallbackReason,
+) -> lir::Opnd {
+ gen_incr_send_fallback_counter(asm, reason);
+
+ gen_prepare_non_leaf_call(jit, asm, state);
+ asm_comment!(asm, "call super with dynamic dispatch (forwarding)");
+ unsafe extern "C" {
+ fn rb_vm_invokesuperforward(ec: EcPtr, cfp: CfpPtr, cd: VALUE, blockiseq: IseqPtr) -> VALUE;
+ }
+ asm_ccall!(
+ asm,
+ rb_vm_invokesuperforward,
+ EC, CFP, Opnd::const_ptr(cd), VALUE::from(blockiseq).into()
+ )
+}
+
/// Compile a string resurrection
fn gen_string_copy(asm: &mut Assembler, recv: Opnd, chilled: bool, state: &FrameState) -> Opnd {
// TODO: split rb_ec_str_resurrect into separate functions
diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs
index 79004c8737..51ab45937c 100644
--- a/zjit/src/hir.rs
+++ b/zjit/src/hir.rs
@@ -936,6 +936,14 @@ pub enum Insn {
state: InsnId,
reason: SendFallbackReason,
},
+ InvokeSuperForward {
+ recv: InsnId,
+ cd: *const rb_call_data,
+ blockiseq: IseqPtr,
+ args: Vec<InsnId>,
+ state: InsnId,
+ reason: SendFallbackReason,
+ },
InvokeBlock {
cd: *const rb_call_data,
args: Vec<InsnId>,
@@ -1183,6 +1191,7 @@ impl Insn {
Insn::Send { .. } => effects::Any,
Insn::SendForward { .. } => effects::Any,
Insn::InvokeSuper { .. } => effects::Any,
+ Insn::InvokeSuperForward { .. } => effects::Any,
Insn::InvokeBlock { .. } => effects::Any,
Insn::SendWithoutBlockDirect { .. } => effects::Any,
Insn::InvokeBuiltin { .. } => effects::Any,
@@ -1471,6 +1480,14 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
write!(f, " # SendFallbackReason: {reason}")?;
Ok(())
}
+ Insn::InvokeSuperForward { recv, blockiseq, args, reason, .. } => {
+ write!(f, "InvokeSuperForward {recv}, {:p}", self.ptr_map.map_ptr(blockiseq))?;
+ for arg in args {
+ write!(f, ", {arg}")?;
+ }
+ write!(f, " # SendFallbackReason: {reason}")?;
+ Ok(())
+ }
Insn::InvokeBlock { args, reason, .. } => {
write!(f, "InvokeBlock")?;
for arg in args {
@@ -2277,6 +2294,14 @@ impl Function {
state,
reason,
},
+ &InvokeSuperForward { recv, cd, blockiseq, ref args, state, reason } => InvokeSuperForward {
+ recv: find!(recv),
+ cd,
+ blockiseq,
+ args: find_vec!(args),
+ state,
+ reason,
+ },
&InvokeBlock { cd, ref args, state, reason } => InvokeBlock {
cd,
args: find_vec!(args),
@@ -2356,6 +2381,7 @@ impl Function {
| SendForward { reason, .. }
| SendWithoutBlock { reason, .. }
| InvokeSuper { reason, .. }
+ | InvokeSuperForward { reason, .. }
| InvokeBlock { reason, .. }
=> *reason = dynamic_send_reason,
_ => unreachable!("unexpected instruction {} at {insn_id}", self.find(insn_id))
@@ -2477,6 +2503,7 @@ impl Function {
Insn::Send { .. } => types::BasicObject,
Insn::SendForward { .. } => types::BasicObject,
Insn::InvokeSuper { .. } => types::BasicObject,
+ Insn::InvokeSuperForward { .. } => types::BasicObject,
Insn::InvokeBlock { .. } => types::BasicObject,
Insn::InvokeProc { .. } => types::BasicObject,
Insn::InvokeBuiltin { return_type, .. } => return_type.unwrap_or(types::BasicObject),
@@ -4592,6 +4619,7 @@ impl Function {
| &Insn::SendWithoutBlockDirect { recv, ref args, state, .. }
| &Insn::InvokeBuiltin { recv, ref args, state, .. }
| &Insn::InvokeSuper { recv, ref args, state, .. }
+ | &Insn::InvokeSuperForward { recv, ref args, state, .. }
| &Insn::InvokeProc { recv, ref args, state, .. } => {
worklist.push_back(recv);
worklist.extend(args);
@@ -5252,6 +5280,7 @@ impl Function {
| Insn::Send { recv, ref args, .. }
| Insn::SendForward { recv, ref args, .. }
| Insn::InvokeSuper { recv, ref args, .. }
+ | Insn::InvokeSuperForward { recv, ref args, .. }
| Insn::CCallWithFrame { recv, ref args, .. }
| Insn::CCallVariadic { recv, ref args, .. }
| Insn::InvokeBuiltin { recv, ref args, .. }
@@ -6827,6 +6856,35 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
}
}
}
+ YARVINSN_invokesuperforward => {
+ let cd: *const rb_call_data = get_arg(pc, 0).as_ptr();
+ let blockiseq: IseqPtr = get_arg(pc, 1).as_iseq();
+ let call_info = unsafe { rb_get_call_data_ci(cd) };
+ let flags = unsafe { rb_vm_ci_flag(call_info) };
+ let forwarding = (flags & VM_CALL_FORWARDING) != 0;
+ if let Err(call_type) = unhandled_call_type(flags) {
+ // Can't handle tailcall; side-exit into the interpreter
+ fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledCallType(call_type) });
+ break; // End the block
+ }
+ let argc = unsafe { vm_ci_argc((*cd).ci) };
+ let args = state.stack_pop_n(argc as usize + usize::from(forwarding))?;
+ let recv = state.stack_pop()?;
+ let result = fun.push_insn(block, Insn::InvokeSuperForward { recv, cd, blockiseq, args, state: exit_id, reason: Uncategorized(opcode) });
+ state.stack_push(result);
+
+ if !blockiseq.is_null() {
+ // Reload locals that may have been modified by the blockiseq.
+ // TODO: Avoid reloading locals that are not referenced by the blockiseq
+ // or not used after this. Max thinks we could eventually DCE them.
+ for local_idx in 0..state.locals.len() {
+ let ep_offset = local_idx_to_ep_offset(iseq, local_idx) as u32;
+ // TODO: We could use `use_sp: true` with PatchPoint
+ let val = fun.push_insn(block, Insn::GetLocal { ep_offset, level: 0, use_sp: false, rest_param: false });
+ state.setlocal(ep_offset, val);
+ }
+ }
+ }
YARVINSN_invokeblock => {
let cd: *const rb_call_data = get_arg(pc, 0).as_ptr();
let call_info = unsafe { rb_get_call_data_ci(cd) };
diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs
index 56f1928f1f..c21402449f 100644
--- a/zjit/src/hir/tests.rs
+++ b/zjit/src/hir/tests.rs
@@ -1843,7 +1843,7 @@ pub mod hir_build_tests {
}
#[test]
- fn test_cant_compile_super_forward() {
+ fn test_compile_super_forward() {
eval("
def test(...) = super(...)
");
@@ -1858,7 +1858,79 @@ pub mod hir_build_tests {
EntryPoint JIT(0)
Jump bb2(v5, v6)
bb2(v8:BasicObject, v9:BasicObject):
- SideExit UnhandledYARVInsn(invokesuperforward)
+ v15:BasicObject = InvokeSuperForward v8, 0x1000, v9 # SendFallbackReason: Uncategorized(invokesuperforward)
+ CheckInterrupts
+ Return v15
+ ");
+ }
+
+ #[test]
+ fn test_compile_super_forward_with_block() {
+ eval("
+ def test(...) = super { |x| x }
+ ");
+ assert_snapshot!(hir_string("test"), @r"
+ fn test@<compiled>:2:
+ bb0():
+ EntryPoint interpreter
+ v1:BasicObject = LoadSelf
+ v2:BasicObject = GetLocal :..., l0, SP@4
+ Jump bb2(v1, v2)
+ bb1(v5:BasicObject, v6:BasicObject):
+ EntryPoint JIT(0)
+ Jump bb2(v5, v6)
+ bb2(v8:BasicObject, v9:BasicObject):
+ v15:BasicObject = InvokeSuperForward v8, 0x1000, v9 # SendFallbackReason: Uncategorized(invokesuperforward)
+ v16:BasicObject = GetLocal :..., l0, EP@3
+ CheckInterrupts
+ Return v15
+ ");
+ }
+
+ #[test]
+ fn test_compile_super_forward_with_use() {
+ eval("
+ def test(...) = super(...) + 1
+ ");
+ assert_snapshot!(hir_string("test"), @r"
+ fn test@<compiled>:2:
+ bb0():
+ EntryPoint interpreter
+ v1:BasicObject = LoadSelf
+ v2:BasicObject = GetLocal :..., l0, SP@4
+ Jump bb2(v1, v2)
+ bb1(v5:BasicObject, v6:BasicObject):
+ EntryPoint JIT(0)
+ Jump bb2(v5, v6)
+ bb2(v8:BasicObject, v9:BasicObject):
+ v15:BasicObject = InvokeSuperForward v8, 0x1000, v9 # SendFallbackReason: Uncategorized(invokesuperforward)
+ v17:Fixnum[1] = Const Value(1)
+ v20:BasicObject = SendWithoutBlock v15, :+, v17 # SendFallbackReason: Uncategorized(opt_plus)
+ CheckInterrupts
+ Return v20
+ ");
+ }
+
+ #[test]
+ fn test_compile_super_forward_with_arg() {
+ eval("
+ def test(...) = super(1, ...)
+ ");
+ assert_snapshot!(hir_string("test"), @r"
+ fn test@<compiled>:2:
+ bb0():
+ EntryPoint interpreter
+ v1:BasicObject = LoadSelf
+ v2:BasicObject = GetLocal :..., l0, SP@4
+ Jump bb2(v1, v2)
+ bb1(v5:BasicObject, v6:BasicObject):
+ EntryPoint JIT(0)
+ Jump bb2(v5, v6)
+ bb2(v8:BasicObject, v9:BasicObject):
+ v14:Fixnum[1] = Const Value(1)
+ v17:BasicObject = InvokeSuperForward v8, 0x1000, v14, v9 # SendFallbackReason: Uncategorized(invokesuperforward)
+ CheckInterrupts
+ Return v17
");
}