mirror of
https://github.com/ruby/ruby.git
synced 2026-01-27 04:24:23 +00:00
ZJIT: Add ArrayAset instruction to HIR (#15747)
Inline `Array#[]=` into `ArrayAset`.
This commit is contained in:
parent
3ea6ec8344
commit
950ffa90b7
Notes:
git
2026-01-07 23:24:58 +00:00
Merged-By: tekknolagi <donotemailthisaddress@bernsteinbear.com>
@ -1646,6 +1646,115 @@ class TestZJIT < Test::Unit::TestCase
|
||||
}, call_threshold: 2, insns: [:opt_aref]
|
||||
end
|
||||
|
||||
def test_array_fixnum_aset
|
||||
assert_compiles '[1, 2, 7]', %q{
|
||||
def test(arr, idx)
|
||||
arr[idx] = 7
|
||||
end
|
||||
arr = [1,2,3]
|
||||
test(arr, 2)
|
||||
arr = [1,2,3]
|
||||
test(arr, 2)
|
||||
arr
|
||||
}, call_threshold: 2, insns: [:opt_aset]
|
||||
end
|
||||
|
||||
def test_array_fixnum_aset_returns_value
|
||||
assert_compiles '7', %q{
|
||||
def test(arr, idx)
|
||||
arr[idx] = 7
|
||||
end
|
||||
test([1,2,3], 2)
|
||||
test([1,2,3], 2)
|
||||
}, call_threshold: 2, insns: [:opt_aset]
|
||||
end
|
||||
|
||||
def test_array_fixnum_aset_out_of_bounds
|
||||
assert_compiles '[1, 2, 3, nil, nil, 7]', %q{
|
||||
def test(arr)
|
||||
arr[5] = 7
|
||||
end
|
||||
arr = [1,2,3]
|
||||
test(arr)
|
||||
arr = [1,2,3]
|
||||
test(arr)
|
||||
arr
|
||||
}, call_threshold: 2
|
||||
end
|
||||
|
||||
def test_array_fixnum_aset_negative_index
|
||||
assert_compiles '[1, 2, 7]', %q{
|
||||
def test(arr)
|
||||
arr[-1] = 7
|
||||
end
|
||||
arr = [1,2,3]
|
||||
test(arr)
|
||||
arr = [1,2,3]
|
||||
test(arr)
|
||||
arr
|
||||
}, call_threshold: 2
|
||||
end
|
||||
|
||||
def test_array_fixnum_aset_shared
|
||||
assert_compiles '[10, 999, -1, -2]', %q{
|
||||
def test(arr, idx, val)
|
||||
arr[idx] = val
|
||||
end
|
||||
arr = (0..50).to_a
|
||||
test(arr, 0, -1)
|
||||
test(arr, 1, -2)
|
||||
shared = arr[10, 20]
|
||||
test(shared, 0, 999)
|
||||
[arr[10], shared[0], arr[0], arr[1]]
|
||||
}, call_threshold: 2
|
||||
end
|
||||
|
||||
def test_array_fixnum_aset_frozen
|
||||
assert_compiles 'FrozenError', %q{
|
||||
def test(arr, idx, val)
|
||||
arr[idx] = val
|
||||
end
|
||||
arr = [1,2,3]
|
||||
test(arr, 1, 9)
|
||||
test(arr, 1, 9)
|
||||
arr.freeze
|
||||
begin
|
||||
test(arr, 1, 9)
|
||||
rescue => e
|
||||
e.class
|
||||
end
|
||||
}, call_threshold: 2
|
||||
end
|
||||
|
||||
def test_array_fixnum_aset_array_subclass
|
||||
assert_compiles '7', %q{
|
||||
class MyArray < Array; end
|
||||
def test(arr, idx)
|
||||
arr[idx] = 7
|
||||
end
|
||||
arr = MyArray.new
|
||||
test(arr, 0)
|
||||
arr = MyArray.new
|
||||
test(arr, 0)
|
||||
arr[0]
|
||||
}, call_threshold: 2, insns: [:opt_aset]
|
||||
end
|
||||
|
||||
def test_array_aset_non_fixnum_index
|
||||
assert_compiles 'TypeError', %q{
|
||||
def test(arr, idx)
|
||||
arr[idx] = 7
|
||||
end
|
||||
test([1,2,3], 0)
|
||||
test([1,2,3], 0)
|
||||
begin
|
||||
test([1,2,3], "0")
|
||||
rescue => e
|
||||
e.class
|
||||
end
|
||||
}, call_threshold: 2
|
||||
end
|
||||
|
||||
def test_empty_array_pop
|
||||
assert_compiles 'nil', %q{
|
||||
def test(arr) = arr.pop
|
||||
|
||||
@ -375,6 +375,9 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
|
||||
Insn::NewRangeFixnum { low, high, flag, state } => gen_new_range_fixnum(asm, opnd!(low), opnd!(high), *flag, &function.frame_state(*state)),
|
||||
Insn::ArrayDup { val, state } => gen_array_dup(asm, opnd!(val), &function.frame_state(*state)),
|
||||
Insn::ArrayArefFixnum { array, index, .. } => gen_aref_fixnum(asm, opnd!(array), opnd!(index)),
|
||||
Insn::ArrayAset { array, index, val } => {
|
||||
no_output!(gen_array_aset(asm, opnd!(array), opnd!(index), opnd!(val)))
|
||||
}
|
||||
Insn::ArrayPop { array, state } => gen_array_pop(asm, opnd!(array), &function.frame_state(*state)),
|
||||
Insn::ArrayLength { array } => gen_array_length(asm, opnd!(array)),
|
||||
Insn::ObjectAlloc { val, state } => gen_object_alloc(jit, asm, opnd!(val), &function.frame_state(*state)),
|
||||
@ -449,6 +452,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
|
||||
Insn::GuardBitEquals { val, expected, state } => gen_guard_bit_equals(jit, asm, opnd!(val), *expected, &function.frame_state(*state)),
|
||||
&Insn::GuardBlockParamProxy { level, state } => no_output!(gen_guard_block_param_proxy(jit, asm, level, &function.frame_state(state))),
|
||||
Insn::GuardNotFrozen { recv, state } => gen_guard_not_frozen(jit, asm, opnd!(recv), &function.frame_state(*state)),
|
||||
Insn::GuardNotShared { recv, state } => gen_guard_not_shared(jit, asm, opnd!(recv), &function.frame_state(*state)),
|
||||
&Insn::GuardLess { left, right, state } => gen_guard_less(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state)),
|
||||
&Insn::GuardGreaterEq { left, right, state } => gen_guard_greater_eq(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state)),
|
||||
Insn::PatchPoint { invariant, state } => no_output!(gen_patch_point(jit, asm, invariant, &function.frame_state(*state))),
|
||||
@ -692,6 +696,15 @@ fn gen_guard_not_frozen(jit: &JITState, asm: &mut Assembler, recv: Opnd, state:
|
||||
recv
|
||||
}
|
||||
|
||||
fn gen_guard_not_shared(jit: &JITState, asm: &mut Assembler, recv: Opnd, state: &FrameState) -> Opnd {
|
||||
let recv = asm.load(recv);
|
||||
// It's a heap object, so check the shared flag
|
||||
let flags = asm.load(Opnd::mem(VALUE_BITS, recv, RUBY_OFFSET_RBASIC_FLAGS));
|
||||
asm.test(flags, (RUBY_ELTS_SHARED as u64).into());
|
||||
asm.jnz(side_exit(jit, state, SideExitReason::GuardNotShared));
|
||||
recv
|
||||
}
|
||||
|
||||
fn gen_guard_less(jit: &JITState, asm: &mut Assembler, left: Opnd, right: Opnd, state: &FrameState) -> Opnd {
|
||||
asm.cmp(left, right);
|
||||
asm.jge(side_exit(jit, state, SideExitReason::GuardLess));
|
||||
@ -1529,6 +1542,20 @@ fn gen_aref_fixnum(
|
||||
asm_ccall!(asm, rb_ary_entry, array, unboxed_idx)
|
||||
}
|
||||
|
||||
fn gen_array_aset(
|
||||
asm: &mut Assembler,
|
||||
array: Opnd,
|
||||
index: Opnd,
|
||||
val: Opnd,
|
||||
) {
|
||||
let unboxed_idx = asm.load(index);
|
||||
let array = asm.load(array);
|
||||
let array_ptr = gen_array_ptr(asm, array);
|
||||
let elem_offset = asm.lshift(unboxed_idx, Opnd::UImm(SIZEOF_VALUE.trailing_zeros() as u64));
|
||||
let elem_ptr = asm.add(array_ptr, elem_offset);
|
||||
asm.store(Opnd::mem(VALUE_BITS, elem_ptr, 0), val);
|
||||
}
|
||||
|
||||
fn gen_array_pop(asm: &mut Assembler, array: Opnd, state: &FrameState) -> lir::Opnd {
|
||||
gen_prepare_leaf_call_with_gc(asm, state);
|
||||
asm_ccall!(asm, rb_ary_pop, array)
|
||||
@ -1545,6 +1572,14 @@ fn gen_array_length(asm: &mut Assembler, array: Opnd) -> lir::Opnd {
|
||||
asm.csel_nz(embedded_len, heap_len)
|
||||
}
|
||||
|
||||
fn gen_array_ptr(asm: &mut Assembler, array: Opnd) -> lir::Opnd {
|
||||
let flags = Opnd::mem(VALUE_BITS, array, RUBY_OFFSET_RBASIC_FLAGS);
|
||||
asm.test(flags, (RARRAY_EMBED_FLAG as u64).into());
|
||||
let heap_ptr = Opnd::mem(usize::BITS as u8, array, RUBY_OFFSET_RARRAY_AS_HEAP_PTR);
|
||||
let embedded_ptr = asm.lea(Opnd::mem(VALUE_BITS, array, RUBY_OFFSET_RARRAY_AS_ARY));
|
||||
asm.csel_nz(embedded_ptr, heap_ptr)
|
||||
}
|
||||
|
||||
/// Compile opt_newarray_hash - create a hash from array elements
|
||||
fn gen_opt_newarray_hash(
|
||||
jit: &JITState,
|
||||
|
||||
@ -225,6 +225,7 @@ pub fn init() -> Annotations {
|
||||
annotate!(rb_cArray, "reverse", types::ArrayExact, leaf, elidable);
|
||||
annotate!(rb_cArray, "join", types::StringExact);
|
||||
annotate!(rb_cArray, "[]", inline_array_aref);
|
||||
annotate!(rb_cArray, "[]=", inline_array_aset);
|
||||
annotate!(rb_cArray, "<<", inline_array_push);
|
||||
annotate!(rb_cArray, "push", inline_array_push);
|
||||
annotate!(rb_cArray, "pop", inline_array_pop);
|
||||
@ -332,6 +333,31 @@ fn inline_array_aref(fun: &mut hir::Function, block: hir::BlockId, recv: hir::In
|
||||
None
|
||||
}
|
||||
|
||||
fn inline_array_aset(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option<hir::InsnId> {
|
||||
if let &[index, val] = args {
|
||||
if fun.likely_a(recv, types::ArrayExact, state)
|
||||
&& fun.likely_a(index, types::Fixnum, state)
|
||||
{
|
||||
let recv = fun.coerce_to(block, recv, types::ArrayExact, state);
|
||||
let index = fun.coerce_to(block, index, types::Fixnum, state);
|
||||
let recv = fun.push_insn(block, hir::Insn::GuardNotFrozen { recv, state });
|
||||
let recv = fun.push_insn(block, hir::Insn::GuardNotShared { recv, state });
|
||||
|
||||
// Bounds check: unbox Fixnum index and guard 0 <= idx < length.
|
||||
let index = fun.push_insn(block, hir::Insn::UnboxFixnum { val: index });
|
||||
let length = fun.push_insn(block, hir::Insn::ArrayLength { array: recv });
|
||||
let index = fun.push_insn(block, hir::Insn::GuardLess { left: index, right: length, state });
|
||||
let zero = fun.push_insn(block, hir::Insn::Const { val: hir::Const::CInt64(0) });
|
||||
let index = fun.push_insn(block, hir::Insn::GuardGreaterEq { left: index, right: zero, state });
|
||||
|
||||
let _ = fun.push_insn(block, hir::Insn::ArrayAset { array: recv, index, val });
|
||||
fun.push_insn(block, hir::Insn::WriteBarrier { recv, val });
|
||||
return Some(val);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn inline_array_push(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option<hir::InsnId> {
|
||||
// Inline only the case of `<<` or `push` when called with a single argument.
|
||||
if let &[val] = args {
|
||||
|
||||
@ -493,6 +493,7 @@ pub enum SideExitReason {
|
||||
GuardShape(ShapeId),
|
||||
GuardBitEquals(Const),
|
||||
GuardNotFrozen,
|
||||
GuardNotShared,
|
||||
GuardLess,
|
||||
GuardGreaterEq,
|
||||
PatchPoint(Invariant),
|
||||
@ -580,6 +581,7 @@ impl std::fmt::Display for SideExitReason {
|
||||
SideExitReason::GuardType(guard_type) => write!(f, "GuardType({guard_type})"),
|
||||
SideExitReason::GuardTypeNot(guard_type) => write!(f, "GuardTypeNot({guard_type})"),
|
||||
SideExitReason::GuardBitEquals(value) => write!(f, "GuardBitEquals({})", value.print(&PtrPrintMap::identity())),
|
||||
SideExitReason::GuardNotShared => write!(f, "GuardNotShared"),
|
||||
SideExitReason::PatchPoint(invariant) => write!(f, "PatchPoint({invariant})"),
|
||||
_ => write!(f, "{self:?}"),
|
||||
}
|
||||
@ -728,6 +730,7 @@ pub enum Insn {
|
||||
/// Push `val` onto `array`, where `array` is already `Array`.
|
||||
ArrayPush { array: InsnId, val: InsnId, state: InsnId },
|
||||
ArrayArefFixnum { array: InsnId, index: InsnId },
|
||||
ArrayAset { array: InsnId, index: InsnId, val: InsnId },
|
||||
ArrayPop { array: InsnId, state: InsnId },
|
||||
/// Return the length of the array as a C `long` ([`types::CInt64`])
|
||||
ArrayLength { array: InsnId },
|
||||
@ -960,6 +963,9 @@ pub enum Insn {
|
||||
/// Side-exit if val is frozen. Does *not* check if the val is an immediate; assumes that it is
|
||||
/// a heap object.
|
||||
GuardNotFrozen { recv: InsnId, state: InsnId },
|
||||
/// Side-exit if val is shared. Does *not* check if the val is an immediate; assumes
|
||||
/// that it is a heap object.
|
||||
GuardNotShared { recv: InsnId, state: InsnId },
|
||||
/// Side-exit if left is not greater than or equal to right (both operands are C long).
|
||||
GuardGreaterEq { left: InsnId, right: InsnId, state: InsnId },
|
||||
/// Side-exit if left is not less than right (both operands are C long).
|
||||
@ -992,8 +998,9 @@ impl Insn {
|
||||
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::SetClassVar { .. } | Insn::ArrayExtend { .. }
|
||||
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetGlobal { .. }
|
||||
| Insn::SetLocal { .. } | Insn::Throw { .. } | Insn::IncrCounter(_) | Insn::IncrCounterPtr { .. }
|
||||
| Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } | Insn::StoreField { .. } | Insn::WriteBarrier { .. }
|
||||
| Insn::HashAset { .. } => false,
|
||||
| Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. }
|
||||
| Insn::StoreField { .. } | Insn::WriteBarrier { .. } | Insn::HashAset { .. }
|
||||
| Insn::ArrayAset { .. } => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
@ -1121,6 +1128,9 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
|
||||
Insn::ArrayArefFixnum { array, index, .. } => {
|
||||
write!(f, "ArrayArefFixnum {array}, {index}")
|
||||
}
|
||||
Insn::ArrayAset { array, index, val, ..} => {
|
||||
write!(f, "ArrayAset {array}, {index}, {val}")
|
||||
}
|
||||
Insn::ArrayPop { array, .. } => {
|
||||
write!(f, "ArrayPop {array}")
|
||||
}
|
||||
@ -1332,6 +1342,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
|
||||
&Insn::GuardShape { val, shape, .. } => { write!(f, "GuardShape {val}, {:p}", self.ptr_map.map_shape(shape)) },
|
||||
Insn::GuardBlockParamProxy { level, .. } => write!(f, "GuardBlockParamProxy l{level}"),
|
||||
Insn::GuardNotFrozen { recv, .. } => write!(f, "GuardNotFrozen {recv}"),
|
||||
Insn::GuardNotShared { recv, .. } => write!(f, "GuardNotShared {recv}"),
|
||||
Insn::GuardLess { left, right, .. } => write!(f, "GuardLess {left}, {right}"),
|
||||
Insn::GuardGreaterEq { left, right, .. } => write!(f, "GuardGreaterEq {left}, {right}"),
|
||||
Insn::PatchPoint { invariant, .. } => { write!(f, "PatchPoint {}", invariant.print(self.ptr_map)) },
|
||||
@ -1968,6 +1979,7 @@ impl Function {
|
||||
&GuardShape { val, shape, state } => GuardShape { val: find!(val), shape, state },
|
||||
&GuardBlockParamProxy { level, state } => GuardBlockParamProxy { level, state: find!(state) },
|
||||
&GuardNotFrozen { recv, state } => GuardNotFrozen { recv: find!(recv), state },
|
||||
&GuardNotShared { recv, state } => GuardNotShared { recv: find!(recv), state },
|
||||
&GuardGreaterEq { left, right, state } => GuardGreaterEq { left: find!(left), right: find!(right), state },
|
||||
&GuardLess { left, right, state } => GuardLess { left: find!(left), right: find!(right), state },
|
||||
&FixnumAdd { left, right, state } => FixnumAdd { left: find!(left), right: find!(right), state },
|
||||
@ -2071,6 +2083,7 @@ impl Function {
|
||||
&NewRange { low, high, flag, state } => NewRange { low: find!(low), high: find!(high), flag, state: find!(state) },
|
||||
&NewRangeFixnum { low, high, flag, state } => NewRangeFixnum { low: find!(low), high: find!(high), flag, state: find!(state) },
|
||||
&ArrayArefFixnum { array, index } => ArrayArefFixnum { array: find!(array), index: find!(index) },
|
||||
&ArrayAset { array, index, val } => ArrayAset { array: find!(array), index: find!(index), val: find!(val) },
|
||||
&ArrayPop { array, state } => ArrayPop { array: find!(array), state: find!(state) },
|
||||
&ArrayLength { array } => ArrayLength { array: find!(array) },
|
||||
&ArrayMax { ref elements, state } => ArrayMax { elements: find_vec!(elements), state: find!(state) },
|
||||
@ -2143,7 +2156,7 @@ impl Function {
|
||||
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::SetClassVar { .. } | Insn::ArrayExtend { .. }
|
||||
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetLocal { .. } | Insn::IncrCounter(_)
|
||||
| Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } | Insn::IncrCounterPtr { .. }
|
||||
| Insn::StoreField { .. } | Insn::WriteBarrier { .. } | Insn::HashAset { .. } =>
|
||||
| Insn::StoreField { .. } | Insn::WriteBarrier { .. } | Insn::HashAset { .. } | Insn::ArrayAset { .. } =>
|
||||
panic!("Cannot infer type of instruction with no output: {}. See Insn::has_output().", self.insns[insn.0]),
|
||||
Insn::Const { val: Const::Value(val) } => Type::from_value(*val),
|
||||
Insn::Const { val: Const::CBool(val) } => Type::from_cbool(*val),
|
||||
@ -2197,7 +2210,7 @@ impl Function {
|
||||
Insn::GuardTypeNot { .. } => types::BasicObject,
|
||||
Insn::GuardBitEquals { val, expected, .. } => self.type_of(*val).intersection(Type::from_const(*expected)),
|
||||
Insn::GuardShape { val, .. } => self.type_of(*val),
|
||||
Insn::GuardNotFrozen { recv, .. } => self.type_of(*recv),
|
||||
Insn::GuardNotFrozen { recv, .. } | Insn::GuardNotShared { recv, .. } => self.type_of(*recv),
|
||||
Insn::GuardLess { left, .. } => self.type_of(*left),
|
||||
Insn::GuardGreaterEq { left, .. } => self.type_of(*left),
|
||||
Insn::FixnumAdd { .. } => types::Fixnum,
|
||||
@ -3940,6 +3953,7 @@ impl Function {
|
||||
| &Insn::GuardBitEquals { val, state, .. }
|
||||
| &Insn::GuardShape { val, state, .. }
|
||||
| &Insn::GuardNotFrozen { recv: val, state }
|
||||
| &Insn::GuardNotShared { recv: val, state }
|
||||
| &Insn::ToArray { val, state }
|
||||
| &Insn::IsMethodCfunc { val, state, .. }
|
||||
| &Insn::ToNewArray { val, state }
|
||||
@ -4004,6 +4018,11 @@ impl Function {
|
||||
worklist.push_back(array);
|
||||
worklist.push_back(index);
|
||||
}
|
||||
&Insn::ArrayAset { array, index, val } => {
|
||||
worklist.push_back(array);
|
||||
worklist.push_back(index);
|
||||
worklist.push_back(val);
|
||||
}
|
||||
&Insn::ArrayPop { array, state } => {
|
||||
worklist.push_back(array);
|
||||
worklist.push_back(state);
|
||||
@ -4628,7 +4647,7 @@ impl Function {
|
||||
| Insn::DefinedIvar { self_val: val, .. } => {
|
||||
self.assert_subtype(insn_id, val, types::BasicObject)
|
||||
}
|
||||
Insn::GuardNotFrozen { recv, .. } => {
|
||||
Insn::GuardNotFrozen { recv, .. } | Insn::GuardNotShared { recv, .. } => {
|
||||
self.assert_subtype(insn_id, recv, types::HeapBasicObject)
|
||||
}
|
||||
// Instructions with 2 Ruby object operands
|
||||
@ -4716,6 +4735,10 @@ impl Function {
|
||||
self.assert_subtype(insn_id, array, types::Array)?;
|
||||
self.assert_subtype(insn_id, index, types::Fixnum)
|
||||
}
|
||||
Insn::ArrayAset { array, index, .. } => {
|
||||
self.assert_subtype(insn_id, array, types::ArrayExact)?;
|
||||
self.assert_subtype(insn_id, index, types::CInt64)
|
||||
}
|
||||
// Instructions with Hash operands
|
||||
Insn::HashAref { hash, .. }
|
||||
| Insn::HashAset { hash, .. } => self.assert_subtype(insn_id, hash, types::HashExact),
|
||||
|
||||
@ -4772,6 +4772,36 @@ mod hir_opt_tests {
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dont_optimize_array_aset_if_redefined() {
|
||||
eval(r##"
|
||||
class Array
|
||||
def []=(*args); :redefined; end
|
||||
end
|
||||
|
||||
def test(arr)
|
||||
arr[1] = 10
|
||||
end
|
||||
"##);
|
||||
assert_snapshot!(hir_string("test"), @r"
|
||||
fn test@<compiled>:7:
|
||||
bb0():
|
||||
EntryPoint interpreter
|
||||
v1:BasicObject = LoadSelf
|
||||
v2:BasicObject = GetLocal :arr, l0, SP@4
|
||||
Jump bb2(v1, v2)
|
||||
bb1(v5:BasicObject, v6:BasicObject):
|
||||
EntryPoint JIT(0)
|
||||
Jump bb2(v5, v6)
|
||||
bb2(v8:BasicObject, v9:BasicObject):
|
||||
v16:Fixnum[1] = Const Value(1)
|
||||
v18:Fixnum[10] = Const Value(10)
|
||||
v22:BasicObject = SendWithoutBlock v9, :[]=, v16, v18 # SendFallbackReason: Uncategorized(opt_aset)
|
||||
CheckInterrupts
|
||||
Return v18
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dont_optimize_array_max_if_redefined() {
|
||||
eval(r##"
|
||||
@ -6901,7 +6931,7 @@ mod hir_opt_tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_optimize_array_aset() {
|
||||
fn test_optimize_array_aset_literal() {
|
||||
eval("
|
||||
def test(arr)
|
||||
arr[1] = 10
|
||||
@ -6924,12 +6954,93 @@ mod hir_opt_tests {
|
||||
PatchPoint MethodRedefined(Array@0x1000, []=@0x1008, cme:0x1010)
|
||||
PatchPoint NoSingletonClass(Array@0x1000)
|
||||
v31:ArrayExact = GuardType v9, ArrayExact
|
||||
v32:BasicObject = CCallVariadic v31, :Array#[]=@0x1038, v16, v18
|
||||
v32:ArrayExact = GuardNotFrozen v31
|
||||
v33:ArrayExact = GuardNotShared v32
|
||||
v34:CInt64 = UnboxFixnum v16
|
||||
v35:CInt64 = ArrayLength v33
|
||||
v36:CInt64 = GuardLess v34, v35
|
||||
v37:CInt64[0] = Const CInt64(0)
|
||||
v38:CInt64 = GuardGreaterEq v36, v37
|
||||
ArrayAset v33, v38, v18
|
||||
WriteBarrier v33, v18
|
||||
IncrCounter inline_cfunc_optimized_send_count
|
||||
CheckInterrupts
|
||||
Return v18
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_optimize_array_aset_profiled() {
|
||||
eval("
|
||||
def test(arr, index, val)
|
||||
arr[index] = val
|
||||
end
|
||||
test([], 0, 1)
|
||||
");
|
||||
assert_snapshot!(hir_string("test"), @r"
|
||||
fn test@<compiled>:3:
|
||||
bb0():
|
||||
EntryPoint interpreter
|
||||
v1:BasicObject = LoadSelf
|
||||
v2:BasicObject = GetLocal :arr, l0, SP@6
|
||||
v3:BasicObject = GetLocal :index, l0, SP@5
|
||||
v4:BasicObject = GetLocal :val, l0, SP@4
|
||||
Jump bb2(v1, v2, v3, v4)
|
||||
bb1(v7:BasicObject, v8:BasicObject, v9:BasicObject, v10:BasicObject):
|
||||
EntryPoint JIT(0)
|
||||
Jump bb2(v7, v8, v9, v10)
|
||||
bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject):
|
||||
PatchPoint MethodRedefined(Array@0x1000, []=@0x1008, cme:0x1010)
|
||||
PatchPoint NoSingletonClass(Array@0x1000)
|
||||
v35:ArrayExact = GuardType v13, ArrayExact
|
||||
v36:Fixnum = GuardType v14, Fixnum
|
||||
v37:ArrayExact = GuardNotFrozen v35
|
||||
v38:ArrayExact = GuardNotShared v37
|
||||
v39:CInt64 = UnboxFixnum v36
|
||||
v40:CInt64 = ArrayLength v38
|
||||
v41:CInt64 = GuardLess v39, v40
|
||||
v42:CInt64[0] = Const CInt64(0)
|
||||
v43:CInt64 = GuardGreaterEq v41, v42
|
||||
ArrayAset v38, v43, v15
|
||||
WriteBarrier v38, v15
|
||||
IncrCounter inline_cfunc_optimized_send_count
|
||||
CheckInterrupts
|
||||
Return v15
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_optimize_array_aset_array_subclass() {
|
||||
eval("
|
||||
class MyArray < Array; end
|
||||
def test(arr, index, val)
|
||||
arr[index] = val
|
||||
end
|
||||
a = MyArray.new
|
||||
test(a, 0, 1)
|
||||
");
|
||||
assert_snapshot!(hir_string("test"), @r"
|
||||
fn test@<compiled>:4:
|
||||
bb0():
|
||||
EntryPoint interpreter
|
||||
v1:BasicObject = LoadSelf
|
||||
v2:BasicObject = GetLocal :arr, l0, SP@6
|
||||
v3:BasicObject = GetLocal :index, l0, SP@5
|
||||
v4:BasicObject = GetLocal :val, l0, SP@4
|
||||
Jump bb2(v1, v2, v3, v4)
|
||||
bb1(v7:BasicObject, v8:BasicObject, v9:BasicObject, v10:BasicObject):
|
||||
EntryPoint JIT(0)
|
||||
Jump bb2(v7, v8, v9, v10)
|
||||
bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject):
|
||||
PatchPoint MethodRedefined(MyArray@0x1000, []=@0x1008, cme:0x1010)
|
||||
PatchPoint NoSingletonClass(MyArray@0x1000)
|
||||
v35:ArraySubclass[class_exact:MyArray] = GuardType v13, ArraySubclass[class_exact:MyArray]
|
||||
v36:BasicObject = CCallVariadic v35, :Array#[]=@0x1038, v14, v15
|
||||
CheckInterrupts
|
||||
Return v15
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_optimize_array_ltlt() {
|
||||
eval("
|
||||
|
||||
@ -191,6 +191,7 @@ make_counters! {
|
||||
exit_guard_int_equals_failure,
|
||||
exit_guard_shape_failure,
|
||||
exit_guard_not_frozen_failure,
|
||||
exit_guard_not_shared_failure,
|
||||
exit_guard_less_failure,
|
||||
exit_guard_greater_eq_failure,
|
||||
exit_patchpoint_bop_redefined,
|
||||
@ -511,6 +512,7 @@ pub fn side_exit_counter(reason: crate::hir::SideExitReason) -> Counter {
|
||||
GuardBitEquals(_) => exit_guard_bit_equals_failure,
|
||||
GuardShape(_) => exit_guard_shape_failure,
|
||||
GuardNotFrozen => exit_guard_not_frozen_failure,
|
||||
GuardNotShared => exit_guard_not_shared_failure,
|
||||
GuardLess => exit_guard_less_failure,
|
||||
GuardGreaterEq => exit_guard_greater_eq_failure,
|
||||
CalleeSideExit => exit_callee_side_exit,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user