Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ jobs:
matrix:
include:
# Using the same setup as ZJIT jobs
- bench_opts: '--warmup=1 --bench=1 --excludes=lobsters,ruby-lsp'
- bench_opts: '--warmup=1 --bench=1'

runs-on: ubuntu-24.04

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/zjit-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ jobs:
include:
# Test --call-threshold=2 with 2 iterations in total
- ruby_opts: '--zjit-call-threshold=2'
bench_opts: '--warmup=1 --bench=1 --excludes=lobsters,ruby-lsp'
bench_opts: '--warmup=1 --bench=1'
configure: '--enable-zjit=dev_nodebug' # --enable-zjit=dev is too slow

runs-on: macos-14
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/zjit-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ jobs:
include:
# Test --call-threshold=2 with 2 iterations in total
- ruby_opts: '--zjit-call-threshold=2'
bench_opts: '--warmup=1 --bench=1 --excludes=lobsters,ruby-lsp'
bench_opts: '--warmup=1 --bench=1'
configure: '--enable-zjit=dev_nodebug' # --enable-zjit=dev is too slow

runs-on: ubuntu-24.04
Expand Down
2 changes: 1 addition & 1 deletion ext/ripper/lib/ripper/lexer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def [](index)
when 0, :to_int
@to_int
when 1, :to_s
@event
@to_s
else
nil
end
Expand Down
5 changes: 2 additions & 3 deletions hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -1558,9 +1558,8 @@ rb_hash_dup(VALUE hash)
const VALUE flags = RBASIC(hash)->flags;
VALUE ret = hash_dup(hash, rb_obj_class(hash), flags & RHASH_PROC_DEFAULT);

if (rb_obj_gen_fields_p(hash)) {
rb_copy_generic_ivar(ret, hash);
}
rb_copy_generic_ivar(ret, hash);

return ret;
}

Expand Down
13 changes: 7 additions & 6 deletions zjit/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
Insn::Test { val } => gen_test(asm, opnd!(val)),
Insn::GuardType { val, guard_type, state } => gen_guard_type(jit, asm, opnd!(val), *guard_type, &function.frame_state(*state)),
Insn::GuardTypeNot { val, guard_type, state } => gen_guard_type_not(jit, asm, opnd!(val), *guard_type, &function.frame_state(*state)),
Insn::GuardBitEquals { val, expected, state } => gen_guard_bit_equals(jit, asm, opnd!(val), *expected, &function.frame_state(*state)),
&Insn::GuardBitEquals { val, expected, reason, state } => gen_guard_bit_equals(jit, asm, opnd!(val), expected, reason, &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)),
Expand Down Expand Up @@ -498,7 +498,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
Insn::LoadPC => gen_load_pc(asm),
Insn::LoadEC => gen_load_ec(),
Insn::LoadSelf => gen_load_self(),
&Insn::LoadField { recv, id, offset, return_type: _ } => gen_load_field(asm, opnd!(recv), id, offset),
&Insn::LoadField { recv, id, offset, return_type } => gen_load_field(asm, opnd!(recv), id, offset, return_type),
&Insn::StoreField { recv, id, offset, val } => no_output!(gen_store_field(asm, opnd!(recv), id, offset, opnd!(val), function.type_of(val))),
&Insn::WriteBarrier { recv, val } => no_output!(gen_write_barrier(asm, opnd!(recv), opnd!(val), function.type_of(val))),
&Insn::IsBlockGiven => gen_is_block_given(jit, asm),
Expand Down Expand Up @@ -1130,10 +1130,10 @@ fn gen_load_self() -> Opnd {
Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)
}

fn gen_load_field(asm: &mut Assembler, recv: Opnd, id: ID, offset: i32) -> Opnd {
fn gen_load_field(asm: &mut Assembler, recv: Opnd, id: ID, offset: i32, return_type: Type) -> Opnd {
asm_comment!(asm, "Load field id={} offset={}", id.contents_lossy(), offset);
let recv = asm.load(recv);
asm.load(Opnd::mem(64, recv, offset))
asm.load(Opnd::mem(return_type.num_bits(), recv, offset))
}

fn gen_store_field(asm: &mut Assembler, recv: Opnd, id: ID, offset: i32, val: Opnd, val_type: Type) {
Expand Down Expand Up @@ -2083,14 +2083,15 @@ fn gen_guard_type_not(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, g
}

/// Compile an identity check with a side exit
fn gen_guard_bit_equals(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, expected: crate::hir::Const, state: &FrameState) -> lir::Opnd {
fn gen_guard_bit_equals(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, expected: crate::hir::Const, reason: SideExitReason, state: &FrameState) -> lir::Opnd {
let expected_opnd: Opnd = match expected {
crate::hir::Const::Value(v) => { Opnd::Value(v) }
crate::hir::Const::CInt64(v) => { v.into() }
crate::hir::Const::CShape(v) => { Opnd::UImm(v.0 as u64) }
_ => panic!("gen_guard_bit_equals: unexpected hir::Const {expected:?}"),
};
asm.cmp(val, expected_opnd);
asm.jnz(side_exit(jit, state, GuardBitEquals(expected)));
asm.jnz(side_exit(jit, state, reason));
val
}

Expand Down
36 changes: 28 additions & 8 deletions zjit/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ pub enum SideExitReason {
GuardType(Type),
GuardTypeNot(Type),
GuardShape(ShapeId),
GuardBitEquals(Const),
ExpandArray,
GuardNotFrozen,
GuardNotShared,
GuardLess,
Expand Down Expand Up @@ -580,7 +580,6 @@ impl std::fmt::Display for SideExitReason {
SideExitReason::UnhandledDuparraySend(method_id) => write!(f, "UnhandledDuparraySend({method_id})"),
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:?}"),
Expand Down Expand Up @@ -954,7 +953,7 @@ pub enum Insn {
GuardType { val: InsnId, guard_type: Type, state: InsnId },
GuardTypeNot { val: InsnId, guard_type: Type, state: InsnId },
/// Side-exit if val is not the expected Const.
GuardBitEquals { val: InsnId, expected: Const, state: InsnId },
GuardBitEquals { val: InsnId, expected: Const, reason: SideExitReason, state: InsnId },
/// Side-exit if val doesn't have the expected shape.
GuardShape { val: InsnId, shape: ShapeId, state: InsnId },
/// Side-exit if the block param has been modified or the block handler for the frame
Expand Down Expand Up @@ -1975,7 +1974,7 @@ impl Function {
&IfFalse { val, ref target } => IfFalse { val: find!(val), target: find_branch_edge!(target) },
&GuardType { val, guard_type, state } => GuardType { val: find!(val), guard_type, state },
&GuardTypeNot { val, guard_type, state } => GuardTypeNot { val: find!(val), guard_type, state },
&GuardBitEquals { val, expected, state } => GuardBitEquals { val: find!(val), expected, state },
&GuardBitEquals { val, expected, reason, state } => GuardBitEquals { val: find!(val), expected, reason, state },
&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 },
Expand Down Expand Up @@ -3069,6 +3068,24 @@ impl Function {
self.infer_types();
}

fn load_shape(&mut self, block: BlockId, recv: InsnId) -> InsnId {
self.push_insn(block, Insn::LoadField {
recv,
id: ID!(_shape_id),
offset: unsafe { rb_shape_id_offset() } as i32,
return_type: types::CShape
})
}

fn guard_shape(&mut self, block: BlockId, val: InsnId, expected: ShapeId, state: InsnId) -> InsnId {
self.push_insn(block, Insn::GuardBitEquals {
val,
expected: Const::CShape(expected),
reason: SideExitReason::GuardShape(expected),
state
})
}

fn optimize_getivar(&mut self) {
for block in self.rpo() {
let old_insns = std::mem::take(&mut self.blocks[block.0].insns);
Expand All @@ -3094,7 +3111,8 @@ impl Function {
self.push_insn_id(block, insn_id); continue;
}
let self_val = self.push_insn(block, Insn::GuardType { val: self_val, guard_type: types::HeapBasicObject, state });
let self_val = self.push_insn(block, Insn::GuardShape { val: self_val, shape: recv_type.shape(), state });
let shape = self.load_shape(block, self_val);
self.guard_shape(block, shape, recv_type.shape(), state);
let mut ivar_index: u16 = 0;
let replacement = if ! unsafe { rb_shape_get_iv_index(recv_type.shape().0, id, &mut ivar_index) } {
// If there is no IVAR index, then the ivar was undefined when we
Expand Down Expand Up @@ -3148,7 +3166,8 @@ impl Function {
self.push_insn_id(block, insn_id); continue;
}
let self_val = self.push_insn(block, Insn::GuardType { val: self_val, guard_type: types::HeapBasicObject, state });
let _ = self.push_insn(block, Insn::GuardShape { val: self_val, shape: recv_type.shape(), state });
let shape = self.load_shape(block, self_val);
self.guard_shape(block, shape, recv_type.shape(), state);
let mut ivar_index: u16 = 0;
let replacement = if unsafe { rb_shape_get_iv_index(recv_type.shape().0, id, &mut ivar_index) } {
self.push_insn(block, Insn::Const { val: Const::Value(pushval) })
Expand Down Expand Up @@ -3219,7 +3238,8 @@ impl Function {
// Fall through to emitting the ivar write
}
let self_val = self.push_insn(block, Insn::GuardType { val: self_val, guard_type: types::HeapBasicObject, state });
let self_val = self.push_insn(block, Insn::GuardShape { val: self_val, shape: recv_type.shape(), state });
let shape = self.load_shape(block, self_val);
self.guard_shape(block, shape, recv_type.shape(), state);
// Current shape contains this ivar
let (ivar_storage, offset) = if recv_type.flags().is_embedded() {
// See ROBJECT_FIELDS() from include/ruby/internal/core/robject.h
Expand Down Expand Up @@ -6261,7 +6281,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
let val = state.stack_pop()?;
let array = fun.push_insn(block, Insn::GuardType { val, guard_type: types::ArrayExact, state: exit_id, });
let length = fun.push_insn(block, Insn::ArrayLength { array });
fun.push_insn(block, Insn::GuardBitEquals { val: length, expected: Const::CInt64(num as i64), state: exit_id });
fun.push_insn(block, Insn::GuardBitEquals { val: length, expected: Const::CInt64(num as i64), reason: SideExitReason::ExpandArray, state: exit_id });
for i in (0..num).rev() {
// TODO(max): Add a short-cut path for long indices into an array where the
// index is known to be in-bounds
Expand Down
Loading