Skip to content

Commit 796d59c

Browse files
committed
ZJIT: Compile the VM_OPT_NEWARRAY_SEND_HASH variant of opt_newarray_send
1 parent 0e30d1e commit 796d59c

4 files changed

Lines changed: 73 additions & 1 deletion

File tree

test/ruby/test_zjit.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,26 @@ def test(x)
10491049
}, insns: [:opt_duparray_send], call_threshold: 1
10501050
end
10511051

1052+
def test_opt_newarray_send_hash
1053+
assert_compiles 'Integer', %q{
1054+
def test(x)
1055+
[1, 2, x].hash
1056+
end
1057+
test(20).class
1058+
}, insns: [:opt_newarray_send], call_threshold: 1
1059+
end
1060+
1061+
def test_opt_newarray_send_hash_redefinition
1062+
assert_compiles 'Integer', %q{
1063+
Array.class_eval { def hash = 42 }
1064+
1065+
def test(x)
1066+
[1, 2, x].hash
1067+
end
1068+
test(20).class
1069+
}, insns: [:opt_newarray_send], call_threshold: 1
1070+
end
1071+
10521072
def test_new_hash_empty
10531073
assert_compiles '{}', %q{
10541074
def test = {}

zjit/src/codegen.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
465465
&Insn::IsBlockGiven => gen_is_block_given(jit, asm),
466466
Insn::ArrayInclude { elements, target, state } => gen_array_include(jit, asm, opnds!(elements), opnd!(target), &function.frame_state(*state)),
467467
&Insn::DupArrayInclude { ary, target, state } => gen_dup_array_include(jit, asm, ary, opnd!(target), &function.frame_state(state)),
468+
Insn::ArrayHash { elements, state } => gen_opt_newarray_hash(jit, asm, opnds!(elements), &function.frame_state(*state)),
468469
&Insn::ArrayMax { state, .. }
469470
| &Insn::FixnumDiv { state, .. }
470471
| &Insn::Throw { state, .. }
@@ -1427,6 +1428,33 @@ fn gen_array_length(asm: &mut Assembler, array: Opnd) -> lir::Opnd {
14271428
asm_ccall!(asm, rb_jit_array_len, array)
14281429
}
14291430

1431+
/// Compile opt_newarray_hash - create a hash from array elements
1432+
fn gen_opt_newarray_hash(
1433+
jit: &JITState,
1434+
asm: &mut Assembler,
1435+
elements: Vec<Opnd>,
1436+
state: &FrameState,
1437+
) -> lir::Opnd {
1438+
// `Array#hash` will hash the elements of the array.
1439+
gen_prepare_non_leaf_call(jit, asm, state);
1440+
1441+
let array_len: c_long = elements.len().try_into().expect("Unable to fit length of elements into c_long");
1442+
1443+
// After gen_prepare_non_leaf_call, the elements are spilled to the Ruby stack.
1444+
// Get a pointer to the first element on the Ruby stack.
1445+
let stack_bottom = state.stack().len() - elements.len();
1446+
let elements_ptr = asm.lea(Opnd::mem(64, SP, stack_bottom as i32 * SIZEOF_VALUE_I32));
1447+
1448+
unsafe extern "C" {
1449+
fn rb_vm_opt_newarray_hash(ec: EcPtr, array_len: u32, elts: *const VALUE) -> VALUE;
1450+
}
1451+
1452+
asm.ccall(
1453+
rb_vm_opt_newarray_hash as *const u8,
1454+
vec![EC, (array_len as u32).into(), elements_ptr],
1455+
)
1456+
}
1457+
14301458
fn gen_array_include(
14311459
jit: &JITState,
14321460
asm: &mut Assembler,

zjit/src/hir.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ impl<'a> std::fmt::Display for InvariantPrinter<'a> {
231231
BOP_FREEZE => write!(f, "BOP_FREEZE")?,
232232
BOP_UMINUS => write!(f, "BOP_UMINUS")?,
233233
BOP_MAX => write!(f, "BOP_MAX")?,
234+
BOP_HASH => write!(f, "BOP_HASH")?,
234235
BOP_AREF => write!(f, "BOP_AREF")?,
235236
_ => write!(f, "{bop}")?,
236237
}
@@ -650,6 +651,7 @@ pub enum Insn {
650651
NewRange { low: InsnId, high: InsnId, flag: RangeType, state: InsnId },
651652
NewRangeFixnum { low: InsnId, high: InsnId, flag: RangeType, state: InsnId },
652653
ArrayDup { val: InsnId, state: InsnId },
654+
ArrayHash { elements: Vec<InsnId>, state: InsnId },
653655
ArrayMax { elements: Vec<InsnId>, state: InsnId },
654656
ArrayInclude { elements: Vec<InsnId>, target: InsnId, state: InsnId },
655657
DupArrayInclude { ary: VALUE, target: InsnId, state: InsnId },
@@ -1042,6 +1044,15 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
10421044
}
10431045
Ok(())
10441046
}
1047+
Insn::ArrayHash { elements, .. } => {
1048+
write!(f, "ArrayHash")?;
1049+
let mut prefix = " ";
1050+
for element in elements {
1051+
write!(f, "{prefix}{element}")?;
1052+
prefix = ", ";
1053+
}
1054+
Ok(())
1055+
}
10451056
Insn::ArrayInclude { elements, target, .. } => {
10461057
write!(f, "ArrayInclude")?;
10471058
let mut prefix = " ";
@@ -1890,6 +1901,7 @@ impl Function {
18901901
&ArrayMax { ref elements, state } => ArrayMax { elements: find_vec!(elements), state: find!(state) },
18911902
&ArrayInclude { ref elements, target, state } => ArrayInclude { elements: find_vec!(elements), target: find!(target), state: find!(state) },
18921903
&DupArrayInclude { ary, target, state } => DupArrayInclude { ary, target: find!(target), state: find!(state) },
1904+
&ArrayHash { ref elements, state } => ArrayHash { elements: find_vec!(elements), state },
18931905
&SetGlobal { id, val, state } => SetGlobal { id, val: find!(val), state },
18941906
&GetIvar { self_val, id, state } => GetIvar { self_val: find!(self_val), id, state },
18951907
&LoadField { recv, id, offset, return_type } => LoadField { recv: find!(recv), id, offset, return_type },
@@ -2036,6 +2048,7 @@ impl Function {
20362048
Insn::ArrayMax { .. } => types::BasicObject,
20372049
Insn::ArrayInclude { .. } => types::BoolExact,
20382050
Insn::DupArrayInclude { .. } => types::BoolExact,
2051+
Insn::ArrayHash { .. } => types::Fixnum,
20392052
Insn::GetGlobal { .. } => types::BasicObject,
20402053
Insn::GetIvar { .. } => types::BasicObject,
20412054
Insn::LoadPC => types::CPtr,
@@ -3350,6 +3363,7 @@ impl Function {
33503363
worklist.push_back(val)
33513364
}
33523365
&Insn::ArrayMax { ref elements, state }
3366+
| &Insn::ArrayHash { ref elements, state }
33533367
| &Insn::NewHash { ref elements, state }
33543368
| &Insn::NewArray { ref elements, state } => {
33553369
worklist.extend(elements);
@@ -4108,6 +4122,7 @@ impl Function {
41084122
| Insn::InvokeBuiltin { ref args, .. }
41094123
| Insn::InvokeBlock { ref args, .. }
41104124
| Insn::NewArray { elements: ref args, .. }
4125+
| Insn::ArrayHash { elements: ref args, .. }
41114126
| Insn::ArrayMax { elements: ref args, .. } => {
41124127
for &arg in args {
41134128
self.assert_subtype(insn_id, arg, types::BasicObject)?;
@@ -4914,6 +4929,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
49144929
let elements = state.stack_pop_n(count)?;
49154930
let (bop, insn) = match method {
49164931
VM_OPT_NEWARRAY_SEND_MAX => (BOP_MAX, Insn::ArrayMax { elements, state: exit_id }),
4932+
VM_OPT_NEWARRAY_SEND_HASH => (BOP_HASH, Insn::ArrayHash { elements, state: exit_id }),
49174933
VM_OPT_NEWARRAY_SEND_INCLUDE_P => {
49184934
let target = elements[elements.len() - 1];
49194935
let array_elements = elements[..elements.len() - 1].to_vec();

zjit/src/hir/tests.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2013,7 +2013,15 @@ pub mod hir_build_tests {
20132013
Jump bb2(v8, v9, v10, v11, v12)
20142014
bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass):
20152015
v25:BasicObject = SendWithoutBlock v15, :+, v16
2016-
SideExit UnhandledNewarraySend(HASH)
2016+
PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_HASH)
2017+
v32:Fixnum = ArrayHash v15, v16
2018+
PatchPoint NoEPEscape(test)
2019+
v39:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
2020+
v40:ArrayExact = ArrayDup v39
2021+
v42:BasicObject = SendWithoutBlock v14, :puts, v40
2022+
PatchPoint NoEPEscape(test)
2023+
CheckInterrupts
2024+
Return v32
20172025
");
20182026
}
20192027

0 commit comments

Comments
 (0)