@@ -450,6 +450,7 @@ impl PtrPrintMap {
450450#[derive(Debug, Clone, Copy)]
451451pub 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
0 commit comments