Skip to content

Commit b06dd64

Browse files
nirvdrumtekknolagi
authored andcommitted
ZJIT: Compile the VM_OPT_NEWARRAY_SEND_HASH variant of opt_newarray_send
1 parent 604fc05 commit b06dd64

File tree

4 files changed

+107
-1
lines changed

4 files changed

+107
-1
lines changed

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 '42', %q{
1063+
Array.class_eval { def hash = 42 }
1064+
1065+
def test(x)
1066+
[1, 2, x].hash
1067+
end
1068+
test(20)
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
@@ -464,6 +464,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
464464
&Insn::IsBlockGiven => gen_is_block_given(jit, asm),
465465
Insn::ArrayInclude { elements, target, state } => gen_array_include(jit, asm, opnds!(elements), opnd!(target), &function.frame_state(*state)),
466466
&Insn::DupArrayInclude { ary, target, state } => gen_dup_array_include(jit, asm, ary, opnd!(target), &function.frame_state(state)),
467+
Insn::ArrayHash { elements, state } => gen_opt_newarray_hash(jit, asm, opnds!(elements), &function.frame_state(*state)),
467468
&Insn::ArrayMax { state, .. }
468469
| &Insn::FixnumDiv { state, .. }
469470
| &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 },
@@ -1040,6 +1042,15 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
10401042
}
10411043
Ok(())
10421044
}
1045+
Insn::ArrayHash { elements, .. } => {
1046+
write!(f, "ArrayHash")?;
1047+
let mut prefix = " ";
1048+
for element in elements {
1049+
write!(f, "{prefix}{element}")?;
1050+
prefix = ", ";
1051+
}
1052+
Ok(())
1053+
}
10431054
Insn::ArrayInclude { elements, target, .. } => {
10441055
write!(f, "ArrayInclude")?;
10451056
let mut prefix = " ";
@@ -1887,6 +1898,7 @@ impl Function {
18871898
&ArrayMax { ref elements, state } => ArrayMax { elements: find_vec!(elements), state: find!(state) },
18881899
&ArrayInclude { ref elements, target, state } => ArrayInclude { elements: find_vec!(elements), target: find!(target), state: find!(state) },
18891900
&DupArrayInclude { ary, target, state } => DupArrayInclude { ary, target: find!(target), state: find!(state) },
1901+
&ArrayHash { ref elements, state } => ArrayHash { elements: find_vec!(elements), state },
18901902
&SetGlobal { id, val, state } => SetGlobal { id, val: find!(val), state },
18911903
&GetIvar { self_val, id, ic, state } => GetIvar { self_val: find!(self_val), id, ic, state },
18921904
&LoadField { recv, id, offset, return_type } => LoadField { recv: find!(recv), id, offset, return_type },
@@ -2032,6 +2044,7 @@ impl Function {
20322044
Insn::ArrayMax { .. } => types::BasicObject,
20332045
Insn::ArrayInclude { .. } => types::BoolExact,
20342046
Insn::DupArrayInclude { .. } => types::BoolExact,
2047+
Insn::ArrayHash { .. } => types::Fixnum,
20352048
Insn::GetGlobal { .. } => types::BasicObject,
20362049
Insn::GetIvar { .. } => types::BasicObject,
20372050
Insn::LoadPC => types::CPtr,
@@ -3346,6 +3359,7 @@ impl Function {
33463359
worklist.push_back(val)
33473360
}
33483361
&Insn::ArrayMax { ref elements, state }
3362+
| &Insn::ArrayHash { ref elements, state }
33493363
| &Insn::NewHash { ref elements, state }
33503364
| &Insn::NewArray { ref elements, state } => {
33513365
worklist.extend(elements);
@@ -4102,6 +4116,7 @@ impl Function {
41024116
| Insn::InvokeBuiltin { ref args, .. }
41034117
| Insn::InvokeBlock { ref args, .. }
41044118
| Insn::NewArray { elements: ref args, .. }
4119+
| Insn::ArrayHash { elements: ref args, .. }
41054120
| Insn::ArrayMax { elements: ref args, .. } => {
41064121
for &arg in args {
41074122
self.assert_subtype(insn_id, arg, types::BasicObject)?;
@@ -4908,6 +4923,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
49084923
let elements = state.stack_pop_n(count)?;
49094924
let (bop, insn) = match method {
49104925
VM_OPT_NEWARRAY_SEND_MAX => (BOP_MAX, Insn::ArrayMax { elements, state: exit_id }),
4926+
VM_OPT_NEWARRAY_SEND_HASH => (BOP_HASH, Insn::ArrayHash { elements, state: exit_id }),
49114927
VM_OPT_NEWARRAY_SEND_INCLUDE_P => {
49124928
let target = elements[elements.len() - 1];
49134929
let array_elements = elements[..elements.len() - 1].to_vec();

zjit/src/hir/tests.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2013,7 +2013,49 @@ 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
2025+
");
2026+
}
2027+
2028+
#[test]
2029+
fn test_opt_newarray_send_hash_redefined() {
2030+
eval("
2031+
Array.class_eval { def hash = 42 }
2032+
2033+
def test(a,b)
2034+
sum = a+b
2035+
result = [a,b].hash
2036+
puts [1,2,3]
2037+
result
2038+
end
2039+
");
2040+
assert_contains_opcode("test", YARVINSN_opt_newarray_send);
2041+
assert_snapshot!(hir_string("test"), @r"
2042+
fn test@<compiled>:5:
2043+
bb0():
2044+
EntryPoint interpreter
2045+
v1:BasicObject = LoadSelf
2046+
v2:BasicObject = GetLocal l0, SP@7
2047+
v3:BasicObject = GetLocal l0, SP@6
2048+
v4:NilClass = Const Value(nil)
2049+
v5:NilClass = Const Value(nil)
2050+
Jump bb2(v1, v2, v3, v4, v5)
2051+
bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject):
2052+
EntryPoint JIT(0)
2053+
v11:NilClass = Const Value(nil)
2054+
v12:NilClass = Const Value(nil)
2055+
Jump bb2(v8, v9, v10, v11, v12)
2056+
bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass):
2057+
v25:BasicObject = SendWithoutBlock v15, :+, v16
2058+
SideExit PatchPoint(BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_HASH))
20172059
");
20182060
}
20192061

0 commit comments

Comments
 (0)