Skip to content

Commit 78f11bd

Browse files
authored
ZJIT: Support VM_OPT_NEWARRAY_SEND_PACK (ruby#16596)
1 parent 3a0b003 commit 78f11bd

4 files changed

Lines changed: 111 additions & 12 deletions

File tree

zjit/src/codegen.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -747,7 +747,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
747747
&Insn::WriteBarrier { recv, val } => no_output!(gen_write_barrier(jit, asm, opnd!(recv), opnd!(val), function.type_of(val))),
748748
&Insn::IsBlockGiven { lep } => gen_is_block_given(asm, opnd!(lep)),
749749
Insn::ArrayInclude { elements, target, state } => gen_array_include(jit, asm, opnds!(elements), opnd!(target), &function.frame_state(*state)),
750-
Insn::ArrayPackBuffer { elements, fmt, buffer, state } => gen_array_pack_buffer(jit, asm, opnds!(elements), opnd!(fmt), opnd!(buffer), &function.frame_state(*state)),
750+
Insn::ArrayPackBuffer { elements, fmt, buffer, state } => gen_array_pack_buffer(jit, asm, opnds!(elements), opnd!(fmt), (*buffer).map(|buffer| opnd!(buffer)), &function.frame_state(*state)),
751751
&Insn::DupArrayInclude { ary, target, state } => gen_dup_array_include(jit, asm, ary, opnd!(target), &function.frame_state(state)),
752752
Insn::ArrayHash { elements, state } => gen_opt_newarray_hash(jit, asm, opnds!(elements), &function.frame_state(*state)),
753753
&Insn::IsA { val, class } => gen_is_a(jit, asm, opnd!(val), opnd!(class)),
@@ -2047,17 +2047,21 @@ fn gen_array_pack_buffer(
20472047
asm: &mut Assembler,
20482048
elements: Vec<Opnd>,
20492049
fmt: Opnd,
2050-
buffer: Opnd,
2050+
buffer: Option<Opnd>,
20512051
state: &FrameState,
20522052
) -> lir::Opnd {
20532053
gen_prepare_non_leaf_call(jit, asm, state);
20542054

20552055
let array_len: c_long = elements.len().try_into().expect("Unable to fit length of elements into c_long");
20562056

20572057
// After gen_prepare_non_leaf_call, the elements are spilled to the Ruby stack.
2058-
// The elements are at the bottom of the virtual stack, followed by the fmt, followed by the buffer.
2058+
// The elements are at the bottom of the virtual stack, followed by the fmt, and optionally the buffer.
20592059
// Get a pointer to the first element on the Ruby stack.
2060-
let stack_bottom = state.stack().len() - elements.len() - 2;
2060+
let stack_bottom = if buffer.is_some() {
2061+
state.stack().len() - elements.len() - 2
2062+
} else {
2063+
state.stack().len() - elements.len() - 1
2064+
};
20612065
let elements_ptr = asm.lea(Opnd::mem(64, SP, stack_bottom as i32 * SIZEOF_VALUE_I32));
20622066

20632067
unsafe extern "C" {
@@ -2066,7 +2070,7 @@ fn gen_array_pack_buffer(
20662070
asm_ccall!(
20672071
asm,
20682072
rb_vm_opt_newarray_pack_buffer,
2069-
EC, array_len.into(), elements_ptr, fmt, buffer
2073+
EC, array_len.into(), elements_ptr, fmt, buffer.unwrap_or_else(|| Qundef.into())
20702074
)
20712075
}
20722076

zjit/src/codegen_tests.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2399,6 +2399,39 @@ fn test_opt_duparray_send_include_p_redefined() {
23992399
"), @"[:true, :false]");
24002400
}
24012401

2402+
#[test]
2403+
fn test_opt_newarray_send_pack() {
2404+
eval(r#"
2405+
def test(num)
2406+
[num].pack('C')
2407+
end
2408+
test(65)
2409+
"#);
2410+
assert_contains_opcode("test", YARVINSN_opt_newarray_send);
2411+
assert_snapshot!(assert_compiles(r#"
2412+
[test(65), test(66), test(67)]
2413+
"#), @r#"["A", "B", "C"]"#);
2414+
}
2415+
2416+
#[test]
2417+
fn test_opt_newarray_send_pack_redefined() {
2418+
eval(r#"
2419+
class Array
2420+
alias_method :old_pack, :pack
2421+
def pack(fmt, buffer: nil)
2422+
"override:#{old_pack(fmt, buffer: buffer)}"
2423+
end
2424+
end
2425+
def test(num)
2426+
[num].pack('C')
2427+
end
2428+
"#);
2429+
assert_contains_opcode("test", YARVINSN_opt_newarray_send);
2430+
assert_snapshot!(assert_compiles(r#"
2431+
[test(65), test(66), test(67)]
2432+
"#), @r#"["override:A", "override:B", "override:C"]"#);
2433+
}
2434+
24022435
#[test]
24032436
fn test_opt_newarray_send_pack_buffer() {
24042437
eval(r#"

zjit/src/hir.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,7 @@ pub enum Insn {
820820
ArrayMax { elements: Vec<InsnId>, state: InsnId },
821821
ArrayMin { elements: Vec<InsnId>, state: InsnId },
822822
ArrayInclude { elements: Vec<InsnId>, target: InsnId, state: InsnId },
823-
ArrayPackBuffer { elements: Vec<InsnId>, fmt: InsnId, buffer: InsnId, state: InsnId },
823+
ArrayPackBuffer { elements: Vec<InsnId>, fmt: InsnId, buffer: Option<InsnId>, state: InsnId },
824824
DupArrayInclude { ary: VALUE, target: InsnId, state: InsnId },
825825
/// Extend `left` with the elements from `right`. `left` and `right` must both be `Array`.
826826
ArrayExtend { left: InsnId, right: InsnId, state: InsnId },
@@ -1179,7 +1179,9 @@ macro_rules! for_each_operand_impl {
11791179
Insn::ArrayPackBuffer { elements, fmt, buffer, state, .. } => {
11801180
$visit_many!(elements);
11811181
$visit_one!(fmt);
1182-
$visit_one!(buffer);
1182+
if let Some(buffer) = buffer {
1183+
$visit_one!(buffer);
1184+
}
11831185
$visit_one!(state);
11841186
}
11851187
Insn::DupArrayInclude { target, state, .. } => {
@@ -1831,7 +1833,11 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
18311833
for element in elements {
18321834
write!(f, "{element}, ")?;
18331835
}
1834-
write!(f, "fmt: {fmt}, buf: {buffer}")
1836+
write!(f, "fmt: {fmt}")?;
1837+
if let Some(buffer) = buffer {
1838+
write!(f, ", buf: {buffer}")?;
1839+
}
1840+
Ok(())
18351841
}
18361842
Insn::DupArrayInclude { ary, target, .. } => {
18371843
write!(f, "DupArrayInclude {} | {}", ary.print(self.ptr_map), target)
@@ -2917,7 +2923,7 @@ impl Function {
29172923
&ArrayMax { ref elements, state } => ArrayMax { elements: find_vec!(elements), state: find!(state) },
29182924
&ArrayMin { ref elements, state } => ArrayMin { elements: find_vec!(elements), state: find!(state) },
29192925
&ArrayInclude { ref elements, target, state } => ArrayInclude { elements: find_vec!(elements), target: find!(target), state: find!(state) },
2920-
&ArrayPackBuffer { ref elements, fmt, buffer, state } => ArrayPackBuffer { elements: find_vec!(elements), fmt: find!(fmt), buffer: find!(buffer), state: find!(state) },
2926+
&ArrayPackBuffer { ref elements, fmt, ref buffer, state } => ArrayPackBuffer { elements: find_vec!(elements), fmt: find!(fmt), buffer: (*buffer).map(|buffer| find!(buffer)), state: find!(state) },
29212927
&DupArrayInclude { ary, target, state } => DupArrayInclude { ary, target: find!(target), state: find!(state) },
29222928
&ArrayHash { ref elements, state } => ArrayHash { elements: find_vec!(elements), state },
29232929
&SetGlobal { id, val, state } => SetGlobal { id, val: find!(val), state },
@@ -6161,7 +6167,9 @@ impl Function {
61616167
}
61626168
Insn::ArrayPackBuffer { ref elements, fmt, buffer, .. } => {
61636169
self.assert_subtype(insn_id, fmt, types::BasicObject)?;
6164-
self.assert_subtype(insn_id, buffer, types::BasicObject)?;
6170+
if let Some(buffer) = buffer {
6171+
self.assert_subtype(insn_id, buffer, types::BasicObject)?;
6172+
}
61656173
for &element in elements {
61666174
self.assert_subtype(insn_id, element, types::BasicObject)?;
61676175
}
@@ -7141,11 +7149,16 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
71417149
let elements = state.stack_pop_n(count - 1)?;
71427150
(BOP_INCLUDE_P, Insn::ArrayInclude { elements, target, state: exit_id })
71437151
}
7152+
VM_OPT_NEWARRAY_SEND_PACK => {
7153+
let fmt = state.stack_pop()?;
7154+
let elements = state.stack_pop_n(count - 1)?;
7155+
(BOP_PACK, Insn::ArrayPackBuffer { elements, fmt, buffer: None, state: exit_id })
7156+
}
71447157
VM_OPT_NEWARRAY_SEND_PACK_BUFFER => {
71457158
let buffer = state.stack_pop()?;
71467159
let fmt = state.stack_pop()?;
71477160
let elements = state.stack_pop_n(count - 2)?;
7148-
(BOP_PACK, Insn::ArrayPackBuffer { elements, fmt, buffer, state: exit_id })
7161+
(BOP_PACK, Insn::ArrayPackBuffer { elements, fmt, buffer: Some(buffer), state: exit_id })
71497162
}
71507163
_ => {
71517164
// Unknown opcode; side-exit into the interpreter

zjit/src/hir/tests.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2808,7 +2808,56 @@ pub(crate) mod hir_build_tests {
28082808
v26:BasicObject = Send v16, :+, v17 # SendFallbackReason: Uncategorized(opt_plus)
28092809
v32:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008))
28102810
v33:StringExact = StringCopy v32
2811-
SideExit UnhandledNewarraySend(PACK)
2811+
PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_PACK)
2812+
v36:String = ArrayPackBuffer v16, v17, fmt: v33
2813+
PatchPoint NoEPEscape(test)
2814+
v43:ArrayExact[VALUE(0x1010)] = Const Value(VALUE(0x1010))
2815+
v44:ArrayExact = ArrayDup v43
2816+
v46:BasicObject = Send v15, :puts, v44 # SendFallbackReason: Uncategorized(opt_send_without_block)
2817+
PatchPoint NoEPEscape(test)
2818+
CheckInterrupts
2819+
Return v36
2820+
");
2821+
}
2822+
2823+
#[test]
2824+
fn test_opt_newarray_send_pack_redefined() {
2825+
eval(r#"
2826+
class Array
2827+
def pack(fmt, buffer: nil) = 5
2828+
end
2829+
def test(a,b)
2830+
sum = a+b
2831+
result = [a,b].pack 'C'
2832+
puts [1,2,3]
2833+
result
2834+
end
2835+
"#);
2836+
assert_contains_opcode("test", YARVINSN_opt_newarray_send);
2837+
assert_snapshot!(hir_string("test"), @"
2838+
fn test@<compiled>:6:
2839+
bb1():
2840+
EntryPoint interpreter
2841+
v1:BasicObject = LoadSelf
2842+
v2:CPtr = LoadSP
2843+
v3:BasicObject = LoadField v2, :a@0x1000
2844+
v4:BasicObject = LoadField v2, :b@0x1001
2845+
v5:NilClass = Const Value(nil)
2846+
v6:NilClass = Const Value(nil)
2847+
Jump bb3(v1, v3, v4, v5, v6)
2848+
bb2():
2849+
EntryPoint JIT(0)
2850+
v9:BasicObject = LoadArg :self@0
2851+
v10:BasicObject = LoadArg :a@1
2852+
v11:BasicObject = LoadArg :b@2
2853+
v12:NilClass = Const Value(nil)
2854+
v13:NilClass = Const Value(nil)
2855+
Jump bb3(v9, v10, v11, v12, v13)
2856+
bb3(v15:BasicObject, v16:BasicObject, v17:BasicObject, v18:NilClass, v19:NilClass):
2857+
v26:BasicObject = Send v16, :+, v17 # SendFallbackReason: Uncategorized(opt_plus)
2858+
v32:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008))
2859+
v33:StringExact = StringCopy v32
2860+
SideExit PatchPoint(BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_PACK))
28122861
");
28132862
}
28142863

0 commit comments

Comments
 (0)