Skip to content

Commit 17fc0b0

Browse files
committed
Store CME on SendWithoutBlockDirect
This allows specializing calls to more than just ISEQs.
1 parent bcd87a5 commit 17fc0b0

3 files changed

Lines changed: 25 additions & 16 deletions

File tree

zjit/src/codegen.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,11 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
255255
Insn::IfTrue { val, target } => return gen_if_true(jit, asm, opnd!(val), target),
256256
Insn::IfFalse { val, target } => return gen_if_false(jit, asm, opnd!(val), target),
257257
Insn::SendWithoutBlock { call_info, cd, state, .. } => gen_send_without_block(jit, asm, call_info, *cd, &function.frame_state(*state))?,
258-
Insn::SendWithoutBlockDirect { iseq, self_val, args, .. } => gen_send_without_block_direct(cb, jit, asm, *iseq, opnd!(self_val), args)?,
258+
// Fall back to a slower send (for now) if the target is not an ISEQ.
259+
// TODO(max): Emit faster send code for calls to cfuncs
260+
Insn::SendWithoutBlockDirect { call_info, cd, state, cme, .. } if unsafe { get_cme_def_type(*cme) } != VM_METHOD_TYPE_ISEQ =>
261+
gen_send_without_block(jit, asm, call_info, *cd, &function.frame_state(*state))?,
262+
Insn::SendWithoutBlockDirect { cme, self_val, args, .. } => gen_send_without_block_direct(cb, jit, asm, *cme, opnd!(self_val), args)?,
259263
Insn::Return { val } => return Some(gen_return(asm, opnd!(val))?),
260264
Insn::FixnumAdd { left, right, state } => gen_fixnum_add(asm, opnd!(left), opnd!(right), &function.frame_state(*state))?,
261265
Insn::FixnumSub { left, right, state } => gen_fixnum_sub(asm, opnd!(left), opnd!(right), &function.frame_state(*state))?,
@@ -472,7 +476,7 @@ fn gen_send_without_block_direct(
472476
cb: &mut CodeBlock,
473477
jit: &mut JITState,
474478
asm: &mut Assembler,
475-
iseq: IseqPtr,
479+
cme: CmePtr,
476480
recv: Opnd,
477481
args: &Vec<InsnId>,
478482
) -> Option<lir::Opnd> {
@@ -493,6 +497,9 @@ fn gen_send_without_block_direct(
493497
// Make a method call. The target address will be rewritten once compiled.
494498
let branch = Branch::new();
495499
let dummy_ptr = cb.get_write_ptr().raw_ptr(cb);
500+
let def_type = unsafe { get_cme_def_type(cme) };
501+
assert_eq!(def_type, VM_METHOD_TYPE_ISEQ, "Specialized direct send support currently only available for ISEQ");
502+
let iseq = unsafe { get_def_iseq_ptr((*cme).def) };
496503
jit.branch_iseqs.push((branch.clone(), iseq));
497504
// TODO(max): Add a PatchPoint here that can side-exit the function if the callee messed with
498505
// the frame's locals

zjit/src/cruby.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,9 @@ pub struct ID(pub ::std::os::raw::c_ulong);
259259
/// Pointer to an ISEQ
260260
pub type IseqPtr = *const rb_iseq_t;
261261

262+
/// Pointer to a callable method entry (CME)
263+
pub type CmePtr = *const rb_callable_method_entry_t;
264+
262265
// Given an ISEQ pointer, convert PC to insn_idx
263266
pub fn iseq_pc_to_insn_idx(iseq: IseqPtr, pc: *mut VALUE) -> Option<u16> {
264267
let pc_zero = unsafe { rb_iseq_pc_at_idx(iseq, 0) };

zjit/src/hir.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ pub enum Insn {
341341
// Ignoring keyword arguments etc for now
342342
SendWithoutBlock { self_val: InsnId, call_info: CallInfo, cd: *const rb_call_data, args: Vec<InsnId>, state: InsnId },
343343
Send { self_val: InsnId, call_info: CallInfo, cd: *const rb_call_data, blockiseq: IseqPtr, args: Vec<InsnId>, state: InsnId },
344-
SendWithoutBlockDirect { self_val: InsnId, call_info: CallInfo, cd: *const rb_call_data, iseq: IseqPtr, args: Vec<InsnId>, state: InsnId },
344+
SendWithoutBlockDirect { self_val: InsnId, call_info: CallInfo, cd: *const rb_call_data, cme: CmePtr, args: Vec<InsnId>, state: InsnId },
345345

346346
// Control flow instructions
347347
Return { val: InsnId },
@@ -455,8 +455,8 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
455455
}
456456
Ok(())
457457
}
458-
Insn::SendWithoutBlockDirect { self_val, call_info, iseq, args, .. } => {
459-
write!(f, "SendWithoutBlockDirect {self_val}, :{} ({:?})", call_info.method_name, self.ptr_map.map_ptr(iseq))?;
458+
Insn::SendWithoutBlockDirect { self_val, call_info, cme, args, .. } => {
459+
write!(f, "SendWithoutBlockDirect {self_val}, :{} ({:?})", call_info.method_name, self.ptr_map.map_ptr(cme))?;
460460
for arg in args {
461461
write!(f, ", {arg}")?;
462462
}
@@ -796,11 +796,11 @@ impl Function {
796796
args: args.iter().map(|arg| find!(*arg)).collect(),
797797
state: *state,
798798
},
799-
SendWithoutBlockDirect { self_val, call_info, cd, iseq, args, state } => SendWithoutBlockDirect {
799+
SendWithoutBlockDirect { self_val, call_info, cd, cme, args, state } => SendWithoutBlockDirect {
800800
self_val: find!(*self_val),
801801
call_info: call_info.clone(),
802802
cd: *cd,
803-
iseq: *iseq,
803+
cme: *cme,
804804
args: args.iter().map(|arg| find!(*arg)).collect(),
805805
state: *state,
806806
},
@@ -986,7 +986,7 @@ impl Function {
986986
}
987987

988988
/// Rewrite SendWithoutBlock opcodes into SendWithoutBlockDirect opcodes if we know the target
989-
/// ISEQ statically. This removes run-time method lookups and opens the door for inlining.
989+
/// CME statically. This removes run-time method lookups and opens the door for inlining.
990990
fn optimize_direct_sends(&mut self) {
991991
let payload = get_or_create_iseq_payload(self.iseq);
992992
for block in self.rpo() {
@@ -1039,16 +1039,14 @@ impl Function {
10391039
// It allows you to use a faster ISEQ if possible.
10401040
cme = unsafe { rb_check_overloaded_cme(cme, ci) };
10411041
let def_type = unsafe { get_cme_def_type(cme) };
1042-
if def_type != VM_METHOD_TYPE_ISEQ {
1043-
// TODO(max): Allow non-iseq; cache cme
1042+
if !(def_type == VM_METHOD_TYPE_ISEQ || def_type == VM_METHOD_TYPE_CFUNC) {
10441043
self.push_insn_id(block, insn_id); continue;
10451044
}
10461045
self.push_insn(block, Insn::PatchPoint(Invariant::MethodRedefined { klass, method: mid }));
1047-
let iseq = unsafe { get_def_iseq_ptr((*cme).def) };
10481046
if let Some(expected) = guard_equal_to {
10491047
self_val = self.push_insn(block, Insn::GuardBitEquals { val: self_val, expected, state });
10501048
}
1051-
let send_direct = self.push_insn(block, Insn::SendWithoutBlockDirect { self_val, call_info, cd, iseq, args, state });
1049+
let send_direct = self.push_insn(block, Insn::SendWithoutBlockDirect { self_val, call_info, cd, cme, args, state });
10521050
self.make_equal_to(insn_id, send_direct);
10531051
}
10541052
Insn::GetConstantPath { ic } => {
@@ -3582,7 +3580,7 @@ mod opt_tests {
35823580
bb0():
35833581
v2:ArrayExact = NewArray
35843582
PatchPoint MethodRedefined(Array@0x1000, itself@0x1008)
3585-
v7:BasicObject = CCall itself@0x1010, v2
3583+
v7:BasicObject = SendWithoutBlockDirect v2, :itself (0x1010)
35863584
Return v7
35873585
"#]]);
35883586
}
@@ -3600,8 +3598,9 @@ mod opt_tests {
36003598
bb0():
36013599
v1:Fixnum[1] = Const Value(1)
36023600
v2:Fixnum[0] = Const Value(0)
3603-
v4:BasicObject = SendWithoutBlock v1, :itself, v2
3604-
Return v4
3601+
PatchPoint MethodRedefined(Integer@0x1000, itself@0x1008)
3602+
v7:BasicObject = SendWithoutBlockDirect v1, :itself (0x1010), v2
3603+
Return v7
36053604
"#]]);
36063605
}
36073606

@@ -3653,7 +3652,7 @@ mod opt_tests {
36533652
v1:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
36543653
v2:StringExact = StringCopy v1
36553654
PatchPoint MethodRedefined(String@0x1008, bytesize@0x1010)
3656-
v7:Fixnum = CCall bytesize@0x1018, v2
3655+
v7:BasicObject = SendWithoutBlockDirect v2, :bytesize (0x1018)
36573656
Return v7
36583657
"#]]);
36593658
}

0 commit comments

Comments
 (0)