Skip to content

Commit 2c32bc1

Browse files
committed
ZJIT: Implement include_p for opt_(new|dup)array_send YARV insns
These just call to the C functions that do the optimized test but this avoids the side exit.
1 parent 33f1af6 commit 2c32bc1

3 files changed

Lines changed: 171 additions & 1 deletion

File tree

zjit/src/codegen.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,8 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
444444
Insn::LoadSelf => gen_load_self(),
445445
&Insn::LoadIvarEmbedded { self_val, id, index } => gen_load_ivar_embedded(asm, opnd!(self_val), id, index),
446446
&Insn::LoadIvarExtended { self_val, id, index } => gen_load_ivar_extended(asm, opnd!(self_val), id, index),
447+
Insn::ArrayInclude { elements, target, state } => gen_array_include(jit, asm, opnds!(elements), opnd!(target), &function.frame_state(*state)),
448+
&Insn::DupArrayInclude { ary, target, state } => gen_dup_array_include(jit, asm, ary, opnd!(target), &function.frame_state(state)),
447449
&Insn::ArrayMax { state, .. }
448450
| &Insn::FixnumDiv { state, .. }
449451
| &Insn::FixnumMod { state, .. }
@@ -1263,6 +1265,51 @@ fn gen_array_length(asm: &mut Assembler, array: Opnd) -> lir::Opnd {
12631265
asm_ccall!(asm, rb_jit_array_len, array)
12641266
}
12651267

1268+
fn gen_array_include(
1269+
jit: &JITState,
1270+
asm: &mut Assembler,
1271+
elements: Vec<Opnd>,
1272+
target: Opnd,
1273+
state: &FrameState,
1274+
) -> lir::Opnd {
1275+
gen_prepare_non_leaf_call(jit, asm, state);
1276+
1277+
let num: c_long = elements.len().try_into().expect("Unable to fit length of elements into c_long");
1278+
1279+
// After gen_prepare_non_leaf_call, the elements are spilled to the Ruby stack.
1280+
// The elements are at the bottom of the virtual stack, followed by the target.
1281+
// Get a pointer to the first element on the Ruby stack.
1282+
let stack_bottom = state.stack().len() - elements.len() - 1;
1283+
let elements_ptr = asm.lea(Opnd::mem(64, SP, stack_bottom as i32 * SIZEOF_VALUE_I32));
1284+
1285+
unsafe extern "C" {
1286+
fn rb_vm_opt_newarray_include_p(ec: EcPtr, num: c_long, elts: *const VALUE, target: VALUE) -> VALUE;
1287+
}
1288+
asm.ccall(
1289+
rb_vm_opt_newarray_include_p as *const u8,
1290+
vec![EC, num.into(), elements_ptr, target],
1291+
)
1292+
}
1293+
1294+
fn gen_dup_array_include(
1295+
jit: &JITState,
1296+
asm: &mut Assembler,
1297+
ary: VALUE,
1298+
target: Opnd,
1299+
state: &FrameState,
1300+
) -> lir::Opnd {
1301+
gen_prepare_non_leaf_call(jit, asm, state);
1302+
1303+
unsafe extern "C" {
1304+
fn rb_vm_opt_duparray_include_p(ec: EcPtr, ary: VALUE, target: VALUE) -> VALUE;
1305+
}
1306+
asm.ccall(
1307+
rb_vm_opt_duparray_include_p as *const u8,
1308+
vec![EC, ary.into(), target],
1309+
)
1310+
}
1311+
1312+
>>>>>>> c68bc51ed9 (ZJIT: Implement include_p for opt_(new|dup)array_send YARV insns)
12661313
/// Compile a new hash instruction
12671314
fn gen_new_hash(
12681315
jit: &mut JITState,

zjit/src/hir.rs

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ impl PtrPrintMap {
450450
#[derive(Debug, Clone, Copy)]
451451
pub enum SideExitReason {
452452
UnknownNewarraySend(vm_opt_newarray_send_type),
453+
UnknownDuparraySend(u64),
453454
UnknownSpecialVariable(u64),
454455
UnhandledHIRInsn(InsnId),
455456
UnhandledYARVInsn(u32),
@@ -517,6 +518,7 @@ impl std::fmt::Display for SideExitReason {
517518
SideExitReason::UnknownNewarraySend(VM_OPT_NEWARRAY_SEND_PACK) => write!(f, "UnknownNewarraySend(PACK)"),
518519
SideExitReason::UnknownNewarraySend(VM_OPT_NEWARRAY_SEND_PACK_BUFFER) => write!(f, "UnknownNewarraySend(PACK_BUFFER)"),
519520
SideExitReason::UnknownNewarraySend(VM_OPT_NEWARRAY_SEND_INCLUDE_P) => write!(f, "UnknownNewarraySend(INCLUDE_P)"),
521+
SideExitReason::UnknownDuparraySend(method_id) => write!(f, "UnknownDuparraySend({})", method_id),
520522
SideExitReason::GuardType(guard_type) => write!(f, "GuardType({guard_type})"),
521523
SideExitReason::GuardTypeNot(guard_type) => write!(f, "GuardTypeNot({guard_type})"),
522524
SideExitReason::GuardBitEquals(value) => write!(f, "GuardBitEquals({})", value.print(&PtrPrintMap::identity())),
@@ -578,6 +580,8 @@ pub enum Insn {
578580
NewRangeFixnum { low: InsnId, high: InsnId, flag: RangeType, state: InsnId },
579581
ArrayDup { val: InsnId, state: InsnId },
580582
ArrayMax { elements: Vec<InsnId>, state: InsnId },
583+
ArrayInclude { elements: Vec<InsnId>, target: InsnId, state: InsnId },
584+
DupArrayInclude { ary: VALUE, target: InsnId, state: InsnId },
581585
/// Extend `left` with the elements from `right`. `left` and `right` must both be `Array`.
582586
ArrayExtend { left: InsnId, right: InsnId, state: InsnId },
583587
/// Push `val` onto `array`, where `array` is already `Array`.
@@ -930,6 +934,18 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
930934
}
931935
Ok(())
932936
}
937+
Insn::ArrayInclude { elements, target, .. } => {
938+
write!(f, "ArrayInclude")?;
939+
let mut prefix = " ";
940+
for element in elements {
941+
write!(f, "{prefix}{element}")?;
942+
prefix = ", ";
943+
}
944+
write!(f, " | {target}")
945+
}
946+
Insn::DupArrayInclude { ary, target, .. } => {
947+
write!(f, "DupArrayInclude {} | {}", ary.print(self.ptr_map), target)
948+
}
933949
Insn::ArrayDup { val, .. } => { write!(f, "ArrayDup {val}") }
934950
Insn::HashDup { val, .. } => { write!(f, "HashDup {val}") }
935951
Insn::HashAref { hash, key, .. } => { write!(f, "HashAref {hash}, {key}")}
@@ -1611,6 +1627,8 @@ impl Function {
16111627
&ArrayArefFixnum { array, index } => ArrayArefFixnum { array: find!(array), index: find!(index) },
16121628
&ArrayLength { array } => ArrayLength { array: find!(array) },
16131629
&ArrayMax { ref elements, state } => ArrayMax { elements: find_vec!(elements), state: find!(state) },
1630+
&ArrayInclude { ref elements, target, state } => ArrayInclude { elements: find_vec!(elements), target: find!(target), state: find!(state) },
1631+
&DupArrayInclude { ary, target, state } => DupArrayInclude { ary, target: find!(target), state: find!(state) },
16141632
&SetGlobal { id, val, state } => SetGlobal { id, val: find!(val), state },
16151633
&GetIvar { self_val, id, state } => GetIvar { self_val: find!(self_val), id, state },
16161634
&LoadIvarEmbedded { self_val, id, index } => LoadIvarEmbedded { self_val: find!(self_val), id, index },
@@ -1737,6 +1755,8 @@ impl Function {
17371755
Insn::DefinedIvar { pushval, .. } => Type::from_value(*pushval).union(types::NilClass),
17381756
Insn::GetConstantPath { .. } => types::BasicObject,
17391757
Insn::ArrayMax { .. } => types::BasicObject,
1758+
Insn::ArrayInclude { .. } => types::BoolExact,
1759+
Insn::DupArrayInclude { .. } => types::BoolExact,
17401760
Insn::GetGlobal { .. } => types::BasicObject,
17411761
Insn::GetIvar { .. } => types::BasicObject,
17421762
Insn::LoadPC => types::CPtr,
@@ -2729,6 +2749,15 @@ impl Function {
27292749
worklist.extend(elements);
27302750
worklist.push_back(state);
27312751
}
2752+
&Insn::ArrayInclude { ref elements, target, state } => {
2753+
worklist.extend(elements);
2754+
worklist.push_back(target);
2755+
worklist.push_back(state);
2756+
}
2757+
&Insn::DupArrayInclude { target, state, .. } => {
2758+
worklist.push_back(target);
2759+
worklist.push_back(state);
2760+
}
27322761
&Insn::NewRange { low, high, state, .. }
27332762
| &Insn::NewRangeFixnum { low, high, state, .. } => {
27342763
worklist.push_back(low);
@@ -3779,6 +3808,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
37793808
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
37803809
let (bop, insn) = match method {
37813810
VM_OPT_NEWARRAY_SEND_MAX => (BOP_MAX, Insn::ArrayMax { elements, state: exit_id }),
3811+
VM_OPT_NEWARRAY_SEND_INCLUDE_P => {
3812+
let target = elements[elements.len() - 1];
3813+
let array_elements = elements[..elements.len() - 1].to_vec();
3814+
(BOP_INCLUDE_P, Insn::ArrayInclude { elements: array_elements, target, state: exit_id })
3815+
},
37823816
_ => {
37833817
// Unknown opcode; side-exit into the interpreter
37843818
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnknownNewarraySend(method) });
@@ -3799,6 +3833,30 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
37993833
let insn_id = fun.push_insn(block, Insn::ArrayDup { val, state: exit_id });
38003834
state.stack_push(insn_id);
38013835
}
3836+
YARVINSN_opt_duparray_send => {
3837+
let ary = get_arg(pc, 0);
3838+
let method_id = get_arg(pc, 1).as_u64();
3839+
let argc = get_arg(pc, 2).as_usize();
3840+
if argc != 1 {
3841+
break;
3842+
}
3843+
let target = state.stack_pop()?;
3844+
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
3845+
let bop = match method_id {
3846+
x if x == ID!(include_p).0 => BOP_INCLUDE_P,
3847+
_ => {
3848+
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnknownDuparraySend(method_id) });
3849+
break;
3850+
},
3851+
};
3852+
if !unsafe { rb_BASIC_OP_UNREDEFINED_P(bop, ARRAY_REDEFINED_OP_FLAG) } {
3853+
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::PatchPoint(Invariant::BOPRedefined { klass: ARRAY_REDEFINED_OP_FLAG, bop }) });
3854+
break;
3855+
}
3856+
fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::BOPRedefined { klass: ARRAY_REDEFINED_OP_FLAG, bop }, state: exit_id });
3857+
let insn_id = fun.push_insn(block, Insn::DupArrayInclude { ary, target, state: exit_id });
3858+
state.stack_push(insn_id);
3859+
}
38023860
YARVINSN_newhash => {
38033861
let count = get_arg(pc, 0).as_usize();
38043862
assert!(count % 2 == 0, "newhash count should be even");
@@ -6977,7 +7035,70 @@ mod tests {
69777035
Jump bb2(v8, v9, v10, v11, v12)
69787036
bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass):
69797037
v25:BasicObject = SendWithoutBlock v15, :+, v16
6980-
SideExit UnknownNewarraySend(INCLUDE_P)
7038+
PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, 33)
7039+
v30:BoolExact = ArrayInclude v15, v16 | v16
7040+
PatchPoint NoEPEscape(test)
7041+
v35:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
7042+
v37:ArrayExact = ArrayDup v35
7043+
v39:BasicObject = SendWithoutBlock v14, :puts, v37
7044+
PatchPoint NoEPEscape(test)
7045+
CheckInterrupts
7046+
Return v30
7047+
");
7048+
}
7049+
7050+
#[test]
7051+
fn test_opt_duparray_send_include_p() {
7052+
eval("
7053+
def test(x)
7054+
[:a, :b].include?(x)
7055+
end
7056+
");
7057+
assert_contains_opcode("test", YARVINSN_opt_duparray_send);
7058+
assert_snapshot!(hir_string("test"), @r"
7059+
fn test@<compiled>:3:
7060+
bb0():
7061+
EntryPoint interpreter
7062+
v1:BasicObject = LoadSelf
7063+
v2:BasicObject = GetLocal l0, SP@4
7064+
Jump bb2(v1, v2)
7065+
bb1(v5:BasicObject, v6:BasicObject):
7066+
EntryPoint JIT(0)
7067+
Jump bb2(v5, v6)
7068+
bb2(v8:BasicObject, v9:BasicObject):
7069+
PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, 33)
7070+
v15:BoolExact = DupArrayInclude VALUE(0x1000) | v9
7071+
CheckInterrupts
7072+
Return v15
7073+
");
7074+
}
7075+
7076+
#[test]
7077+
fn test_opt_duparray_send_include_p_redefined() {
7078+
eval("
7079+
class Array
7080+
alias_method :old_include?, :include?
7081+
def include?(x)
7082+
old_include?(x)
7083+
end
7084+
end
7085+
def test(x)
7086+
[:a, :b].include?(x)
7087+
end
7088+
");
7089+
assert_contains_opcode("test", YARVINSN_opt_duparray_send);
7090+
assert_snapshot!(hir_string("test"), @r"
7091+
fn test@<compiled>:9:
7092+
bb0():
7093+
EntryPoint interpreter
7094+
v1:BasicObject = LoadSelf
7095+
v2:BasicObject = GetLocal l0, SP@4
7096+
Jump bb2(v1, v2)
7097+
bb1(v5:BasicObject, v6:BasicObject):
7098+
EntryPoint JIT(0)
7099+
Jump bb2(v5, v6)
7100+
bb2(v8:BasicObject, v9:BasicObject):
7101+
SideExit PatchPoint(BOPRedefined(ARRAY_REDEFINED_OP_FLAG, 33))
69817102
");
69827103
}
69837104

zjit/src/stats.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ make_counters! {
128128
// exit_: Side exits reasons
129129
exit_compile_error,
130130
exit_unknown_newarray_send,
131+
exit_unknown_duparray_send,
131132
exit_unhandled_tailcall,
132133
exit_unhandled_splat,
133134
exit_unhandled_kwarg,
@@ -323,6 +324,7 @@ pub fn side_exit_counter(reason: crate::hir::SideExitReason) -> Counter {
323324
use crate::stats::Counter::*;
324325
match reason {
325326
UnknownNewarraySend(_) => exit_unknown_newarray_send,
327+
UnknownDuparraySend(_) => exit_unknown_duparray_send,
326328
UnhandledCallType(Tailcall) => exit_unhandled_tailcall,
327329
UnhandledCallType(Splat) => exit_unhandled_splat,
328330
UnhandledCallType(Kwarg) => exit_unhandled_kwarg,

0 commit comments

Comments
 (0)