summaryrefslogtreecommitdiff
path: root/zjit
diff options
context:
space:
mode:
Diffstat (limited to 'zjit')
-rw-r--r--zjit/bindgen/src/main.rs3
-rw-r--r--zjit/src/backend/lir.rs10
-rw-r--r--zjit/src/codegen.rs146
-rw-r--r--zjit/src/codegen_tests.rs1
-rw-r--r--zjit/src/cruby.rs10
-rw-r--r--zjit/src/cruby_bindings.inc.rs4
-rw-r--r--zjit/src/hir.rs18
-rw-r--r--zjit/src/hir/opt_tests.rs69
-rw-r--r--zjit/src/hir_type/gen_hir_type.rb7
-rw-r--r--zjit/src/hir_type/hir_type.inc.rs8
-rw-r--r--zjit/src/hir_type/mod.rs4
-rw-r--r--zjit/src/profile.rs16
12 files changed, 163 insertions, 133 deletions
diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs
index 32b75117c8..59daf0b92f 100644
--- a/zjit/bindgen/src/main.rs
+++ b/zjit/bindgen/src/main.rs
@@ -321,8 +321,7 @@ fn main() {
.allowlist_function("rb_jit_shape_complex_p")
.allowlist_function("rb_jit_multi_ractor_p")
.allowlist_function("rb_jit_class_fields_embedded_p")
- .allowlist_function("rb_jit_typed_data_p")
- .allowlist_function("rb_jit_typed_data_fields_embedded_p")
+ .allowlist_function("rb_jit_data_fields_embedded_p")
.allowlist_function("rb_jit_vm_lock_then_barrier")
.allowlist_function("rb_jit_vm_unlock")
.allowlist_function("rb_jit_for_each_iseq")
diff --git a/zjit/src/backend/lir.rs b/zjit/src/backend/lir.rs
index b29f832000..25fa0cc151 100644
--- a/zjit/src/backend/lir.rs
+++ b/zjit/src/backend/lir.rs
@@ -203,7 +203,7 @@ pub use crate::backend::current::{
mem_base_reg,
Reg,
EC, CFP, SP,
- NATIVE_STACK_PTR, NATIVE_BASE_PTR,
+ NATIVE_BASE_PTR,
C_ARG_OPNDS, C_RET_OPND,
};
@@ -1406,10 +1406,9 @@ pub struct Assembler {
/// On `compile`, it also disables the backend's use of them.
pub(super) accept_scratch_reg: bool,
- /// The Assembler can use NATIVE_BASE_PTR + stack_base_idx as the
- /// first stack slot in case it needs to allocate memory. This is
- /// equal to the number of spilled basic block arguments.
- pub(super) stack_base_idx: usize,
+ /// The maximum number of stack slots that have been reserved
+ /// by Assembler::alloc_stack().
+ pub stack_base_idx: usize,
/// If Some, the next ccall should verify its leafness
leaf_ccall_stack_size: Option<usize>,
@@ -3603,6 +3602,7 @@ pub(crate) use asm_ccall;
mod tests {
use super::*;
use insta::assert_snapshot;
+ use crate::backend::current::NATIVE_STACK_PTR;
fn scratch_reg() -> Opnd {
Assembler::new_with_scratch_reg().1
diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs
index fbf62a5c81..7c893a72fe 100644
--- a/zjit/src/codegen.rs
+++ b/zjit/src/codegen.rs
@@ -19,13 +19,17 @@ use crate::state::ZJITState;
use crate::stats::{CompileError, exit_counter_for_compile_error, exit_counter_for_unhandled_hir_insn, incr_counter, incr_counter_by, send_fallback_counter, send_fallback_counter_for_method_type, send_fallback_counter_for_super_method_type, send_fallback_counter_ptr_for_opcode, send_without_block_fallback_counter_for_method_type, send_without_block_fallback_counter_for_optimized_method_type};
use crate::stats::{counter_ptr, with_time_stat, trace_compile_phase, Counter, Counter::{compile_time_ns, exit_compile_error}};
use crate::{asm::CodeBlock, cruby::*, options::debug, virtualmem::CodePtr};
-use crate::backend::lir::{self, Assembler, C_ARG_OPNDS, C_RET_OPND, CFP, EC, NATIVE_BASE_PTR, NATIVE_STACK_PTR, Opnd, SP, SideExit, SideExitRecompile, Target, asm_ccall, asm_comment};
+use crate::backend::lir::{self, Assembler, C_ARG_OPNDS, C_RET_OPND, CFP, EC, NATIVE_BASE_PTR, Opnd, SP, SideExit, SideExitRecompile, Target, asm_ccall, asm_comment};
use crate::hir::{iseq_to_hir, BlockId, Invariant, RangeType, SideExitReason::{self, *}, SpecialBackrefSymbol, SpecialObjectType};
use crate::hir::{BlockHandler, Const, FieldName, FrameState, Function, Insn, InsnId, Recompile, SendFallbackReason};
use crate::hir_type::{types, Type};
use crate::options::{get_option, PerfMap};
use crate::cast::IntoUsize;
+/// The number of stack slots used for JITFrame. gen_save_pc_for_gc() writes
+/// JITFrame into this number of slots at the bottom of the native stack.
+const JIT_FRAME_SIZE: usize = 1;
+
/// Default maximum number of compiled versions per ISEQ.
const DEFAULT_MAX_VERSIONS: usize = 2;
@@ -66,17 +70,22 @@ struct JITState {
/// ISEQ calls that need to be compiled later
iseq_calls: Vec<IseqCallRef>,
+
+ /// The number of native stack slots reserved for JITFrame.
+ /// gen_save_pc_for_gc() writes JITFrame into the allocated space.
+ jit_frame_size: usize,
}
impl JITState {
/// Create a new JITState instance
- fn new(version: IseqVersionRef, num_insns: usize, num_blocks: usize) -> Self {
+ fn new(version: IseqVersionRef, num_insns: usize, num_blocks: usize, jit_frame_size: usize) -> Self {
JITState {
version,
opnds: vec![None; num_insns],
labels: vec![None; num_blocks],
jit_entries: Vec::default(),
iseq_calls: Vec::default(),
+ jit_frame_size,
}
}
@@ -374,9 +383,8 @@ fn gen_iseq_body(cb: &mut CodeBlock, iseq: IseqPtr, mut version: IseqVersionRef,
/// Compile a function
fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, version: IseqVersionRef, function: &Function) -> Result<(IseqCodePtrs, Vec<CodePtr>, Vec<IseqCallRef>), CompileError> {
let (mut jit, asm) = trace_compile_phase("codegen", || {
- let num_spilled_params = max_num_params(function).saturating_sub(ALLOC_REGS.len());
- let mut jit = JITState::new(version, function.num_insns(), function.num_blocks());
- let mut asm = Assembler::new_with_stack_slots(num_spilled_params + 1); // +1 for JITFrame
+ let mut jit = JITState::new(version, function.num_insns(), function.num_blocks(), JIT_FRAME_SIZE);
+ let mut asm = Assembler::new_with_stack_slots(JIT_FRAME_SIZE);
// Mapping from HIR block IDs to LIR block IDs.
// This is is a one-to-one mapping from HIR to LIR blocks used for finding
@@ -583,7 +591,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
gen_const_uint32(val.0)
}
Insn::Const { .. } => panic!("Unexpected Const in gen_insn: {insn}"),
- Insn::NewArray { elements, state } => gen_new_array(asm, opnds!(elements), &function.frame_state(*state)),
+ Insn::NewArray { elements, state } => gen_new_array(jit, asm, opnds!(elements), &function.frame_state(*state)),
Insn::NewHash { elements, state } => gen_new_hash(jit, asm, opnds!(elements), &function.frame_state(*state)),
Insn::NewRange { low, high, flag, state } => gen_new_range(jit, asm, opnd!(low), opnd!(high), *flag, &function.frame_state(*state)),
Insn::NewRangeFixnum { low, high, flag, state } => gen_new_range_fixnum(asm, opnd!(low), opnd!(high), *flag, &function.frame_state(*state)),
@@ -1139,10 +1147,9 @@ fn gen_ccall_variadic(
asm.mov(CFP, new_cfp);
asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP as i32), CFP);
- let argv_ptr = gen_push_opnds(asm, &args);
+ let argv_ptr = gen_push_opnds(jit, asm, &args);
asm.count_call_to(&name.contents_lossy());
let result = asm.ccall(cfunc, vec![args.len().into(), argv_ptr, recv]);
- gen_pop_opnds(asm, &args);
asm_comment!(asm, "pop C frame");
let new_cfp = asm.add(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
@@ -1699,7 +1706,7 @@ fn gen_invokeblock_ifunc(
gen_prepare_non_leaf_call(jit, asm, state);
// Push args to memory so we can pass argv pointer
- let argv_ptr = gen_push_opnds(asm, &args);
+ let argv_ptr = gen_push_opnds(jit, asm, &args);
// Untag the block handler to get the captured block pointer
// captured = block_handler & ~0x3
@@ -1715,9 +1722,7 @@ fn gen_invokeblock_ifunc(
argv: *const VALUE,
) -> VALUE;
}
- let result = asm_ccall!(asm, rb_vm_yield_with_cfunc, EC, captured, (args.len() as i64).into(), argv_ptr);
- gen_pop_opnds(asm, &args);
- result
+ asm_ccall!(asm, rb_vm_yield_with_cfunc, EC, captured, (args.len() as i64).into(), argv_ptr)
}
fn gen_invokeproc(
@@ -1732,9 +1737,9 @@ fn gen_invokeproc(
asm_comment!(asm, "call invokeproc");
- let argv_ptr = gen_push_opnds(asm, &args);
+ let argv_ptr = gen_push_opnds(jit, asm, &args);
let kw_splat_opnd = Opnd::Imm(i64::from(kw_splat));
- let result = asm_ccall!(
+ asm_ccall!(
asm,
rb_optimized_call,
recv,
@@ -1743,10 +1748,7 @@ fn gen_invokeproc(
argv_ptr,
kw_splat_opnd,
VM_BLOCK_HANDLER_NONE.into()
- );
- gen_pop_opnds(asm, &args);
-
- result
+ )
}
/// Compile a dynamic dispatch for `super`
@@ -1820,6 +1822,7 @@ fn gen_array_dup(
/// Compile a new array instruction
fn gen_new_array(
+ jit: &JITState,
asm: &mut Assembler,
elements: Vec<Opnd>,
state: &FrameState,
@@ -1831,10 +1834,8 @@ fn gen_new_array(
if elements.is_empty() {
asm_ccall!(asm, rb_ec_ary_new_from_values, EC, 0i64.into(), Opnd::UImm(0))
} else {
- let argv = gen_push_opnds(asm, &elements);
- let new_array = asm_ccall!(asm, rb_ec_ary_new_from_values, EC, num.into(), argv);
- gen_pop_opnds(asm, &elements);
- new_array
+ let argv = gen_push_opnds(jit, asm, &elements);
+ asm_ccall!(asm, rb_ec_ary_new_from_values, EC, num.into(), argv)
}
}
@@ -2117,10 +2118,8 @@ fn gen_new_hash(
} else {
gen_prepare_non_leaf_call(jit, asm, state);
- let argv = gen_push_opnds(asm, &elements);
- let hash = asm_ccall!(asm, rb_hash_new_with_bulk_insert, elements.len().into(), argv);
- gen_pop_opnds(asm, &elements);
- hash
+ let argv = gen_push_opnds(jit, asm, &elements);
+ asm_ccall!(asm, rb_hash_new_with_bulk_insert, elements.len().into(), argv)
}
}
@@ -2552,7 +2551,7 @@ fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, guard
asm.cmp(klass, Opnd::Value(expected_class));
asm.jne(jit, side_exit);
- } else if guard_type.is_subtype(types::TypedTData) {
+ } else if guard_type.is_subtype(types::TData) {
let side = side_exit(jit, state, GuardType(guard_type));
// Check special constant
@@ -2563,11 +2562,11 @@ fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, guard
asm.cmp(val, Qfalse.into());
asm.je(jit, side.clone());
- // Check the builtin type and RUBY_TYPED_FL_IS_TYPED_DATA with mask and compare
+ // Check the T_DATA builtin type.
let val = asm.load_mem(val);
let flags = asm.load(Opnd::mem(VALUE_BITS, val, RUBY_OFFSET_RBASIC_FLAGS));
- let mask = RUBY_T_MASK.to_usize() | RUBY_TYPED_FL_IS_TYPED_DATA.to_usize();
- let expected = RUBY_T_DATA.to_usize() | RUBY_TYPED_FL_IS_TYPED_DATA.to_usize();
+ let mask = RUBY_T_MASK.to_usize();
+ let expected = RUBY_T_DATA.to_usize();
let masked = asm.and(flags, mask.into());
asm.cmp(masked, expected.into());
asm.jne(jit, side);
@@ -2986,20 +2985,6 @@ fn build_side_exit(jit: &JITState, state: &FrameState) -> SideExit {
}
}
-/// Returne the maximum number of arguments for a block in a given function
-fn max_num_params(function: &Function) -> usize {
- let reverse_post_order = function.reverse_post_order();
- reverse_post_order
- .iter()
- .filter(|&&block_id| function.is_entry_block(block_id))
- .map(|&block_id| {
- let block = function.block(block_id);
- block.params().len()
- })
- .max()
- .unwrap_or(0)
-}
-
#[cfg(target_arch = "x86_64")]
macro_rules! c_callable {
($(#[$outer:meta])*
@@ -3362,21 +3347,18 @@ pub fn gen_exit_trampoline_with_counter(cb: &mut CodeBlock, exit_trampoline: Cod
})
}
-fn gen_push_opnds(asm: &mut Assembler, opnds: &[Opnd]) -> lir::Opnd {
- let n = opnds.len();
- let allocation_size = aligned_stack_bytes(n);
-
- // Bump the stack pointer to reserve the space for opnds
- if n != 0 {
- asm_comment!(asm, "allocate {} bytes on C stack for {} values", allocation_size, n);
- asm.sub_into(NATIVE_STACK_PTR, allocation_size.into());
+/// Reserve native stack space and write operands into it.
+fn gen_push_opnds(jit: &JITState, asm: &mut Assembler, opnds: &[Opnd]) -> lir::Opnd {
+ let argv = if opnds.len() > 0 {
+ // Make sure the Assembler will reserve a sufficient stack size for given opnds
+ asm_comment!(asm, "allocate space on C stack for {} values", opnds.len());
+ asm.alloc_stack(jit, opnds.len())
} else {
asm_comment!(asm, "no opnds to allocate");
- }
+ Opnd::UImm(0)
+ };
- // Load NATIVE_STACK_PTR to get the address of a returned array
- // to allow the backend to move it for its own use.
- let argv = asm.load(NATIVE_STACK_PTR);
+ // Write operands into stack slots allocated by asm.alloc_stack()
for (idx, &opnd) in opnds.iter().enumerate() {
asm.mov(Opnd::mem(VALUE_BITS, argv, idx as i32 * SIZEOF_VALUE_I32), opnd);
}
@@ -3384,35 +3366,18 @@ fn gen_push_opnds(asm: &mut Assembler, opnds: &[Opnd]) -> lir::Opnd {
argv
}
-fn gen_pop_opnds(asm: &mut Assembler, opnds: &[Opnd]) {
- if opnds.is_empty() {
- asm_comment!(asm, "no opnds to restore");
- return
- }
-
- asm_comment!(asm, "restore C stack pointer");
- let allocation_size = aligned_stack_bytes(opnds.len());
- asm.add_into(NATIVE_STACK_PTR, allocation_size.into());
-}
-
fn gen_toregexp(jit: &mut JITState, asm: &mut Assembler, opt: usize, values: Vec<Opnd>, state: &FrameState) -> Opnd {
gen_prepare_non_leaf_call(jit, asm, state);
- let first_opnd_ptr = gen_push_opnds(asm, &values);
- let result = asm_ccall!(asm, rb_reg_new_from_values, values.len().into(), first_opnd_ptr, opt.into());
- gen_pop_opnds(asm, &values);
-
- result
+ let first_opnd_ptr = gen_push_opnds(jit, asm, &values);
+ asm_ccall!(asm, rb_reg_new_from_values, values.len().into(), first_opnd_ptr, opt.into())
}
fn gen_string_concat(jit: &mut JITState, asm: &mut Assembler, strings: Vec<Opnd>, state: &FrameState) -> Opnd {
gen_prepare_non_leaf_call(jit, asm, state);
- let first_string_ptr = gen_push_opnds(asm, &strings);
- let result = asm_ccall!(asm, rb_str_concat_literals, strings.len().into(), first_string_ptr);
- gen_pop_opnds(asm, &strings);
-
- result
+ let first_string_ptr = gen_push_opnds(jit, asm, &strings);
+ asm_ccall!(asm, rb_str_concat_literals, strings.len().into(), first_string_ptr)
}
// Generate RSTRING_PTR
@@ -3484,6 +3449,33 @@ fn aligned_stack_bytes(num_slots: usize) -> usize {
}
impl Assembler {
+ /// Allocate stack space on top of the stack slots reserved for JITFrame,
+ /// and return a pointer to the allocated space.
+ fn alloc_stack(&mut self, jit: &JITState, stack_size: usize) -> Opnd {
+ let total_stack_size = jit.jit_frame_size + stack_size;
+ self.stack_base_idx = self.stack_base_idx.max(total_stack_size);
+ // high addr
+ // +------------------------+
+ // | return address |
+ // +------------------------+
+ // | previous frame pointer | <- NATIVE_BASE_PTR == cfp->jit_return
+ // +------------------------+
+ // | JITFrame pointer | <- jit.jit_frame_size, read by CFP_ZJIT_FRAME(cfp)
+ // +------------------------+
+ // | opnds.last() |
+ // +------------------------+
+ // | ... |
+ // +------------------------+
+ // | opnds.first() | <- pointer returned by alloc_stack()
+ // +------------------------+
+ // | register spill slots | if any
+ // +------------------------+
+ // | FrameSetup align slot | if needed
+ // +------------------------+
+ // low addr
+ self.sub(NATIVE_BASE_PTR, (SIZEOF_VALUE * total_stack_size).into())
+ }
+
/// Emits a load for memory based operands and returns a vreg,
/// otherwise returns recv.
fn load_mem(&mut self, recv: Opnd) -> Opnd {
diff --git a/zjit/src/codegen_tests.rs b/zjit/src/codegen_tests.rs
index fd6367b7a3..b099371718 100644
--- a/zjit/src/codegen_tests.rs
+++ b/zjit/src/codegen_tests.rs
@@ -26,6 +26,7 @@ fn test_breakpoint_hir_codegen() {
IseqVersion::new(iseq),
function.num_insns(),
function.num_blocks(),
+ 0,
);
let mut asm = Assembler::new();
asm.new_block_without_id("test");
diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs
index 14db7c57d7..eb3241b0a2 100644
--- a/zjit/src/cruby.rs
+++ b/zjit/src/cruby.rs
@@ -608,15 +608,13 @@ impl VALUE {
unsafe { rb_jit_class_fields_embedded_p(self) }
}
- /// Typed `T_DATA` made from `TypedData_Make_Struct()` (e.g. Thread, ARGF)
- pub fn typed_data_p(self) -> bool {
+ pub fn data_p(self) -> bool {
!self.special_const_p() &&
- self.builtin_type() == RUBY_T_DATA &&
- 0 != (self.builtin_flags() & RUBY_TYPED_FL_IS_TYPED_DATA.to_usize())
+ self.builtin_type() == RUBY_T_DATA
}
- pub fn typed_data_fields_embedded_p(self) -> bool {
- unsafe { rb_jit_typed_data_fields_embedded_p(self) }
+ pub fn data_fields_embedded_p(self) -> bool {
+ unsafe { rb_jit_data_fields_embedded_p(self) }
}
pub fn as_fixnum(self) -> i64 {
diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs
index 21a2f6bbb4..dad29087be 100644
--- a/zjit/src/cruby_bindings.inc.rs
+++ b/zjit/src/cruby_bindings.inc.rs
@@ -1929,7 +1929,7 @@ pub type zjit_struct_offsets = u32;
pub const ROBJECT_OFFSET_AS_HEAP_FIELDS: jit_bindgen_constants = 16;
pub const ROBJECT_OFFSET_AS_ARY: jit_bindgen_constants = 16;
pub const RCLASS_OFFSET_PRIME_FIELDS_OBJ: jit_bindgen_constants = 40;
-pub const RTYPEDDATA_OFFSET_FIELDS_OBJ: jit_bindgen_constants = 16;
+pub const TDATA_OFFSET_FIELDS_OBJ: jit_bindgen_constants = 16;
pub const RUBY_OFFSET_RSTRING_LEN: jit_bindgen_constants = 16;
pub const RB_SHAPE_FLAG_SHIFT: jit_bindgen_constants = 32;
pub const RUBY_OFFSET_EC_CFP: jit_bindgen_constants = 16;
@@ -2294,7 +2294,7 @@ unsafe extern "C" {
pub fn rb_jit_shape_complex_p(shape_id: shape_id_t) -> bool;
pub fn rb_jit_multi_ractor_p() -> bool;
pub fn rb_jit_class_fields_embedded_p(klass: VALUE) -> bool;
- pub fn rb_jit_typed_data_fields_embedded_p(obj: VALUE) -> bool;
+ pub fn rb_jit_data_fields_embedded_p(obj: VALUE) -> bool;
pub fn rb_jit_vm_lock_then_barrier(
recursive_lock_level: *mut ::std::os::raw::c_uint,
file: *const ::std::os::raw::c_char,
diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs
index 02ef9e0a1f..86078f4ac8 100644
--- a/zjit/src/hir.rs
+++ b/zjit/src/hir.rs
@@ -4147,7 +4147,6 @@ impl Function {
if let Some(replacement) = (props.inline)(self, tmp_block, recv, &args, state) {
// Copy contents of tmp_block to block
assert_ne!(block, tmp_block);
- emit_super_call_guards(self, block, super_cme, current_cme, mid, state);
let insns = std::mem::take(&mut self.blocks[tmp_block.0].insns);
self.blocks[block.0].insns.extend(insns);
self.count(block, Counter::inline_cfunc_optimized_send_count);
@@ -4336,8 +4335,8 @@ impl Function {
self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::Class, state })
} else if recv_type.flags().is_t_module() {
self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::Module, state })
- } else if recv_type.flags().is_typed_data() {
- self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::TypedTData, state })
+ } else if recv_type.flags().is_t_data() {
+ self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::TData, state })
} else {
// HeapBasicObject is wider than T_OBJECT, but shapes for T_OBJECTs are in a pool of
// its own and are guaranteed to be different from shapes of any other T_* types. So
@@ -4373,11 +4372,10 @@ impl Function {
});
return self.load_ivar_from_fields(block, fields_obj, recv_type.flags().is_fields_embedded(), id, ivar_index);
}
- if recv_type.flags().is_typed_data() {
- // Typed T_DATA: load from fields_obj at fixed offset in RTypedData
+ if recv_type.flags().is_t_data() {
let fields_obj = self.push_insn(block, Insn::LoadField {
recv: self_val, id: FieldName::fields_obj,
- offset: RTYPEDDATA_OFFSET_FIELDS_OBJ as i32,
+ offset: TDATA_OFFSET_FIELDS_OBJ as i32,
return_type: types::RubyValue,
});
return self.load_ivar_from_fields(block, fields_obj, recv_type.flags().is_fields_embedded(), id, ivar_index);
@@ -5318,6 +5316,14 @@ impl Function {
Insn::Test { val } if self.type_of(val).is_known_truthy() => {
self.new_insn(Insn::Const { val: Const::CBool(true) })
}
+ Insn::Test { val: test_val } => {
+ if let Insn::BoxBool { val: bool_val } = self.find(test_val) {
+ self.make_equal_to(insn_id, bool_val);
+ continue;
+ } else {
+ insn_id
+ }
+ }
Insn::CondBranch { val, if_true, .. } if self.is_a(val, Type::from_cbool(true)) => {
self.new_insn(Insn::Jump(if_true))
}
diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs
index 4a83b40e38..74c0af710e 100644
--- a/zjit/src/hir/opt_tests.rs
+++ b/zjit/src/hir/opt_tests.rs
@@ -7912,8 +7912,8 @@ mod hir_opt_tests {
}
#[test]
- fn test_optimize_getivar_on_typed_data() {
- // Thread is typed T_DATA: uses LoadField chain via RTypedData fields_obj
+ fn test_optimize_getivar_on_t_data() {
+ // T_DATA uses fields_obj for instance variables.
eval("
class C < Thread
def test = @a
@@ -7936,7 +7936,7 @@ mod hir_opt_tests {
Jump bb3(v4)
bb3(v6:BasicObject):
PatchPoint SingleRactorMode
- v17:TypedTData = GuardType v6, TypedTData
+ v17:TData = GuardType v6, TData
v18:CShape = LoadField v17, :shape_id@0x1000
v19:CShape[0x1001] = GuardBitEquals v18, CShape(0x1001) recompile
v20:RubyValue = LoadField v17, :fields_obj@0x1002
@@ -7947,8 +7947,8 @@ mod hir_opt_tests {
}
#[test]
- fn test_optimize_getivar_on_typed_data_complex_fields() {
- // Typed T_DATA with enough ivars to force heap field storage
+ fn test_optimize_getivar_on_t_data_complex_fields() {
+ // T_DATA with enough ivars to force heap field storage
eval("
class C < Thread
def test = @var1000
@@ -8260,7 +8260,7 @@ mod hir_opt_tests {
}
#[test]
- fn test_getivar_polymorphic_t_class_and_typed_data() {
+ fn test_getivar_polymorphic_t_class_and_t_data() {
set_call_threshold(3);
eval(r#"
module Reader
@@ -8295,7 +8295,7 @@ mod hir_opt_tests {
PatchPoint SingleRactorMode
v11:HeapBasicObject = GuardType v6, HeapBasicObject
v12:CUInt64 = LoadField v11, :RBASIC_FLAGS@0x1000
- v14:CUInt64[0xffffffff0000005f] = Const CUInt64(0xffffffff0000005f)
+ v14:CUInt64[0xffffffff0000001f] = Const CUInt64(0xffffffff0000001f)
v15:CPtr[CPtr(0x1001)] = Const CPtr(0x1001)
v16 = RefineType v15, CUInt64
v17:CInt64 = IntAnd v12, v14
@@ -10010,11 +10010,6 @@ mod hir_opt_tests {
v20:CallableMethodEntry[VALUE(0x1040)] = GuardBitEquals v19, Value(VALUE(0x1040))
v21:RubyValue = LoadField v18, :VM_ENV_DATA_INDEX_SPECVAL@0x1048
v22:FalseClass = GuardBitEquals v21, Value(false)
- v30:CPtr = GetEP 0
- v31:RubyValue = LoadField v30, :VM_ENV_DATA_INDEX_ME_CREF@0x1038
- v32:CallableMethodEntry[VALUE(0x1040)] = GuardBitEquals v31, Value(VALUE(0x1040))
- v33:RubyValue = LoadField v30, :VM_ENV_DATA_INDEX_SPECVAL@0x1048
- v34:FalseClass = GuardBitEquals v33, Value(false)
v23:Array = GuardType v6, Array
v24:CUInt64 = LoadField v23, :RBASIC_FLAGS@0x1049
v25:CUInt64 = GuardNoBitsSet v24, RUBY_FL_FREEZE=CUInt64(2048)
@@ -10054,11 +10049,6 @@ mod hir_opt_tests {
v25:CallableMethodEntry[VALUE(0x1048)] = GuardBitEquals v24, Value(VALUE(0x1048))
v26:RubyValue = LoadField v23, :VM_ENV_DATA_INDEX_SPECVAL@0x1050
v27:FalseClass = GuardBitEquals v26, Value(false)
- v38:CPtr = GetEP 0
- v39:RubyValue = LoadField v38, :VM_ENV_DATA_INDEX_ME_CREF@0x1040
- v40:CallableMethodEntry[VALUE(0x1048)] = GuardBitEquals v39, Value(VALUE(0x1048))
- v41:RubyValue = LoadField v38, :VM_ENV_DATA_INDEX_SPECVAL@0x1050
- v42:FalseClass = GuardBitEquals v41, Value(false)
v28:Array = GuardType v9, Array
v29:Fixnum = GuardType v10, Fixnum
v30:CInt64 = UnboxFixnum v29
@@ -16604,4 +16594,49 @@ mod hir_opt_tests {
Return v27
");
}
+
+ #[test]
+ fn test_elide_test_of_box_bool() {
+ eval(r#"
+ def test(a, b)
+ if a == b
+ 3
+ else
+ 4
+ end
+ end
+ test(:a, :b)
+ "#);
+ assert_snapshot!(hir_string("test"), @"
+ fn test@<compiled>:3:
+ bb1():
+ EntryPoint interpreter
+ v1:BasicObject = LoadSelf
+ v2:CPtr = LoadSP
+ v3:BasicObject = LoadField v2, :a@0x1000
+ v4:BasicObject = LoadField v2, :b@0x1001
+ Jump bb3(v1, v3, v4)
+ bb2():
+ EntryPoint JIT(0)
+ v7:BasicObject = LoadArg :self@0
+ v8:BasicObject = LoadArg :a@1
+ v9:BasicObject = LoadArg :b@2
+ Jump bb3(v7, v8, v9)
+ bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject):
+ PatchPoint MethodRedefined(Symbol@0x1008, ==@0x1010, cme:0x1018)
+ v48:StaticSymbol = GuardType v12, StaticSymbol
+ v49:CBool = IsBitEqual v48, v13
+ v50:BoolExact = BoxBool v49
+ CheckInterrupts
+ CondBranch v49, bb5(), bb4(v11, v48, v13)
+ bb5():
+ v29:Fixnum[3] = Const Value(3)
+ CheckInterrupts
+ Return v29
+ bb4(v34:BasicObject, v35:StaticSymbol, v36:BasicObject):
+ v40:Fixnum[4] = Const Value(4)
+ CheckInterrupts
+ Return v40
+ ");
+ }
}
diff --git a/zjit/src/hir_type/gen_hir_type.rb b/zjit/src/hir_type/gen_hir_type.rb
index 41f96a7a82..2eb5ca1932 100644
--- a/zjit/src/hir_type/gen_hir_type.rb
+++ b/zjit/src/hir_type/gen_hir_type.rb
@@ -134,10 +134,9 @@ nil_exact = final_type "NilClass", c_name: "rb_cNilClass"
true_exact = final_type "TrueClass", c_name: "rb_cTrueClass"
false_exact = final_type "FalseClass", c_name: "rb_cFalseClass"
-# Typed T_DATA objects (RTYPEDDATA_P). These have a distinct memory layout
-# for field access (fields_obj at a fixed offset in RTypedData). These
-# don't have a common class ancestor below BasicObject.
-basic_object.subtype "TypedTData"
+# T_DATA objects have a distinct memory layout for field access and don't have a
+# common class ancestor below BasicObject.
+basic_object.subtype "TData"
# Build the cvalue object universe. This is for C-level types that may be
# passed around when calling into the Ruby VM or after some strength reduction
diff --git a/zjit/src/hir_type/hir_type.inc.rs b/zjit/src/hir_type/hir_type.inc.rs
index f4c3bf3489..1dae344b36 100644
--- a/zjit/src/hir_type/hir_type.inc.rs
+++ b/zjit/src/hir_type/hir_type.inc.rs
@@ -4,7 +4,7 @@ mod bits {
pub const Array: u64 = ArrayExact | ArraySubclass;
pub const ArrayExact: u64 = 1u64 << 0;
pub const ArraySubclass: u64 = 1u64 << 1;
- pub const BasicObject: u64 = BasicObjectExact | BasicObjectSubclass | Object | TypedTData;
+ pub const BasicObject: u64 = BasicObjectExact | BasicObjectSubclass | Object | TData;
pub const BasicObjectExact: u64 = 1u64 << 2;
pub const BasicObjectSubclass: u64 = 1u64 << 3;
pub const Bignum: u64 = 1u64 << 4;
@@ -76,7 +76,7 @@ mod bits {
pub const Symbol: u64 = DynamicSymbol | StaticSymbol;
pub const TrueClass: u64 = 1u64 << 45;
pub const Truthy: u64 = BasicObject & !Falsy;
- pub const TypedTData: u64 = 1u64 << 46;
+ pub const TData: u64 = 1u64 << 46;
pub const Undef: u64 = 1u64 << 47;
pub const AllBitPatterns: [(&str, u64); 78] = [
("Any", Any),
@@ -87,7 +87,7 @@ mod bits {
("NotNil", NotNil),
("Truthy", Truthy),
("HeapBasicObject", HeapBasicObject),
- ("TypedTData", TypedTData),
+ ("TData", TData),
("Object", Object),
("BuiltinExact", BuiltinExact),
("BoolExact", BoolExact),
@@ -238,7 +238,7 @@ pub mod types {
pub const Symbol: Type = Type::from_bits(bits::Symbol);
pub const TrueClass: Type = Type::from_bits(bits::TrueClass);
pub const Truthy: Type = Type::from_bits(bits::Truthy);
- pub const TypedTData: Type = Type::from_bits(bits::TypedTData);
+ pub const TData: Type = Type::from_bits(bits::TData);
pub const Undef: Type = Type::from_bits(bits::Undef);
pub const ExactBitsAndClass: [(u64, *const VALUE); 17] = [
(bits::ObjectExact, &raw const crate::cruby::rb_cObject),
diff --git a/zjit/src/hir_type/mod.rs b/zjit/src/hir_type/mod.rs
index cdfc15a935..6aa10dea55 100644
--- a/zjit/src/hir_type/mod.rs
+++ b/zjit/src/hir_type/mod.rs
@@ -214,7 +214,7 @@ impl Type {
else if val.class_of() == unsafe { rb_cSymbol } { bits::DynamicSymbol }
else if let Some(bits) = Self::bits_from_exact_class(val.class_of()) { bits }
else if let Some(bits) = Self::bits_from_subclass(val.class_of()) { bits }
- else if val.typed_data_p() { bits::TypedTData }
+ else if val.data_p() { bits::TData }
else {
unreachable!("Class {} is not a subclass of BasicObject! Don't know what to do.",
get_class_name(val.class_of()))
@@ -445,7 +445,7 @@ impl Type {
} else if self.bit_equal(types::Hash) {
Some(cruby::RUBY_T_HASH)
} else {
- // Note that types::TypedTData is narrower than T_DATA, so not here.
+ // T_DATA uses a specialized guard, so not here.
None
}
}
diff --git a/zjit/src/profile.rs b/zjit/src/profile.rs
index 08803c4570..000c424da4 100644
--- a/zjit/src/profile.rs
+++ b/zjit/src/profile.rs
@@ -219,8 +219,8 @@ impl Flags {
const IS_T_CLASS: u32 = 1 << 6;
/// Object is a T_MODULE
const IS_T_MODULE: u32 = 1 << 7;
- /// Object is a typed T_DATA (RTYPEDDATA_P)
- const IS_TYPED_DATA: u32 = 1 << 8;
+ /// Object is a T_DATA
+ const IS_T_DATA: u32 = 1 << 8;
pub fn none() -> Self { Self(Self::NONE) }
@@ -233,7 +233,7 @@ impl Flags {
pub fn is_fields_embedded(self) -> bool { (self.0 & Self::IS_FIELDS_EMBEDDED) != 0 }
pub fn is_t_class(self) -> bool { (self.0 & Self::IS_T_CLASS) != 0 }
pub fn is_t_module(self) -> bool { (self.0 & Self::IS_T_MODULE) != 0 }
- pub fn is_typed_data(self) -> bool { (self.0 & Self::IS_TYPED_DATA) != 0 }
+ pub fn is_t_data(self) -> bool { (self.0 & Self::IS_T_DATA) != 0 }
}
/// opt_send_without_block/opt_plus/... should store:
@@ -322,9 +322,9 @@ impl ProfiledType {
flags.0 |= Flags::IS_FIELDS_EMBEDDED;
}
}
- if obj.typed_data_p() {
- flags.0 |= Flags::IS_TYPED_DATA;
- if obj.typed_data_fields_embedded_p() {
+ if obj.data_p() {
+ flags.0 |= Flags::IS_T_DATA;
+ if obj.data_fields_embedded_p() {
flags.0 |= Flags::IS_FIELDS_EMBEDDED;
}
}
@@ -365,8 +365,8 @@ impl ProfiledType {
(RUBY_T_CLASS, RUBY_T_MASK)
} else if self.flags().is_t_module() {
(RUBY_T_MODULE, RUBY_T_MASK)
- } else if self.flags().is_typed_data() {
- (RUBY_T_DATA | RUBY_TYPED_FL_IS_TYPED_DATA, RUBY_T_MASK | RUBY_TYPED_FL_IS_TYPED_DATA)
+ } else if self.flags().is_t_data() {
+ (RUBY_T_DATA, RUBY_T_MASK)
} else {
(0, 0)
};