diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index e76e140ba13f2b..6805d91406be5c 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -1191,6 +1191,30 @@ def test(x) = [1,2,3][x] }, call_threshold: 2, insns: [:opt_aref] end + def test_empty_array_pop + assert_compiles 'nil', %q{ + def test(arr) = arr.pop + test([]) + test([]) + }, call_threshold: 2 + end + + def test_array_pop_no_arg + assert_compiles '42', %q{ + def test(arr) = arr.pop + test([32, 33, 42]) + test([32, 33, 42]) + }, call_threshold: 2 + end + + def test_array_pop_arg + assert_compiles '[33, 42]', %q{ + def test(arr) = arr.pop(2) + test([32, 33, 42]) + test([32, 33, 42]) + }, call_threshold: 2 + end + def test_new_range_inclusive assert_compiles '1..5', %q{ def test(a, b) = a..b diff --git a/zjit.rb b/zjit.rb index 9ea83fe10f716f..39e353f32741ca 100644 --- a/zjit.rb +++ b/zjit.rb @@ -151,6 +151,8 @@ def stats_string buf = +"***ZJIT: Printing ZJIT statistics on exit***\n" stats = self.stats + stats[:guard_type_exit_ratio] = stats[:exit_guard_type_failure].to_f / stats[:guard_type_count] * 100 + # Show counters independent from exit_* or dynamic_send_* print_counters_with_prefix(prefix: 'not_inlined_cfuncs_', prompt: 'not inlined C methods', buf:, stats:, limit: 20) # Don't show not_annotated_cfuncs right now because it mostly duplicates not_inlined_cfuncs @@ -197,6 +199,9 @@ def stats_string :vm_write_to_parent_iseq_local_count, :vm_read_from_parent_iseq_local_count, + :guard_type_count, + :guard_type_exit_ratio, + :code_region_bytes, :side_exit_count, :total_insn_count, @@ -232,6 +237,8 @@ def print_counters(keys, buf:, stats:, right_align: false, base: nil) case key when :ratio_in_zjit value = '%0.1f%%' % value + when :guard_type_exit_ratio + value = '%0.1f%%' % value when /_time_ns\z/ key = key.to_s.sub(/_time_ns\z/, '_time') value = "#{number_with_delimiter(value / 10**6)}ms" diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index 75dbd46794abe4..76f04f4369e3da 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -124,6 +124,7 @@ fn main() { .allowlist_function("rb_ary_clear") .allowlist_function("rb_ary_dup") .allowlist_function("rb_ary_push") + .allowlist_function("rb_ary_pop") .allowlist_function("rb_ary_unshift_m") .allowlist_function("rb_ec_ary_new_from_values") .allowlist_function("rb_ary_tmp_new_from_values") @@ -331,6 +332,7 @@ fn main() { .allowlist_function("rb_class_new_instance_pass_kw") .allowlist_function("rb_obj_alloc") .allowlist_function("rb_obj_info") + .allowlist_function("rb_obj_frozen_p") // From include/ruby/debug.h .allowlist_function("rb_profile_frames") .allowlist_function("ruby_xfree") diff --git a/zjit/src/asm/x86_64/mod.rs b/zjit/src/asm/x86_64/mod.rs index 854438ad40d231..8077218c4ac720 100644 --- a/zjit/src/asm/x86_64/mod.rs +++ b/zjit/src/asm/x86_64/mod.rs @@ -509,7 +509,7 @@ fn write_rm_unary(cb: &mut CodeBlock, op_mem_reg_8: u8, op_mem_reg_pref: u8, op_ // Encode an add-like RM instruction with multiple possible encodings fn write_rm_multi(cb: &mut CodeBlock, op_mem_reg8: u8, op_mem_reg_pref: u8, op_reg_mem8: u8, op_reg_mem_pref: u8, op_mem_imm8: u8, op_mem_imm_sml: u8, op_mem_imm_lrg: u8, op_ext_imm: Option, opnd0: X86Opnd, opnd1: X86Opnd) { - assert!(matches!(opnd0, X86Opnd::Reg(_) | X86Opnd::Mem(_))); + assert!(matches!(opnd0, X86Opnd::Reg(_) | X86Opnd::Mem(_)), "unexpected opnd0: {opnd0:?}, {opnd1:?}"); // Check the size of opnd0 let opnd_size = opnd0.num_bits(); @@ -1334,7 +1334,7 @@ pub fn test(cb: &mut CodeBlock, rm_opnd: X86Opnd, test_opnd: X86Opnd) { write_rm(cb, rm_num_bits == 16, rm_num_bits == 64, test_opnd, rm_opnd, None, &[0x85]); } }, - _ => unreachable!() + _ => unreachable!("unexpected operands for test: {rm_opnd:?}, {test_opnd:?}") }; } diff --git a/zjit/src/backend/arm64/mod.rs b/zjit/src/backend/arm64/mod.rs index a6a5fc59582610..867ad493ec2821 100644 --- a/zjit/src/backend/arm64/mod.rs +++ b/zjit/src/backend/arm64/mod.rs @@ -207,7 +207,7 @@ impl Assembler { /// Return an Assembler with scratch registers disabled in the backend, and a scratch register. pub fn new_with_scratch_reg() -> (Self, Opnd) { - (Self::new_with_label_names(Vec::default(), 0, true), SCRATCH_OPND) + (Self::new_with_accept_scratch_reg(true), SCRATCH_OPND) } /// Return true if opnd contains a scratch reg @@ -386,9 +386,9 @@ impl Assembler { } } + let mut asm_local = Assembler::new_with_asm(&self); let live_ranges: Vec = take(&mut self.live_ranges); let mut iterator = self.insns.into_iter().enumerate().peekable(); - let mut asm_local = Assembler::new_with_label_names(take(&mut self.label_names), live_ranges.len(), self.accept_scratch_reg); let asm = &mut asm_local; while let Some((index, mut insn)) = iterator.next() { @@ -691,9 +691,10 @@ impl Assembler { /// VRegs, most splits should happen in [`Self::arm64_split`]. However, some instructions /// need to be split with registers after `alloc_regs`, e.g. for `compile_side_exits`, so this /// splits them and uses scratch registers for it. - fn arm64_split_with_scratch_reg(mut self) -> Assembler { + fn arm64_split_with_scratch_reg(self) -> Assembler { + let mut asm = Assembler::new_with_asm(&self); + asm.accept_scratch_reg = true; let iterator = self.insns.into_iter().enumerate().peekable(); - let mut asm = Assembler::new_with_label_names(take(&mut self.label_names), self.live_ranges.len(), true); for (_, mut insn) in iterator { match &mut insn { @@ -1664,7 +1665,7 @@ mod tests { fn test_emit_frame() { let (mut asm, mut cb) = setup_asm(); - asm.frame_setup(&[], 0); + asm.frame_setup(&[]); asm.frame_teardown(&[]); asm.compile_with_num_regs(&mut cb, 0); @@ -1683,7 +1684,8 @@ mod tests { // Test 3 preserved regs (odd), odd slot_count let cb1 = { let (mut asm, mut cb) = setup_asm(); - asm.frame_setup(THREE_REGS, 3); + asm.stack_base_idx = 3; + asm.frame_setup(THREE_REGS); asm.frame_teardown(THREE_REGS); asm.compile_with_num_regs(&mut cb, 0); cb @@ -1692,7 +1694,8 @@ mod tests { // Test 3 preserved regs (odd), even slot_count let cb2 = { let (mut asm, mut cb) = setup_asm(); - asm.frame_setup(THREE_REGS, 4); + asm.stack_base_idx = 4; + asm.frame_setup(THREE_REGS); asm.frame_teardown(THREE_REGS); asm.compile_with_num_regs(&mut cb, 0); cb @@ -1702,7 +1705,8 @@ mod tests { let cb3 = { static FOUR_REGS: &[Opnd] = &[Opnd::Reg(X19_REG), Opnd::Reg(X20_REG), Opnd::Reg(X21_REG), Opnd::Reg(X22_REG)]; let (mut asm, mut cb) = setup_asm(); - asm.frame_setup(FOUR_REGS, 3); + asm.stack_base_idx = 3; + asm.frame_setup(FOUR_REGS); asm.frame_teardown(FOUR_REGS); asm.compile_with_num_regs(&mut cb, 0); cb diff --git a/zjit/src/backend/lir.rs b/zjit/src/backend/lir.rs index 6efb3e1259e4e6..eb49b419d62046 100644 --- a/zjit/src/backend/lir.rs +++ b/zjit/src/backend/lir.rs @@ -1034,6 +1034,9 @@ impl fmt::Debug for Insn { // Print list of operands let mut opnd_iter = self.opnd_iter(); + if let Insn::FrameSetup { slot_count, .. } = self { + write!(fmt, "{slot_count}")?; + } if let Some(first_opnd) = opnd_iter.next() { write!(fmt, "{first_opnd:?}")?; } @@ -1176,32 +1179,54 @@ 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, + /// If Some, the next ccall should verify its leafness leaf_ccall_stack_size: Option } impl Assembler { - /// Create an Assembler + /// Create an Assembler with defaults pub fn new() -> Self { - Self::new_with_label_names(Vec::default(), 0, false) - } - - /// Create an Assembler with parameters that are populated by another Assembler instance. - /// This API is used for copying an Assembler for the next compiler pass. - pub fn new_with_label_names(label_names: Vec, num_vregs: usize, accept_scratch_reg: bool) -> Self { - let mut live_ranges = Vec::with_capacity(ASSEMBLER_INSNS_CAPACITY); - live_ranges.resize(num_vregs, LiveRange { start: None, end: None }); - Self { insns: Vec::with_capacity(ASSEMBLER_INSNS_CAPACITY), - live_ranges, - label_names, - accept_scratch_reg, + live_ranges: Vec::with_capacity(ASSEMBLER_INSNS_CAPACITY), + label_names: Vec::default(), + accept_scratch_reg: false, + stack_base_idx: 0, leaf_ccall_stack_size: None, } } + /// Create an Assembler, reserving a specified number of stack slots + pub fn new_with_stack_slots(stack_base_idx: usize) -> Self { + Self { stack_base_idx, ..Self::new() } + } + + /// Create an Assembler that allows the use of scratch registers. + /// This should be called only through [`Self::new_with_scratch_reg`]. + pub(super) fn new_with_accept_scratch_reg(accept_scratch_reg: bool) -> Self { + Self { accept_scratch_reg, ..Self::new() } + } + + /// Create an Assembler with parameters of another Assembler and empty instructions. + /// Compiler passes build a next Assembler with this API and insert new instructions to it. + pub(super) fn new_with_asm(old_asm: &Assembler) -> Self { + let mut asm = Self { + label_names: old_asm.label_names.clone(), + accept_scratch_reg: old_asm.accept_scratch_reg, + stack_base_idx: old_asm.stack_base_idx, + ..Self::new() + }; + // Bump the initial VReg index to allow the use of the VRegs for the old Assembler + asm.live_ranges.resize(old_asm.live_ranges.len(), LiveRange { start: None, end: None }); + asm + } + pub fn expect_leaf_ccall(&mut self, stack_size: usize) { self.leaf_ccall_stack_size = Some(stack_size); } @@ -1357,9 +1382,9 @@ impl Assembler let mut saved_regs: Vec<(Reg, usize)> = vec![]; // live_ranges is indexed by original `index` given by the iterator. + let mut asm = Assembler::new_with_asm(&self); let live_ranges: Vec = take(&mut self.live_ranges); let mut iterator = self.insns.into_iter().enumerate().peekable(); - let mut asm = Assembler::new_with_label_names(take(&mut self.label_names), live_ranges.len(), self.accept_scratch_reg); while let Some((index, mut insn)) = iterator.next() { let before_ccall = match (&insn, iterator.peek().map(|(_, insn)| insn)) { @@ -1831,7 +1856,8 @@ impl Assembler { out } - pub fn frame_setup(&mut self, preserved_regs: &'static [Opnd], slot_count: usize) { + pub fn frame_setup(&mut self, preserved_regs: &'static [Opnd]) { + let slot_count = self.stack_base_idx; self.push_insn(Insn::FrameSetup { preserved: preserved_regs, slot_count }); } diff --git a/zjit/src/backend/x86_64/mod.rs b/zjit/src/backend/x86_64/mod.rs index b6c4658463048a..bd2421823c9f3c 100644 --- a/zjit/src/backend/x86_64/mod.rs +++ b/zjit/src/backend/x86_64/mod.rs @@ -69,7 +69,7 @@ impl From for X86Opnd { "Attempted to lower an Opnd::None. This often happens when an out operand was not allocated for an instruction because the output of the instruction was not used. Please ensure you are using the output." ), - _ => panic!("unsupported x86 operand type") + _ => panic!("unsupported x86 operand type: {opnd:?}") } } } @@ -102,7 +102,7 @@ const SCRATCH_OPND: Opnd = Opnd::Reg(R11_REG); impl Assembler { /// Return an Assembler with scratch registers disabled in the backend, and a scratch register. pub fn new_with_scratch_reg() -> (Self, Opnd) { - (Self::new_with_label_names(Vec::default(), 0, true), SCRATCH_OPND) + (Self::new_with_accept_scratch_reg(true), SCRATCH_OPND) } /// Return true if opnd contains a scratch reg @@ -137,9 +137,9 @@ impl Assembler { /// Split IR instructions for the x86 platform fn x86_split(mut self) -> Assembler { + let mut asm = Assembler::new_with_asm(&self); let live_ranges: Vec = take(&mut self.live_ranges); let mut iterator = self.insns.into_iter().enumerate().peekable(); - let mut asm = Assembler::new_with_label_names(take(&mut self.label_names), live_ranges.len(), self.accept_scratch_reg); while let Some((index, mut insn)) = iterator.next() { let is_load = matches!(insn, Insn::Load { .. } | Insn::LoadInto { .. }); @@ -390,7 +390,7 @@ impl Assembler { /// for VRegs, most splits should happen in [`Self::x86_split`]. However, some instructions /// need to be split with registers after `alloc_regs`, e.g. for `compile_side_exits`, so /// this splits them and uses scratch registers for it. - pub fn x86_split_with_scratch_reg(mut self) -> Assembler { + pub fn x86_split_with_scratch_reg(self) -> Assembler { /// For some instructions, we want to be able to lower a 64-bit operand /// without requiring more registers to be available in the register /// allocator. So we just use the SCRATCH_OPND register temporarily to hold @@ -419,8 +419,9 @@ impl Assembler { } } + let mut asm = Assembler::new_with_asm(&self); + asm.accept_scratch_reg = true; let mut iterator = self.insns.into_iter().enumerate().peekable(); - let mut asm = Assembler::new_with_label_names(take(&mut self.label_names), self.live_ranges.len(), true); while let Some((_, mut insn)) = iterator.next() { match &mut insn { @@ -1551,38 +1552,46 @@ mod tests { } #[test] - fn frame_setup_teardown() { + fn frame_setup_teardown_preserved_regs() { let (mut asm, mut cb) = setup_asm(); - asm.frame_setup(JIT_PRESERVED_REGS, 0); + asm.frame_setup(JIT_PRESERVED_REGS); asm.frame_teardown(JIT_PRESERVED_REGS); - asm.cret(C_RET_OPND); + asm.compile_with_num_regs(&mut cb, 0); - asm.frame_setup(&[], 5); - asm.frame_teardown(&[]); + assert_disasm_snapshot!(cb.disasm(), @r" + 0x0: push rbp + 0x1: mov rbp, rsp + 0x4: push r13 + 0x6: push rbx + 0x7: push r12 + 0x9: sub rsp, 8 + 0xd: mov r13, qword ptr [rbp - 8] + 0x11: mov rbx, qword ptr [rbp - 0x10] + 0x15: mov r12, qword ptr [rbp - 0x18] + 0x19: mov rsp, rbp + 0x1c: pop rbp + 0x1d: ret + "); + assert_snapshot!(cb.hexdump(), @"554889e541555341544883ec084c8b6df8488b5df04c8b65e84889ec5dc3"); + } + #[test] + fn frame_setup_teardown_stack_base_idx() { + let (mut asm, mut cb) = setup_asm(); + asm.stack_base_idx = 5; + asm.frame_setup(&[]); + asm.frame_teardown(&[]); asm.compile_with_num_regs(&mut cb, 0); - assert_disasm_snapshot!(cb.disasm(), @" - 0x0: push rbp - 0x1: mov rbp, rsp - 0x4: push r13 - 0x6: push rbx - 0x7: push r12 - 0x9: sub rsp, 8 - 0xd: mov r13, qword ptr [rbp - 8] - 0x11: mov rbx, qword ptr [rbp - 0x10] - 0x15: mov r12, qword ptr [rbp - 0x18] - 0x19: mov rsp, rbp - 0x1c: pop rbp - 0x1d: ret - 0x1e: push rbp - 0x1f: mov rbp, rsp - 0x22: sub rsp, 0x30 - 0x26: mov rsp, rbp - 0x29: pop rbp + assert_disasm_snapshot!(cb.disasm(), @r" + 0x0: push rbp + 0x1: mov rbp, rsp + 0x4: sub rsp, 0x30 + 0x8: mov rsp, rbp + 0xb: pop rbp "); - assert_snapshot!(cb.hexdump(), @"554889e541555341544883ec084c8b6df8488b5df04c8b65e84889ec5dc3554889e54883ec304889ec5d"); + assert_snapshot!(cb.hexdump(), @"554889e54883ec304889ec5d"); } #[test] diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 1179b9bf16924b..2b71be0e15ba71 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -41,21 +41,17 @@ struct JITState { /// ISEQ calls that need to be compiled later iseq_calls: Vec, - - /// The number of bytes allocated for basic block arguments spilled onto the C stack - c_stack_slots: usize, } impl JITState { /// Create a new JITState instance - fn new(iseq: IseqPtr, num_insns: usize, num_blocks: usize, c_stack_slots: usize) -> Self { + fn new(iseq: IseqPtr, num_insns: usize, num_blocks: usize) -> Self { JITState { iseq, opnds: vec![None; num_insns], labels: vec![None; num_blocks], jit_entries: Vec::default(), iseq_calls: Vec::default(), - c_stack_slots, } } @@ -246,9 +242,9 @@ fn gen_iseq_body(cb: &mut CodeBlock, iseq: IseqPtr, function: Option<&Function>, /// Compile a function fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function) -> Result<(IseqCodePtrs, Vec, Vec), CompileError> { - let c_stack_slots = max_num_params(function).saturating_sub(ALLOC_REGS.len()); - let mut jit = JITState::new(iseq, function.num_insns(), function.num_blocks(), c_stack_slots); - let mut asm = Assembler::new(); + let num_spilled_params = max_num_params(function).saturating_sub(ALLOC_REGS.len()); + let mut jit = JITState::new(iseq, function.num_insns(), function.num_blocks()); + let mut asm = Assembler::new_with_stack_slots(num_spilled_params); // Compile each basic block let reverse_post_order = function.rpo(); @@ -357,6 +353,7 @@ 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::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)), &Insn::ObjectAllocClass { class, state } => gen_object_alloc_class(asm, class, &function.frame_state(state)), @@ -405,11 +402,14 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::IsNil { val } => gen_isnil(asm, opnd!(val)), &Insn::IsMethodCfunc { val, cd, cfunc, state: _ } => gen_is_method_cfunc(jit, asm, opnd!(val), cd, cfunc), &Insn::IsBitEqual { left, right } => gen_is_bit_equal(asm, opnd!(left), opnd!(right)), + &Insn::IsBitNotEqual { left, right } => gen_is_bit_not_equal(asm, opnd!(left), opnd!(right)), + &Insn::BoxBool { val } => gen_box_bool(asm, opnd!(val)), 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::GuardBlockParamProxy { level, state } => no_output!(gen_guard_block_param_proxy(jit, asm, level, &function.frame_state(state))), + Insn::GuardNotFrozen { val, state } => gen_guard_not_frozen(jit, asm, opnd!(val), &function.frame_state(*state)), Insn::PatchPoint { invariant, state } => no_output!(gen_patch_point(jit, asm, invariant, &function.frame_state(*state))), Insn::CCall { cfunc, args, name: _, return_type: _, elidable: _ } => gen_ccall(asm, *cfunc, opnds!(args)), // Give up CCallWithFrame for 7+ args since asm.ccall() doesn't support it. @@ -628,6 +628,14 @@ fn gen_guard_block_param_proxy(jit: &JITState, asm: &mut Assembler, level: u32, asm.jz(side_exit(jit, state, SideExitReason::BlockParamProxyNotIseqOrIfunc)); } +fn gen_guard_not_frozen(jit: &JITState, asm: &mut Assembler, val: Opnd, state: &FrameState) -> Opnd { + let ret = asm_ccall!(asm, rb_obj_frozen_p, val); + asm_comment!(asm, "side-exit if rb_obj_frozen_p returns Qtrue"); + asm.cmp(ret, Qtrue.into()); + asm.je(side_exit(jit, state, GuardNotFrozen)); + val +} + fn gen_get_constant_path(jit: &JITState, asm: &mut Assembler, ic: *const iseq_inline_constant_cache, state: &FrameState) -> Opnd { unsafe extern "C" { fn rb_vm_opt_getconstant_path(ec: EcPtr, cfp: CfpPtr, ic: *const iseq_inline_constant_cache) -> VALUE; @@ -802,7 +810,7 @@ fn gen_ccall_variadic( asm.mov(CFP, new_cfp); asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP); - let argv_ptr = gen_push_opnds(jit, asm, &args); + let argv_ptr = gen_push_opnds(asm, &args); let result = asm.ccall(cfunc, vec![args.len().into(), argv_ptr, recv]); gen_pop_opnds(asm, &args); @@ -999,7 +1007,7 @@ fn gen_load_ivar_extended(asm: &mut Assembler, self_val: Opnd, id: ID, index: u1 fn gen_entry_prologue(asm: &mut Assembler, iseq: IseqPtr) { asm_comment!(asm, "ZJIT entry point: {}", iseq_get_location(iseq, 0)); // Save the registers we'll use for CFP, EP, SP - asm.frame_setup(lir::JIT_PRESERVED_REGS, 0); + asm.frame_setup(lir::JIT_PRESERVED_REGS); // EC and CFP are passed as arguments asm.mov(EC, C_ARG_OPNDS[0]); @@ -1338,6 +1346,11 @@ fn gen_aref_fixnum( asm_ccall!(asm, rb_ary_entry, array, unboxed_idx) } +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) +} + fn gen_array_length(asm: &mut Assembler, array: Opnd) -> lir::Opnd { asm_ccall!(asm, rb_jit_array_len, array) } @@ -1355,7 +1368,7 @@ fn gen_new_hash( let new_hash = asm_ccall!(asm, rb_hash_new_with_size, lir::Opnd::Imm(cap)); if !elements.is_empty() { - let argv = gen_push_opnds(jit, asm, &elements); + let argv = gen_push_opnds(asm, &elements); asm_ccall!(asm, rb_hash_bulk_insert, elements.len().into(), argv, new_hash); gen_pop_opnds(asm, &elements); @@ -1422,7 +1435,7 @@ fn gen_entry_point(jit: &mut JITState, asm: &mut Assembler, jit_entry_idx: Optio jit_entry.borrow_mut().start_addr.set(Some(code_ptr)); }); } - asm.frame_setup(&[], jit.c_stack_slots); + asm.frame_setup(&[]); } /// Compile code that exits from JIT code with a return value @@ -1553,6 +1566,16 @@ fn gen_is_bit_equal(asm: &mut Assembler, left: lir::Opnd, right: lir::Opnd) -> l asm.csel_e(Opnd::Imm(1), Opnd::Imm(0)) } +fn gen_is_bit_not_equal(asm: &mut Assembler, left: lir::Opnd, right: lir::Opnd) -> lir::Opnd { + asm.cmp(left, right); + asm.csel_ne(Opnd::Imm(1), Opnd::Imm(0)) +} + +fn gen_box_bool(asm: &mut Assembler, val: lir::Opnd) -> lir::Opnd { + asm.test(val, val); + asm.csel_nz(Opnd::Value(Qtrue), Opnd::Value(Qfalse)) +} + fn gen_anytostring(asm: &mut Assembler, val: lir::Opnd, str: lir::Opnd, state: &FrameState) -> lir::Opnd { gen_prepare_leaf_call_with_gc(asm, state); @@ -1572,6 +1595,7 @@ fn gen_test(asm: &mut Assembler, val: lir::Opnd) -> lir::Opnd { /// Compile a type check with a side exit fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, guard_type: Type, state: &FrameState) -> lir::Opnd { + gen_incr_counter(asm, Counter::guard_type_count); if guard_type.is_subtype(types::Fixnum) { asm.test(val, Opnd::UImm(RUBY_FIXNUM_FLAG as u64)); asm.jz(side_exit(jit, state, GuardType(guard_type))); @@ -1883,6 +1907,8 @@ fn param_opnd(idx: usize) -> Opnd { if idx < ALLOC_REGS.len() { Opnd::Reg(ALLOC_REGS[idx]) } else { + // With FrameSetup, the address that NATIVE_BASE_PTR points to stores an old value in the register. + // To avoid clobbering it, we need to start from the next slot, hence `+ 1` for the index. Opnd::mem(64, NATIVE_BASE_PTR, (idx - ALLOC_REGS.len() + 1) as i32 * -SIZEOF_VALUE_I32) } } @@ -2094,19 +2120,24 @@ pub fn gen_function_stub_hit_trampoline(cb: &mut CodeBlock) -> Result lir::Opnd { +fn gen_push_opnds(asm: &mut Assembler, opnds: &[Opnd]) -> lir::Opnd { let n = opnds.len(); - - // Calculate the compile-time NATIVE_STACK_PTR offset from NATIVE_BASE_PTR - // At this point, frame_setup(&[], jit.c_stack_slots) has been called, - // which allocated aligned_stack_bytes(jit.c_stack_slots) on the stack - let frame_size = aligned_stack_bytes(jit.c_stack_slots); 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()); @@ -2167,18 +2194,14 @@ fn gen_push_opnds(jit: &mut JITState, asm: &mut Assembler, opnds: &[Opnd]) -> li asm_comment!(asm, "no opnds to allocate"); } - // Calculate the total offset from NATIVE_BASE_PTR to our buffer - let total_offset_from_base = (frame_size + allocation_size) as i32; - + // 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); for (idx, &opnd) in opnds.iter().enumerate() { - let slot_offset = -total_offset_from_base + (idx as i32 * SIZEOF_VALUE_I32); - asm.mov( - Opnd::mem(VALUE_BITS, NATIVE_BASE_PTR, slot_offset), - opnd - ); + asm.mov(Opnd::mem(VALUE_BITS, argv, idx as i32 * SIZEOF_VALUE_I32), opnd); } - asm.lea(Opnd::mem(64, NATIVE_BASE_PTR, -total_offset_from_base)) + argv } fn gen_pop_opnds(asm: &mut Assembler, opnds: &[Opnd]) { @@ -2195,7 +2218,7 @@ fn gen_pop_opnds(asm: &mut Assembler, opnds: &[Opnd]) { fn gen_toregexp(jit: &mut JITState, asm: &mut Assembler, opt: usize, values: Vec, state: &FrameState) -> Opnd { gen_prepare_non_leaf_call(jit, asm, state); - let first_opnd_ptr = gen_push_opnds(jit, asm, &values); + let first_opnd_ptr = gen_push_opnds(asm, &values); let tmp_ary = asm_ccall!(asm, rb_ary_tmp_new_from_values, Opnd::Imm(0), values.len().into(), first_opnd_ptr); let result = asm_ccall!(asm, rb_reg_new_ary, tmp_ary, opt.into()); @@ -2209,7 +2232,7 @@ fn gen_toregexp(jit: &mut JITState, asm: &mut Assembler, opt: usize, values: Vec fn gen_string_concat(jit: &mut JITState, asm: &mut Assembler, strings: Vec, state: &FrameState) -> Opnd { gen_prepare_non_leaf_call(jit, asm, state); - let first_string_ptr = gen_push_opnds(jit, asm, &strings); + 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); diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index f7e6cdde9419b3..eb8ffcd1580e8c 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -1158,6 +1158,7 @@ unsafe extern "C" { pub fn rb_ary_resurrect(ary: VALUE) -> VALUE; pub fn rb_ary_cat(ary: VALUE, train: *const VALUE, len: ::std::os::raw::c_long) -> VALUE; pub fn rb_ary_push(ary: VALUE, elem: VALUE) -> VALUE; + pub fn rb_ary_pop(ary: VALUE) -> VALUE; pub fn rb_ary_entry(ary: VALUE, off: ::std::os::raw::c_long) -> VALUE; pub fn rb_ary_clear(ary: VALUE) -> VALUE; pub fn rb_ary_concat(lhs: VALUE, rhs: VALUE) -> VALUE; diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index dd33bb206a5bb3..12d226ce51bea2 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -156,12 +156,22 @@ pub fn init() -> Annotations { macro_rules! annotate { ($module:ident, $method_name:literal, $inline:ident) => { - let props = FnProperties { no_gc: false, leaf: false, elidable: false, return_type: types::BasicObject, inline: $inline }; + let mut props = FnProperties::default(); + props.inline = $inline; + annotate_c_method(cfuncs, unsafe { $module }, $method_name, props); + }; + ($module:ident, $method_name:literal, $inline:ident, $return_type:expr $(, $properties:ident)*) => { + let mut props = FnProperties::default(); + props.return_type = $return_type; + props.inline = $inline; + $( + props.$properties = true; + )* annotate_c_method(cfuncs, unsafe { $module }, $method_name, props); }; ($module:ident, $method_name:literal, $return_type:expr $(, $properties:ident)*) => { - #[allow(unused_mut)] - let mut props = FnProperties { no_gc: false, leaf: false, elidable: false, return_type: $return_type, inline: no_inline }; + let mut props = FnProperties::default(); + props.return_type = $return_type; $( props.$properties = true; )* @@ -175,13 +185,8 @@ pub fn init() -> Annotations { annotate_builtin!($module, $method_name, $return_type, no_gc, leaf, elidable) }; ($module:ident, $method_name:literal, $return_type:expr, $($properties:ident),+) => { - let mut props = FnProperties { - no_gc: false, - leaf: false, - elidable: false, - return_type: $return_type, - inline: no_inline, - }; + let mut props = FnProperties::default(); + props.return_type = $return_type; $(props.$properties = true;)+ annotate_builtin_method(builtin_funcs, unsafe { $module }, $method_name, props); } @@ -189,10 +194,10 @@ pub fn init() -> Annotations { annotate!(rb_mKernel, "itself", inline_kernel_itself); annotate!(rb_mKernel, "block_given?", inline_kernel_block_given_p); + annotate!(rb_mKernel, "===", inline_eqq); annotate!(rb_cString, "bytesize", types::Fixnum, no_gc, leaf, elidable); annotate!(rb_cString, "size", types::Fixnum, no_gc, leaf, elidable); annotate!(rb_cString, "length", types::Fixnum, no_gc, leaf, elidable); - annotate!(rb_cString, "to_s", types::StringExact); annotate!(rb_cString, "getbyte", inline_string_getbyte); annotate!(rb_cString, "empty?", types::BoolExact, no_gc, leaf, elidable); annotate!(rb_cString, "<<", inline_string_append); @@ -207,18 +212,20 @@ pub fn init() -> Annotations { annotate!(rb_cArray, "[]", inline_array_aref); annotate!(rb_cArray, "<<", inline_array_push); annotate!(rb_cArray, "push", inline_array_push); + annotate!(rb_cArray, "pop", inline_array_pop); annotate!(rb_cHash, "[]", inline_hash_aref); annotate!(rb_cHash, "size", types::Fixnum, no_gc, leaf, elidable); annotate!(rb_cHash, "empty?", types::BoolExact, no_gc, leaf, elidable); - annotate!(rb_cNilClass, "nil?", types::TrueClass, no_gc, leaf, elidable); - annotate!(rb_mKernel, "nil?", types::FalseClass, no_gc, leaf, elidable); + annotate!(rb_cNilClass, "nil?", inline_nilclass_nil_p); + annotate!(rb_mKernel, "nil?", inline_kernel_nil_p); annotate!(rb_mKernel, "respond_to?", inline_kernel_respond_to_p); - annotate!(rb_cBasicObject, "==", types::BoolExact, no_gc, leaf, elidable); + annotate!(rb_cBasicObject, "==", inline_basic_object_eq, types::BoolExact, no_gc, leaf, elidable); annotate!(rb_cBasicObject, "!", types::BoolExact, no_gc, leaf, elidable); + annotate!(rb_cBasicObject, "!=", inline_basic_object_neq, types::BoolExact, no_gc, leaf, elidable); annotate!(rb_cBasicObject, "initialize", inline_basic_object_initialize); annotate!(rb_cInteger, "succ", inline_integer_succ); annotate!(rb_cInteger, "^", inline_integer_xor); - annotate!(rb_cString, "to_s", inline_string_to_s); + annotate!(rb_cString, "to_s", inline_string_to_s, types::StringExact); let thread_singleton = unsafe { rb_singleton_class(rb_cThread) }; annotate!(thread_singleton, "current", types::BasicObject, no_gc, leaf); @@ -281,6 +288,13 @@ fn inline_array_push(fun: &mut hir::Function, block: hir::BlockId, recv: hir::In None } +fn inline_array_pop(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { + // Only inline the case of no arguments. + let &[] = args else { return None; }; + let arr = fun.push_insn(block, hir::Insn::GuardNotFrozen { val: recv, state }); + Some(fun.push_insn(block, hir::Insn::ArrayPop { array: arr, state })) +} + fn inline_hash_aref(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { if let &[key] = args { let result = fun.push_insn(block, hir::Insn::HashAref { hash: recv, key, state }); @@ -357,12 +371,51 @@ fn inline_integer_xor(fun: &mut hir::Function, block: hir::BlockId, recv: hir::I None } +fn inline_basic_object_eq(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], _state: hir::InsnId) -> Option { + let &[other] = args else { return None; }; + let c_result = fun.push_insn(block, hir::Insn::IsBitEqual { left: recv, right: other }); + let result = fun.push_insn(block, hir::Insn::BoxBool { val: c_result }); + Some(result) +} + +fn inline_basic_object_neq(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { + let &[other] = args else { return None; }; + let recv_class = fun.type_of(recv).runtime_exact_ruby_class()?; + if !fun.assume_expected_cfunc(block, recv_class, ID!(eq), rb_obj_equal as _, state) { + return None; + } + let c_result = fun.push_insn(block, hir::Insn::IsBitNotEqual { left: recv, right: other }); + let result = fun.push_insn(block, hir::Insn::BoxBool { val: c_result }); + Some(result) +} + fn inline_basic_object_initialize(fun: &mut hir::Function, block: hir::BlockId, _recv: hir::InsnId, args: &[hir::InsnId], _state: hir::InsnId) -> Option { if !args.is_empty() { return None; } let result = fun.push_insn(block, hir::Insn::Const { val: hir::Const::Value(Qnil) }); Some(result) } +fn inline_nilclass_nil_p(fun: &mut hir::Function, block: hir::BlockId, _recv: hir::InsnId, args: &[hir::InsnId], _state: hir::InsnId) -> Option { + if !args.is_empty() { return None; } + Some(fun.push_insn(block, hir::Insn::Const { val: hir::Const::Value(Qtrue) })) +} + +fn inline_eqq(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { + let &[other] = args else { return None; }; + let recv_class = fun.type_of(recv).runtime_exact_ruby_class()?; + if !fun.assume_expected_cfunc(block, recv_class, ID!(eq), rb_obj_equal as _, state) { + return None; + } + let c_result = fun.push_insn(block, hir::Insn::IsBitEqual { left: recv, right: other }); + let result = fun.push_insn(block, hir::Insn::BoxBool { val: c_result }); + Some(result) +} + +fn inline_kernel_nil_p(fun: &mut hir::Function, block: hir::BlockId, _recv: hir::InsnId, args: &[hir::InsnId], _state: hir::InsnId) -> Option { + if !args.is_empty() { return None; } + Some(fun.push_insn(block, hir::Insn::Const { val: hir::Const::Value(Qfalse) })) +} + fn inline_kernel_respond_to_p( fun: &mut hir::Function, block: hir::BlockId, diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index e0e682ccb2fc71..3f68764722b448 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -17,6 +17,9 @@ use crate::profile::{TypeDistributionSummary, ProfiledType}; use crate::stats::Counter; use SendFallbackReason::*; +mod tests; +mod opt_tests; + /// An index of an [`Insn`] in a [`Function`]. This is a popular /// type since this effectively acts as a pointer to an [`Insn`]. /// See also: [`Function::find`]. @@ -461,6 +464,7 @@ pub enum SideExitReason { GuardTypeNot(Type), GuardShape(ShapeId), GuardBitEquals(Const), + GuardNotFrozen, PatchPoint(Invariant), CalleeSideExit, ObjToStringFallback, @@ -585,6 +589,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 }, + ArrayPop { array: InsnId, state: InsnId }, /// Return the length of the array as a C `long` ([`types::CInt64`]) ArrayLength { array: InsnId }, @@ -610,6 +615,10 @@ pub enum Insn { IsMethodCfunc { val: InsnId, cd: *const rb_call_data, cfunc: *const u8, state: InsnId }, /// Return C `true` if left == right IsBitEqual { left: InsnId, right: InsnId }, + /// Return C `true` if left != right + IsBitNotEqual { left: InsnId, right: InsnId }, + /// Convert a C `bool` to a Ruby `Qtrue`/`Qfalse`. Same as `RBOOL` macro. + BoxBool { val: InsnId }, // TODO(max): In iseq body types that are not ISEQ_TYPE_METHOD, rewrite to Constant false. Defined { op_type: usize, obj: VALUE, pushval: VALUE, v: InsnId, state: InsnId }, GetConstantPath { ic: *const iseq_inline_constant_cache, state: InsnId }, @@ -791,6 +800,8 @@ pub enum Insn { /// Side-exit if the block param has been modified or the block handler for the frame /// is neither ISEQ nor ifunc, which makes it incompatible with rb_block_param_proxy. GuardBlockParamProxy { level: u32, state: InsnId }, + /// Side-exit if val is frozen. + GuardNotFrozen { val: InsnId, state: InsnId }, /// Generate no code (or padding if necessary) and insert a patch point /// that can be rewritten to a side exit when the Invariant is broken. @@ -917,6 +928,9 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::ArrayArefFixnum { array, index, .. } => { write!(f, "ArrayArefFixnum {array}, {index}") } + Insn::ArrayPop { array, .. } => { + write!(f, "ArrayPop {array}") + } Insn::ArrayLength { array } => { write!(f, "ArrayLength {array}") } @@ -997,6 +1011,8 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::IsNil { val } => { write!(f, "IsNil {val}") } Insn::IsMethodCfunc { val, cd, .. } => { write!(f, "IsMethodCFunc {val}, :{}", ruby_call_method_name(*cd)) } Insn::IsBitEqual { left, right } => write!(f, "IsBitEqual {left}, {right}"), + Insn::IsBitNotEqual { left, right } => write!(f, "IsBitNotEqual {left}, {right}"), + Insn::BoxBool { val } => write!(f, "BoxBool {val}"), Insn::Jump(target) => { write!(f, "Jump {target}") } Insn::IfTrue { val, target } => { write!(f, "IfTrue {val}, {target}") } Insn::IfFalse { val, target } => { write!(f, "IfFalse {val}, {target}") } @@ -1076,6 +1092,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::GuardBitEquals { val, expected, .. } => { write!(f, "GuardBitEquals {val}, {}", expected.print(self.ptr_map)) }, &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 { val, .. } => write!(f, "GuardNotFrozen {val}"), Insn::PatchPoint { invariant, .. } => { write!(f, "PatchPoint {}", invariant.print(self.ptr_map)) }, Insn::GetConstantPath { ic, .. } => { write!(f, "GetConstantPath {:p}", self.ptr_map.map_ptr(ic)) }, Insn::IsBlockGiven => { write!(f, "IsBlockGiven") }, @@ -1623,6 +1640,8 @@ impl Function { &IsNil { val } => IsNil { val: find!(val) }, &IsMethodCfunc { val, cd, cfunc, state } => IsMethodCfunc { val: find!(val), cd, cfunc, state }, &IsBitEqual { left, right } => IsBitEqual { left: find!(left), right: find!(right) }, + &IsBitNotEqual { left, right } => IsBitNotEqual { left: find!(left), right: find!(right) }, + &BoxBool { val } => BoxBool { val: find!(val) }, Jump(target) => Jump(find_branch_edge!(target)), &IfTrue { val, ref target } => IfTrue { val: find!(val), target: find_branch_edge!(target) }, &IfFalse { val, ref target } => IfFalse { val: find!(val), target: find_branch_edge!(target) }, @@ -1631,6 +1650,7 @@ impl Function { &GuardBitEquals { val, expected, state } => GuardBitEquals { val: find!(val), expected, state }, &GuardShape { val, shape, state } => GuardShape { val: find!(val), shape, state }, &GuardBlockParamProxy { level, state } => GuardBlockParamProxy { level, state: find!(state) }, + &GuardNotFrozen { val, state } => GuardNotFrozen { val: find!(val), state }, &FixnumAdd { left, right, state } => FixnumAdd { left: find!(left), right: find!(right), state }, &FixnumSub { left, right, state } => FixnumSub { left: find!(left), right: find!(right), state }, &FixnumMult { left, right, state } => FixnumMult { left: find!(left), right: find!(right), state }, @@ -1728,6 +1748,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) }, + &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) }, &SetGlobal { id, val, state } => SetGlobal { id, val: find!(val), state }, @@ -1810,6 +1831,8 @@ impl Function { Insn::IsNil { .. } => types::CBool, Insn::IsMethodCfunc { .. } => types::CBool, Insn::IsBitEqual { .. } => types::CBool, + Insn::IsBitNotEqual { .. } => types::CBool, + Insn::BoxBool { .. } => types::BoolExact, Insn::StringCopy { .. } => types::StringExact, Insn::StringIntern { .. } => types::Symbol, Insn::StringConcat { .. } => types::StringExact, @@ -1819,6 +1842,7 @@ impl Function { Insn::NewArray { .. } => types::ArrayExact, Insn::ArrayDup { .. } => types::ArrayExact, Insn::ArrayArefFixnum { .. } => types::BasicObject, + Insn::ArrayPop { .. } => types::BasicObject, Insn::ArrayLength { .. } => types::CInt64, Insn::HashAref { .. } => types::BasicObject, Insn::NewHash { .. } => types::HashExact, @@ -1834,6 +1858,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 { val, .. } => self.type_of(*val), Insn::FixnumAdd { .. } => types::Fixnum, Insn::FixnumSub { .. } => types::Fixnum, Insn::FixnumMult { .. } => types::Fixnum, @@ -2033,6 +2058,21 @@ impl Function { None } + pub fn assume_expected_cfunc(&mut self, block: BlockId, class: VALUE, method_id: ID, cfunc: *mut c_void, state: InsnId) -> bool { + let cme = unsafe { rb_callable_method_entry(class, method_id) }; + if cme.is_null() { return false; } + let def_type = unsafe { get_cme_def_type(cme) }; + if def_type != VM_METHOD_TYPE_CFUNC { return false; } + if unsafe { get_mct_func(get_cme_def_body_cfunc(cme)) } != cfunc { + return false; + } + self.gen_patch_points_for_optimized_ccall(block, class, method_id, cme, state); + if class.instance_can_have_singleton_class() { + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: class }, state }); + } + true + } + pub fn likely_a(&self, val: InsnId, ty: Type, state: InsnId) -> bool { if self.type_of(val).is_subtype(ty) { return true; @@ -2541,6 +2581,11 @@ impl Function { self.infer_types(); } + fn gen_patch_points_for_optimized_ccall(&mut self, block: BlockId, recv_class: VALUE, method_id: ID, method: *const rb_callable_method_entry_struct, state: InsnId) { + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoTracePoint, state }); + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass: recv_class, method: method_id, cme: method }, state }); + } + /// Optimize SendWithoutBlock that land in a C method to a direct CCall without /// runtime lookup. fn optimize_c_calls(&mut self) { @@ -2548,11 +2593,6 @@ impl Function { return; } - fn gen_patch_points_for_optimized_ccall(fun: &mut Function, block: BlockId, recv_class: VALUE, method_id: ID, method: *const rb_callable_method_entry_struct, state: InsnId) { - fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoTracePoint, state }); - fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass: recv_class, method: method_id, cme: method }, state }); - } - // Try to reduce a Send insn to a CCallWithFrame fn reduce_send_to_ccall( fun: &mut Function, @@ -2611,7 +2651,7 @@ impl Function { } // Commit to the replacement. Put PatchPoint. - gen_patch_points_for_optimized_ccall(fun, block, recv_class, method_id, method, state); + fun.gen_patch_points_for_optimized_ccall(block, recv_class, method_id, method, state); if recv_class.instance_can_have_singleton_class() { fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_class }, state }); } @@ -2713,7 +2753,7 @@ impl Function { } // Commit to the replacement. Put PatchPoint. - gen_patch_points_for_optimized_ccall(fun, block, recv_class, method_id, method, state); + fun.gen_patch_points_for_optimized_ccall(block, recv_class, method_id, method, state); if recv_class.instance_can_have_singleton_class() { fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_class }, state }); } @@ -2780,7 +2820,7 @@ impl Function { // func(int argc, VALUE *argv, VALUE recv) let ci_flags = unsafe { vm_ci_flag(call_info) }; if ci_flags & VM_CALL_ARGS_SIMPLE != 0 { - gen_patch_points_for_optimized_ccall(fun, block, recv_class, method_id, method, state); + fun.gen_patch_points_for_optimized_ccall(block, recv_class, method_id, method, state); if recv_class.instance_can_have_singleton_class() { fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_class }, state }); @@ -3094,6 +3134,7 @@ impl Function { | &Insn::Return { val } | &Insn::Test { val } | &Insn::SetLocal { val, .. } + | &Insn::BoxBool { val } | &Insn::IsNil { val } => worklist.push_back(val), &Insn::SetGlobal { val, state, .. } @@ -3105,6 +3146,7 @@ impl Function { | &Insn::GuardTypeNot { val, state, .. } | &Insn::GuardBitEquals { val, state, .. } | &Insn::GuardShape { val, state, .. } + | &Insn::GuardNotFrozen { val, state } | &Insn::ToArray { val, state } | &Insn::IsMethodCfunc { val, state, .. } | &Insn::ToNewArray { val, state } => { @@ -3136,6 +3178,7 @@ impl Function { | &Insn::FixnumOr { left, right } | &Insn::FixnumXor { left, right } | &Insn::IsBitEqual { left, right } + | &Insn::IsBitNotEqual { left, right } => { worklist.push_back(left); worklist.push_back(right); @@ -3155,6 +3198,10 @@ impl Function { worklist.push_back(array); worklist.push_back(index); } + &Insn::ArrayPop { array, state } => { + worklist.push_back(array); + worklist.push_back(state); + } &Insn::ArrayLength { array } => { worklist.push_back(array); } @@ -5340,7 +5387,7 @@ mod infer_tests { } #[cfg(test)] -mod snapshot_tests { +mod graphviz_tests { use super::*; use insta::assert_snapshot; @@ -5348,10463 +5395,108 @@ mod snapshot_tests { fn hir_string(method: &str) -> String { let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("self", method)); unsafe { crate::cruby::rb_zjit_profile_disable(iseq) }; - let function = iseq_to_hir(iseq).unwrap(); - format!("{}", FunctionPrinter::with_snapshot(&function)) + let mut function = iseq_to_hir(iseq).unwrap(); + function.optimize(); + function.validate().unwrap(); + format!("{}", FunctionGraphvizPrinter::new(&function)) } #[test] - fn test_new_array_with_elements() { - eval("def test(a, b) = [a, b]"); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v13:Any = Snapshot FrameState { pc: 0x1000, stack: [], locals: [a=v11, b=v12] } - v14:Any = Snapshot FrameState { pc: 0x1008, stack: [], locals: [a=v11, b=v12] } - PatchPoint NoTracePoint - v16:Any = Snapshot FrameState { pc: 0x1010, stack: [v11, v12], locals: [a=v11, b=v12] } - v17:ArrayExact = NewArray v11, v12 - v18:Any = Snapshot FrameState { pc: 0x1018, stack: [v17], locals: [a=v11, b=v12] } - PatchPoint NoTracePoint - v20:Any = Snapshot FrameState { pc: 0x1018, stack: [v17], locals: [a=v11, b=v12] } - CheckInterrupts - Return v17 - "); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use insta::assert_snapshot; - - fn iseq_contains_opcode(iseq: IseqPtr, expected_opcode: u32) -> bool { - let iseq_size = unsafe { get_iseq_encoded_size(iseq) }; - let mut insn_idx = 0; - while insn_idx < iseq_size { - // Get the current pc and opcode - let pc = unsafe { rb_iseq_pc_at_idx(iseq, insn_idx) }; - - // try_into() call below is unfortunate. Maybe pick i32 instead of usize for opcodes. - let opcode: u32 = unsafe { rb_iseq_opcode_at_pc(iseq, pc) } - .try_into() - .unwrap(); - if opcode == expected_opcode { - return true; - } - insn_idx += insn_len(opcode as usize); - } - false - } - - #[track_caller] - pub fn assert_contains_opcode(method: &str, opcode: u32) { - let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("self", method)); - unsafe { crate::cruby::rb_zjit_profile_disable(iseq) }; - assert!(iseq_contains_opcode(iseq, opcode), "iseq {method} does not contain {}", insn_name(opcode as usize)); - } + fn test_guard_fixnum_or_fixnum() { + eval(r#" + def test(x, y) = x | y - #[track_caller] - fn assert_contains_opcodes(method: &str, opcodes: &[u32]) { - let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("self", method)); - unsafe { crate::cruby::rb_zjit_profile_disable(iseq) }; - for &opcode in opcodes { - assert!(iseq_contains_opcode(iseq, opcode), "iseq {method} does not contain {}", insn_name(opcode as usize)); + test(1, 2) + "#); + assert_snapshot!(hir_string("test"), @r#" + digraph G { # test@<compiled>:2 + node [shape=plaintext]; + mode=hier; overlap=false; splines=true; + bb0 [label=< + + + + + + +
bb0() 
EntryPoint interpreter 
v1:BasicObject = LoadSelf 
v2:BasicObject = GetLocal l0, SP@5 
v3:BasicObject = GetLocal l0, SP@4 
Jump bb2(v1, v2, v3) 
>]; + bb0:v4 -> bb2:params:n; + bb1 [label=< + + + +
bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject) 
EntryPoint JIT(0) 
Jump bb2(v6, v7, v8) 
>]; + bb1:v9 -> bb2:params:n; + bb2 [label=< + + + + + + + + + + +
bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject) 
PatchPoint NoTracePoint 
PatchPoint NoTracePoint 
PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, 29) 
v26:Fixnum = GuardType v11, Fixnum 
v27:Fixnum = GuardType v12, Fixnum 
v28:Fixnum = FixnumOr v26, v27 
PatchPoint NoTracePoint 
CheckInterrupts 
Return v28 
>]; } - } - - /// Combine multiple hir_string() results to match all of them at once, which allows - /// us to avoid running the set of zjit-test -> zjit-test-update multiple times. - #[macro_export] - macro_rules! hir_strings { - ($( $s:expr ),+ $(,)?) => {{ - vec![$( hir_string($s) ),+].join("\n") - }}; - } - - #[track_caller] - fn hir_string(method: &str) -> String { - hir_string_proc(&format!("{}.method(:{})", "self", method)) - } - - #[track_caller] - fn hir_string_proc(proc: &str) -> String { - let iseq = crate::cruby::with_rubyvm(|| get_proc_iseq(proc)); - unsafe { crate::cruby::rb_zjit_profile_disable(iseq) }; - let function = iseq_to_hir(iseq).unwrap(); - hir_string_function(&function) - } - - #[track_caller] - fn hir_string_function(function: &Function) -> String { - format!("{}", FunctionPrinter::without_snapshot(function)) - } - - #[track_caller] - fn assert_compile_fails(method: &str, reason: ParseError) { - let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("self", method)); - unsafe { crate::cruby::rb_zjit_profile_disable(iseq) }; - let result = iseq_to_hir(iseq); - assert!(result.is_err(), "Expected an error but successfully compiled to HIR: {}", FunctionPrinter::without_snapshot(&result.unwrap())); - assert_eq!(result.unwrap_err(), reason); - } - - #[test] - fn test_compile_optional() { - eval("def test(x=1) = 123"); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - v3:CPtr = LoadPC - v4:CPtr[CPtr(0x1000)] = Const CPtr(0x1008) - v5:CBool = IsBitEqual v3, v4 - IfTrue v5, bb2(v1, v2) - Jump bb4(v1, v2) - bb1(v9:BasicObject, v10:BasicObject): - EntryPoint JIT(0) - Jump bb2(v9, v10) - bb2(v12:BasicObject, v13:BasicObject): - v15:Fixnum[1] = Const Value(1) - Jump bb4(v12, v15) - bb3(v18:BasicObject, v19:BasicObject): - EntryPoint JIT(1) - Jump bb4(v18, v19) - bb4(v21:BasicObject, v22:BasicObject): - v26:Fixnum[123] = Const Value(123) - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_putobject() { - eval("def test = 123"); - assert_contains_opcode("test", YARVINSN_putobject); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[123] = Const Value(123) - CheckInterrupts - Return v10 - "); - } - - #[test] - fn test_new_array() { - eval("def test = []"); - assert_contains_opcode("test", YARVINSN_newarray); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:ArrayExact = NewArray - CheckInterrupts - Return v11 - "); - } - - #[test] - fn test_new_array_with_element() { - eval("def test(a) = [a]"); - assert_contains_opcode("test", YARVINSN_newarray); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v14:ArrayExact = NewArray v9 - CheckInterrupts - Return v14 - "); - } - - #[test] - fn test_new_array_with_elements() { - eval("def test(a, b) = [a, b]"); - assert_contains_opcode("test", YARVINSN_newarray); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v17:ArrayExact = NewArray v11, v12 - CheckInterrupts - Return v17 - "); - } - - #[test] - fn test_new_range_inclusive_with_one_element() { - eval("def test(a) = (a..10)"); - assert_contains_opcode("test", YARVINSN_newrange); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[10] = Const Value(10) - v15:RangeExact = NewRange v9 NewRangeInclusive v13 - CheckInterrupts - Return v15 - "); - } - - #[test] - fn test_new_range_inclusive_with_two_elements() { - eval("def test(a, b) = (a..b)"); - assert_contains_opcode("test", YARVINSN_newrange); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v17:RangeExact = NewRange v11 NewRangeInclusive v12 - CheckInterrupts - Return v17 - "); - } - - #[test] - fn test_new_range_exclusive_with_one_element() { - eval("def test(a) = (a...10)"); - assert_contains_opcode("test", YARVINSN_newrange); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[10] = Const Value(10) - v15:RangeExact = NewRange v9 NewRangeExclusive v13 - CheckInterrupts - Return v15 - "); - } - - #[test] - fn test_new_range_exclusive_with_two_elements() { - eval("def test(a, b) = (a...b)"); - assert_contains_opcode("test", YARVINSN_newrange); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v17:RangeExact = NewRange v11 NewRangeExclusive v12 - CheckInterrupts - Return v17 - "); - } - - #[test] - fn test_array_dup() { - eval("def test = [1, 2, 3]"); - assert_contains_opcode("test", YARVINSN_duparray); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v12:ArrayExact = ArrayDup v10 - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_hash_dup() { - eval("def test = {a: 1, b: 2}"); - assert_contains_opcode("test", YARVINSN_duphash); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v12:HashExact = HashDup v10 - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_new_hash_empty() { - eval("def test = {}"); - assert_contains_opcode("test", YARVINSN_newhash); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:HashExact = NewHash - CheckInterrupts - Return v11 - "); - } - - #[test] - fn test_new_hash_with_elements() { - eval("def test(aval, bval) = {a: aval, b: bval}"); - assert_contains_opcode("test", YARVINSN_newhash); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v16:StaticSymbol[:a] = Const Value(VALUE(0x1000)) - v17:StaticSymbol[:b] = Const Value(VALUE(0x1008)) - v19:HashExact = NewHash v16: v11, v17: v12 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_string_copy() { - eval("def test = \"hello\""); - assert_contains_opcode("test", YARVINSN_putchilledstring); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v12:StringExact = StringCopy v10 - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_bignum() { - eval("def test = 999999999999999999999999999999999999"); - assert_contains_opcode("test", YARVINSN_putobject); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Bignum[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - CheckInterrupts - Return v10 - "); - } - - #[test] - fn test_flonum() { - eval("def test = 1.5"); - assert_contains_opcode("test", YARVINSN_putobject); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Flonum[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - CheckInterrupts - Return v10 - "); - } - - #[test] - fn test_heap_float() { - eval("def test = 1.7976931348623157e+308"); - assert_contains_opcode("test", YARVINSN_putobject); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:HeapFloat[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - CheckInterrupts - Return v10 - "); - } - - #[test] - fn test_static_sym() { - eval("def test = :foo"); - assert_contains_opcode("test", YARVINSN_putobject); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - CheckInterrupts - Return v10 - "); - } - - #[test] - fn test_opt_plus() { - eval("def test = 1+2"); - assert_contains_opcode("test", YARVINSN_opt_plus); - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - v11:Fixnum[2] = Const Value(2) - v15:BasicObject = SendWithoutBlock v10, :+, v11 - CheckInterrupts - Return v15 - "); - } - - #[test] - fn test_opt_hash_freeze() { - eval(" - def test = {}.freeze - "); - assert_contains_opcode("test", YARVINSN_opt_hash_freeze); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) - v12:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_opt_hash_freeze_rewritten() { - eval(" - class Hash - def freeze; 5; end - end - def test = {}.freeze - "); - assert_contains_opcode("test", YARVINSN_opt_hash_freeze); - assert_snapshot!(hir_string("test"), @r" - fn test@:5: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - SideExit PatchPoint(BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE)) - "); - } - - #[test] - fn test_opt_ary_freeze() { - eval(" - def test = [].freeze - "); - assert_contains_opcode("test", YARVINSN_opt_ary_freeze); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) - v12:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_opt_ary_freeze_rewritten() { - eval(" - class Array - def freeze; 5; end - end - def test = [].freeze - "); - assert_contains_opcode("test", YARVINSN_opt_ary_freeze); - assert_snapshot!(hir_string("test"), @r" - fn test@:5: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - SideExit PatchPoint(BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE)) - "); - } - - #[test] - fn test_opt_str_freeze() { - eval(" - def test = ''.freeze - "); - assert_contains_opcode("test", YARVINSN_opt_str_freeze); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) - v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_opt_str_freeze_rewritten() { - eval(" - class String - def freeze; 5; end - end - def test = ''.freeze - "); - assert_contains_opcode("test", YARVINSN_opt_str_freeze); - assert_snapshot!(hir_string("test"), @r" - fn test@:5: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - SideExit PatchPoint(BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE)) - "); - } - - #[test] - fn test_opt_str_uminus() { - eval(" - def test = -'' - "); - assert_contains_opcode("test", YARVINSN_opt_str_uminus); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS) - v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_opt_str_uminus_rewritten() { - eval(" - class String - def -@; 5; end - end - def test = -'' - "); - assert_contains_opcode("test", YARVINSN_opt_str_uminus); - assert_snapshot!(hir_string("test"), @r" - fn test@:5: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - SideExit PatchPoint(BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS)) - "); - } - - #[test] - fn test_setlocal_getlocal() { - eval(" - def test - a = 1 - a - end - "); - assert_contains_opcodes("test", &[YARVINSN_getlocal_WC_0, YARVINSN_setlocal_WC_0]); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v13:Fixnum[1] = Const Value(1) - CheckInterrupts - Return v13 - "); - } - - #[test] - fn test_nested_setlocal_getlocal() { - eval(" - l3 = 3 - _unused = _unused1 = nil - 1.times do |l2| - _ = nil - l2 = 2 - 1.times do |l1| - l1 = 1 - define_method(:test) do - l1 = l2 - l2 = l1 + l2 - l3 = l2 + l3 - end - end - end - "); - assert_contains_opcodes( - "test", - &[YARVINSN_getlocal_WC_1, YARVINSN_setlocal_WC_1, - YARVINSN_getlocal, YARVINSN_setlocal]); - assert_snapshot!(hir_string("test"), @r" - fn block (3 levels) in @:10: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:BasicObject = GetLocal l2, EP@4 - SetLocal l1, EP@3, v10 - v14:BasicObject = GetLocal l1, EP@3 - v15:BasicObject = GetLocal l2, EP@4 - v19:BasicObject = SendWithoutBlock v14, :+, v15 - SetLocal l2, EP@4, v19 - v23:BasicObject = GetLocal l2, EP@4 - v24:BasicObject = GetLocal l3, EP@5 - v28:BasicObject = SendWithoutBlock v23, :+, v24 - SetLocal l3, EP@5, v28 - CheckInterrupts - Return v28 - " - ); - } - - #[test] - fn test_setlocal_in_default_args() { - eval(" - def test(a = (b = 1)) = [a, b] - "); - assert_contains_opcode("test", YARVINSN_setlocal_WC_0); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:NilClass = Const Value(nil) - v4:CPtr = LoadPC - v5:CPtr[CPtr(0x1000)] = Const CPtr(0x1008) - v6:CBool = IsBitEqual v4, v5 - IfTrue v6, bb2(v1, v2, v3) - Jump bb4(v1, v2, v3) - bb1(v10:BasicObject, v11:BasicObject): - EntryPoint JIT(0) - v12:NilClass = Const Value(nil) - Jump bb2(v10, v11, v12) - bb2(v14:BasicObject, v15:BasicObject, v16:NilClass): - v20:Fixnum[1] = Const Value(1) - Jump bb4(v14, v20, v20) - bb3(v23:BasicObject, v24:BasicObject): - EntryPoint JIT(1) - v25:NilClass = Const Value(nil) - Jump bb4(v23, v24, v25) - bb4(v27:BasicObject, v28:BasicObject, v29:NilClass|Fixnum): - v34:ArrayExact = NewArray v28, v29 - CheckInterrupts - Return v34 - "); - } - - #[test] - fn test_setlocal_in_default_args_with_tracepoint() { - eval(" - def test(a = (b = 1)) = [a, b] - TracePoint.new(:line) {}.enable - test - "); - assert_compile_fails("test", ParseError::FailedOptionalArguments); - } - - #[test] - fn test_setlocal_in_default_args_with_side_exit() { - eval(" - def test(a = (def foo = nil)) = a - "); - assert_compile_fails("test", ParseError::FailedOptionalArguments); - } - - #[test] - fn test_setlocal_cyclic_default_args() { - eval(" - def test = proc { |a=a| a } - "); - assert_snapshot!(hir_string_proc("test"), @r" - fn block in test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - CheckInterrupts - Return v9 - "); - } - - #[test] - fn defined_ivar() { - eval(" - def test = defined?(@foo) - "); - assert_contains_opcode("test", YARVINSN_definedivar); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:StringExact|NilClass = DefinedIvar v6, :@foo - CheckInterrupts - Return v11 - "); - } - - #[test] - fn if_defined_ivar() { - eval(" - def test - if defined?(@foo) - 3 - else - 4 - end - end - "); - assert_contains_opcode("test", YARVINSN_definedivar); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:TrueClass|NilClass = DefinedIvar v6, :@foo - CheckInterrupts - v14:CBool = Test v11 - IfFalse v14, bb3(v6) - v18:Fixnum[3] = Const Value(3) - CheckInterrupts - Return v18 - bb3(v24:BasicObject): - v28:Fixnum[4] = Const Value(4) - CheckInterrupts - Return v28 - "); - } - - #[test] - fn defined() { - eval(" - def test = return defined?(SeaChange), defined?(favourite), defined?($ruby) - "); - assert_contains_opcode("test", YARVINSN_defined); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:NilClass = Const Value(nil) - v12:StringExact|NilClass = Defined constant, v10 - v14:StringExact|NilClass = Defined func, v6 - v15:NilClass = Const Value(nil) - v17:StringExact|NilClass = Defined global-variable, v15 - v19:ArrayExact = NewArray v12, v14, v17 - CheckInterrupts - Return v19 - "); + "#); } #[test] - fn test_return_const() { - eval(" - def test(cond) - if cond + fn test_multiple_blocks() { + eval(r#" + def test(c) + if c 3 else 4 end end - "); - assert_contains_opcode("test", YARVINSN_leave); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - CheckInterrupts - v15:CBool = Test v9 - IfFalse v15, bb3(v8, v9) - v19:Fixnum[3] = Const Value(3) - CheckInterrupts - Return v19 - bb3(v25:BasicObject, v26:BasicObject): - v30:Fixnum[4] = Const Value(4) - CheckInterrupts - Return v30 - "); - } - - #[test] - fn test_merge_const() { - eval(" - def test(cond) - if cond - result = 3 - else - result = 4 - end - result - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:NilClass = Const Value(nil) - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject): - EntryPoint JIT(0) - v8:NilClass = Const Value(nil) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:NilClass): - CheckInterrupts - v18:CBool = Test v11 - IfFalse v18, bb3(v10, v11, v12) - v22:Fixnum[3] = Const Value(3) - PatchPoint NoEPEscape(test) - CheckInterrupts - Jump bb4(v10, v11, v22) - bb3(v28:BasicObject, v29:BasicObject, v30:NilClass): - v34:Fixnum[4] = Const Value(4) - PatchPoint NoEPEscape(test) - Jump bb4(v28, v29, v34) - bb4(v38:BasicObject, v39:BasicObject, v40:Fixnum): - PatchPoint NoEPEscape(test) - CheckInterrupts - Return v40 - "); - } - - #[test] - fn test_opt_plus_fixnum() { - eval(" - def test(a, b) = a + b - test(1, 2); test(1, 2) - "); - assert_contains_opcode("test", YARVINSN_opt_plus); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :+, v12 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_opt_minus_fixnum() { - eval(" - def test(a, b) = a - b - test(1, 2); test(1, 2) - "); - assert_contains_opcode("test", YARVINSN_opt_minus); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :-, v12 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_opt_mult_fixnum() { - eval(" - def test(a, b) = a * b - test(1, 2); test(1, 2) - "); - assert_contains_opcode("test", YARVINSN_opt_mult); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :*, v12 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_opt_div_fixnum() { - eval(" - def test(a, b) = a / b - test(1, 2); test(1, 2) - "); - assert_contains_opcode("test", YARVINSN_opt_div); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :/, v12 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_opt_mod_fixnum() { - eval(" - def test(a, b) = a % b - test(1, 2); test(1, 2) - "); - assert_contains_opcode("test", YARVINSN_opt_mod); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :%, v12 - CheckInterrupts - Return v19 - "); - } - #[test] - fn test_opt_eq_fixnum() { - eval(" - def test(a, b) = a == b - test(1, 2); test(1, 2) - "); - assert_contains_opcode("test", YARVINSN_opt_eq); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :==, v12 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_opt_neq_fixnum() { - eval(" - def test(a, b) = a != b - test(1, 2); test(1, 2) - "); - assert_contains_opcode("test", YARVINSN_opt_neq); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :!=, v12 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_opt_lt_fixnum() { - eval(" - def test(a, b) = a < b - test(1, 2); test(1, 2) - "); - assert_contains_opcode("test", YARVINSN_opt_lt); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :<, v12 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_opt_le_fixnum() { - eval(" - def test(a, b) = a <= b - test(1, 2); test(1, 2) - "); - assert_contains_opcode("test", YARVINSN_opt_le); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :<=, v12 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_opt_gt_fixnum() { - eval(" - def test(a, b) = a > b - test(1, 2); test(1, 2) - "); - assert_contains_opcode("test", YARVINSN_opt_gt); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :>, v12 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_loop() { - eval(" - def test - result = 0 - times = 10 - while times > 0 - result = result + 1 - times = times - 1 - end - result - end - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - v3:NilClass = Const Value(nil) - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject): - EntryPoint JIT(0) - v7:NilClass = Const Value(nil) - v8:NilClass = Const Value(nil) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:NilClass, v12:NilClass): - v16:Fixnum[0] = Const Value(0) - v19:Fixnum[10] = Const Value(10) - CheckInterrupts - Jump bb4(v10, v16, v19) - bb4(v25:BasicObject, v26:BasicObject, v27:BasicObject): - PatchPoint NoEPEscape(test) - v31:Fixnum[0] = Const Value(0) - v35:BasicObject = SendWithoutBlock v27, :>, v31 - CheckInterrupts - v38:CBool = Test v35 - IfTrue v38, bb3(v25, v26, v27) - v40:NilClass = Const Value(nil) - PatchPoint NoEPEscape(test) - CheckInterrupts - Return v26 - bb3(v50:BasicObject, v51:BasicObject, v52:BasicObject): - PatchPoint NoEPEscape(test) - v58:Fixnum[1] = Const Value(1) - v62:BasicObject = SendWithoutBlock v51, :+, v58 - v65:Fixnum[1] = Const Value(1) - v69:BasicObject = SendWithoutBlock v52, :-, v65 - Jump bb4(v50, v62, v69) - "); - } - - #[test] - fn test_opt_ge_fixnum() { - eval(" - def test(a, b) = a >= b - test(1, 2); test(1, 2) - "); - assert_contains_opcode("test", YARVINSN_opt_ge); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :>=, v12 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_display_types() { - eval(" - def test - cond = true - if cond - 3 - else - 4 - end - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v13:TrueClass = Const Value(true) - CheckInterrupts - v18:CBool[true] = Test v13 - IfFalse v18, bb3(v8, v13) - v22:Fixnum[3] = Const Value(3) - CheckInterrupts - Return v22 - bb3(v28, v29): - v33 = Const Value(4) - CheckInterrupts - Return v33 - "); - } - - #[test] - fn test_send_without_block() { - eval(" - def bar(a, b) - a+b - end - def test - bar(2, 3) - end - "); - assert_contains_opcode("test", YARVINSN_opt_send_without_block); - assert_snapshot!(hir_string("test"), @r" - fn test@:6: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[2] = Const Value(2) - v11:Fixnum[3] = Const Value(3) - v13:BasicObject = SendWithoutBlock v6, :bar, v10, v11 - CheckInterrupts - Return v13 - "); - } - - #[test] - fn test_send_with_block() { - eval(" - def test(a) - a.each {|item| - item - } - end - test([1,2,3]) - "); - assert_contains_opcode("test", YARVINSN_send); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:BasicObject = GetLocal l0, EP@3 - v15:BasicObject = Send v13, 0x1000, :each - v16:BasicObject = GetLocal l0, EP@3 - CheckInterrupts - Return v15 - "); - } - - #[test] - fn test_intern_interpolated_symbol() { - eval(r#" - def test - :"foo#{123}" - end - "#); - assert_contains_opcode("test", YARVINSN_intern); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v11:Fixnum[123] = Const Value(123) - v13:BasicObject = ObjToString v11 - v15:String = AnyToString v11, str: v13 - v17:StringExact = StringConcat v10, v15 - v19:Symbol = StringIntern v17 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn different_objects_get_addresses() { - eval("def test = unknown_method([0], [1], '2', '2')"); - - // The 2 string literals have the same address because they're deduped. - assert_snapshot!(hir_string("test"), @r" - fn test@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v12:ArrayExact = ArrayDup v10 - v13:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v15:ArrayExact = ArrayDup v13 - v16:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) - v18:StringExact = StringCopy v16 - v19:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) - v21:StringExact = StringCopy v19 - v23:BasicObject = SendWithoutBlock v6, :unknown_method, v12, v15, v18, v21 - CheckInterrupts - Return v23 - "); - } - - #[test] - fn test_cant_compile_splat() { - eval(" - def test(a) = foo(*a) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v14:ArrayExact = ToArray v9 - SideExit UnhandledCallType(Splat) - "); - } - - #[test] - fn test_compile_block_arg() { - eval(" - def test(a) = foo(&a) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v14:BasicObject = Send v8, 0x1000, :foo, v9 - CheckInterrupts - Return v14 - "); - } - - #[test] - fn test_cant_compile_kwarg() { - eval(" - def test(a) = foo(a: 1) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[1] = Const Value(1) - SideExit UnhandledCallType(Kwarg) - "); - } - - #[test] - fn test_cant_compile_kw_splat() { - eval(" - def test(a) = foo(**a) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v14:BasicObject = SendWithoutBlock v8, :foo, v9 - CheckInterrupts - Return v14 - "); - } - - // TODO(max): Figure out how to generate a call with TAILCALL flag - - #[test] - fn test_compile_super() { - eval(" - def test = super() - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:BasicObject = InvokeSuper v6, 0x1000 - CheckInterrupts - Return v11 - "); - } - - #[test] - fn test_compile_zsuper() { - eval(" - def test = super - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:BasicObject = InvokeSuper v6, 0x1000 - CheckInterrupts - Return v11 - "); - } - - #[test] - fn test_cant_compile_super_nil_blockarg() { - eval(" - def test = super(&nil) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:NilClass = Const Value(nil) - v12:BasicObject = InvokeSuper v6, 0x1000, v10 - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_cant_compile_super_forward() { - eval(" - def test(...) = super(...) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - SideExit UnhandledYARVInsn(invokesuperforward) - "); - } - - #[test] - fn test_compile_forwardable() { - eval("def forwardable(...) = nil"); - assert_snapshot!(hir_string("forwardable"), @r" - fn forwardable@:1: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:NilClass = Const Value(nil) - CheckInterrupts - Return v13 - "); - } - - // TODO(max): Figure out how to generate a call with OPT_SEND flag - - #[test] - fn test_cant_compile_kw_splat_mut() { - eval(" - def test(a) = foo **a, b: 1 - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Class[VMFrozenCore] = Const Value(VALUE(0x1000)) - v15:HashExact = NewHash - PatchPoint NoEPEscape(test) - v19:BasicObject = SendWithoutBlock v13, :core#hash_merge_kwd, v15, v9 - v20:Class[VMFrozenCore] = Const Value(VALUE(0x1000)) - v21:StaticSymbol[:b] = Const Value(VALUE(0x1008)) - v22:Fixnum[1] = Const Value(1) - v24:BasicObject = SendWithoutBlock v20, :core#hash_merge_ptr, v19, v21, v22 - v26:BasicObject = SendWithoutBlock v8, :foo, v24 - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_cant_compile_splat_mut() { - eval(" - def test(*) = foo *, 1 - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:ArrayExact = GetLocal l0, SP@4, * - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:ArrayExact): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:ArrayExact): - v14:ArrayExact = ToNewArray v9 - v15:Fixnum[1] = Const Value(1) - ArrayPush v14, v15 - SideExit UnhandledCallType(Splat) - "); - } - - #[test] - fn test_compile_forwarding() { - eval(" - def test(...) = foo(...) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v14:BasicObject = SendForward 0x1000, :foo, v9 - CheckInterrupts - Return v14 - "); - } - - #[test] - fn test_compile_triple_dots_with_positional_args() { - eval(" - def test(a, ...) = foo(a, ...) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@8 - v3:ArrayExact = GetLocal l0, SP@7, * - v4:BasicObject = GetLocal l0, SP@6 - v5:BasicObject = GetLocal l0, SP@5 - v6:NilClass = Const Value(nil) - Jump bb2(v1, v2, v3, v4, v5, v6) - bb1(v9:BasicObject, v10:BasicObject, v11:ArrayExact, v12:BasicObject, v13:BasicObject): - EntryPoint JIT(0) - v14:NilClass = Const Value(nil) - Jump bb2(v9, v10, v11, v12, v13, v14) - bb2(v16:BasicObject, v17:BasicObject, v18:ArrayExact, v19:BasicObject, v20:BasicObject, v21:NilClass): - v26:ArrayExact = ToArray v18 - PatchPoint NoEPEscape(test) - GuardBlockParamProxy l0 - v31:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1000)) - SideExit UnhandledYARVInsn(splatkw) - "); - } - - #[test] - fn test_opt_new() { - eval(" - class C; end - def test = C.new - "); - assert_contains_opcode("test", YARVINSN_opt_new); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:BasicObject = GetConstantPath 0x1000 - v12:NilClass = Const Value(nil) - v14:CBool = IsMethodCFunc v11, :new - IfFalse v14, bb3(v6, v12, v11) - v16:HeapBasicObject = ObjectAlloc v11 - v18:BasicObject = SendWithoutBlock v16, :initialize - CheckInterrupts - Jump bb4(v6, v16, v18) - bb3(v22:BasicObject, v23:NilClass, v24:BasicObject): - v27:BasicObject = SendWithoutBlock v24, :new - Jump bb4(v22, v27, v23) - bb4(v29:BasicObject, v30:BasicObject, v31:BasicObject): - CheckInterrupts - Return v30 - "); - } - - #[test] - fn test_opt_newarray_send_max_no_elements() { - eval(" - def test = [].max - "); - // TODO(max): Rewrite to nil - assert_contains_opcode("test", YARVINSN_opt_newarray_send); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_MAX) - v12:BasicObject = ArrayMax - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_opt_newarray_send_max() { - eval(" - def test(a,b) = [a,b].max - "); - assert_contains_opcode("test", YARVINSN_opt_newarray_send); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_MAX) - v18:BasicObject = ArrayMax v11, v12 - CheckInterrupts - Return v18 - "); - } - - #[test] - fn test_opt_newarray_send_min() { - eval(" - def test(a,b) - sum = a+b - result = [a,b].min - puts [1,2,3] - result - end - "); - assert_contains_opcode("test", YARVINSN_opt_newarray_send); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@7 - v3:BasicObject = GetLocal l0, SP@6 - v4:NilClass = Const Value(nil) - v5:NilClass = Const Value(nil) - Jump bb2(v1, v2, v3, v4, v5) - bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject): - EntryPoint JIT(0) - v11:NilClass = Const Value(nil) - v12:NilClass = Const Value(nil) - Jump bb2(v8, v9, v10, v11, v12) - bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 - SideExit UnknownNewarraySend(MIN) - "); - } - - #[test] - fn test_opt_newarray_send_hash() { - eval(" - def test(a,b) - sum = a+b - result = [a,b].hash - puts [1,2,3] - result - end - "); - assert_contains_opcode("test", YARVINSN_opt_newarray_send); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@7 - v3:BasicObject = GetLocal l0, SP@6 - v4:NilClass = Const Value(nil) - v5:NilClass = Const Value(nil) - Jump bb2(v1, v2, v3, v4, v5) - bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject): - EntryPoint JIT(0) - v11:NilClass = Const Value(nil) - v12:NilClass = Const Value(nil) - Jump bb2(v8, v9, v10, v11, v12) - bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 - SideExit UnknownNewarraySend(HASH) - "); - } - - #[test] - fn test_opt_newarray_send_pack() { - eval(" - def test(a,b) - sum = a+b - result = [a,b].pack 'C' - puts [1,2,3] - result - end - "); - assert_contains_opcode("test", YARVINSN_opt_newarray_send); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@7 - v3:BasicObject = GetLocal l0, SP@6 - v4:NilClass = Const Value(nil) - v5:NilClass = Const Value(nil) - Jump bb2(v1, v2, v3, v4, v5) - bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject): - EntryPoint JIT(0) - v11:NilClass = Const Value(nil) - v12:NilClass = Const Value(nil) - Jump bb2(v8, v9, v10, v11, v12) - bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 - v28:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v30:StringExact = StringCopy v28 - SideExit UnknownNewarraySend(PACK) - "); - } - - // TODO(max): Add a test for VM_OPT_NEWARRAY_SEND_PACK_BUFFER - - #[test] - fn test_opt_newarray_send_include_p() { - eval(" - def test(a,b) - sum = a+b - result = [a,b].include? b - puts [1,2,3] - result - end - "); - assert_contains_opcode("test", YARVINSN_opt_newarray_send); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@7 - v3:BasicObject = GetLocal l0, SP@6 - v4:NilClass = Const Value(nil) - v5:NilClass = Const Value(nil) - Jump bb2(v1, v2, v3, v4, v5) - bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject): - EntryPoint JIT(0) - v11:NilClass = Const Value(nil) - v12:NilClass = Const Value(nil) - Jump bb2(v8, v9, v10, v11, v12) - bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 - SideExit UnknownNewarraySend(INCLUDE_P) - "); - } - - #[test] - fn test_opt_length() { - eval(" - def test(a,b) = [a,b].length - "); - assert_contains_opcode("test", YARVINSN_opt_length); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v17:ArrayExact = NewArray v11, v12 - v21:BasicObject = SendWithoutBlock v17, :length - CheckInterrupts - Return v21 - "); - } - - #[test] - fn test_opt_size() { - eval(" - def test(a,b) = [a,b].size - "); - assert_contains_opcode("test", YARVINSN_opt_size); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v17:ArrayExact = NewArray v11, v12 - v21:BasicObject = SendWithoutBlock v17, :size - CheckInterrupts - Return v21 - "); - } - - #[test] - fn test_getinstancevariable() { - eval(" - def test = @foo - test - "); - assert_contains_opcode("test", YARVINSN_getinstancevariable); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - v12:BasicObject = GetIvar v6, :@foo - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_trace_getinstancevariable() { - eval(" - def test = @foo - test - trace = TracePoint.trace(:call) { |tp| } - trace.enable { test } - "); - assert_contains_opcode("test", YARVINSN_trace_getinstancevariable); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - SideExit UnhandledYARVInsn(trace_getinstancevariable) - "); - } - - #[test] - fn test_setinstancevariable() { - eval(" - def test = @foo = 1 - test - "); - assert_contains_opcode("test", YARVINSN_setinstancevariable); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - PatchPoint SingleRactorMode - SetIvar v6, :@foo, v10 - CheckInterrupts - Return v10 - "); - } - - #[test] - fn test_set_ivar_rescue_frozen() { - let result = eval(" - class Foo - attr_accessor :bar - def initialize - @bar = 1 - freeze - end - end - - def test(foo) - begin - foo.bar = 2 - rescue FrozenError - end - end - - foo = Foo.new - test(foo) - test(foo) - - foo.bar - "); - assert_eq!(VALUE::fixnum_from_usize(1), result); - } - - #[test] - fn test_getclassvariable() { - eval(" - class Foo - def self.test = @@foo - end - "); - let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("Foo", "test")); - assert!(iseq_contains_opcode(iseq, YARVINSN_getclassvariable), "iseq Foo.test does not contain getclassvariable"); - let function = iseq_to_hir(iseq).unwrap(); - assert_snapshot!(hir_string_function(&function), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:BasicObject = GetClassVar :@@foo - CheckInterrupts - Return v11 - "); - } - - #[test] - fn test_setclassvariable() { - eval(" - class Foo - def self.test = @@foo = 42 - end - "); - let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("Foo", "test")); - assert!(iseq_contains_opcode(iseq, YARVINSN_setclassvariable), "iseq Foo.test does not contain setclassvariable"); - let function = iseq_to_hir(iseq).unwrap(); - assert_snapshot!(hir_string_function(&function), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[42] = Const Value(42) - SetClassVar :@@foo, v10 - CheckInterrupts - Return v10 - "); - } - - #[test] - fn test_setglobal() { - eval(" - def test = $foo = 1 - test - "); - assert_contains_opcode("test", YARVINSN_setglobal); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - SetGlobal :$foo, v10 - CheckInterrupts - Return v10 - "); - } - - #[test] - fn test_getglobal() { - eval(" - def test = $foo - test - "); - assert_contains_opcode("test", YARVINSN_getglobal); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:BasicObject = GetGlobal :$foo - CheckInterrupts - Return v11 - "); - } - - #[test] - fn test_splatarray_mut() { - eval(" - def test(a) = [*a] - "); - assert_contains_opcode("test", YARVINSN_splatarray); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v14:ArrayExact = ToNewArray v9 - CheckInterrupts - Return v14 - "); - } - - #[test] - fn test_concattoarray() { - eval(" - def test(a) = [1, *a] - "); - assert_contains_opcode("test", YARVINSN_concattoarray); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[1] = Const Value(1) - v15:ArrayExact = NewArray v13 - v17:ArrayExact = ToArray v9 - ArrayExtend v15, v17 - CheckInterrupts - Return v15 - "); - } - - #[test] - fn test_pushtoarray_one_element() { - eval(" - def test(a) = [*a, 1] - "); - assert_contains_opcode("test", YARVINSN_pushtoarray); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v14:ArrayExact = ToNewArray v9 - v15:Fixnum[1] = Const Value(1) - ArrayPush v14, v15 - CheckInterrupts - Return v14 - "); - } - - #[test] - fn test_pushtoarray_multiple_elements() { - eval(" - def test(a) = [*a, 1, 2, 3] - "); - assert_contains_opcode("test", YARVINSN_pushtoarray); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v14:ArrayExact = ToNewArray v9 - v15:Fixnum[1] = Const Value(1) - v16:Fixnum[2] = Const Value(2) - v17:Fixnum[3] = Const Value(3) - ArrayPush v14, v15 - ArrayPush v14, v16 - ArrayPush v14, v17 - CheckInterrupts - Return v14 - "); - } - - #[test] - fn test_aset() { - eval(" - def test(a, b) = a[b] = 1 - "); - assert_contains_opcode("test", YARVINSN_opt_aset); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v16:NilClass = Const Value(nil) - v17:Fixnum[1] = Const Value(1) - v21:BasicObject = SendWithoutBlock v11, :[]=, v12, v17 - CheckInterrupts - Return v17 - "); - } - - #[test] - fn test_aref() { - eval(" - def test(a, b) = a[b] - "); - assert_contains_opcode("test", YARVINSN_opt_aref); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :[], v12 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn opt_empty_p() { - eval(" - def test(x) = x.empty? - "); - assert_contains_opcode("test", YARVINSN_opt_empty_p); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal 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:BasicObject = SendWithoutBlock v9, :empty? - CheckInterrupts - Return v16 - "); - } - - #[test] - fn opt_succ() { - eval(" - def test(x) = x.succ - "); - assert_contains_opcode("test", YARVINSN_opt_succ); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal 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:BasicObject = SendWithoutBlock v9, :succ - CheckInterrupts - Return v16 - "); - } - - #[test] - fn opt_and() { - eval(" - def test(x, y) = x & y - "); - assert_contains_opcode("test", YARVINSN_opt_and); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :&, v12 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn opt_or() { - eval(" - def test(x, y) = x | y - "); - assert_contains_opcode("test", YARVINSN_opt_or); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :|, v12 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn opt_not() { - eval(" - def test(x) = !x - "); - assert_contains_opcode("test", YARVINSN_opt_not); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal 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:BasicObject = SendWithoutBlock v9, :! - CheckInterrupts - Return v16 - "); - } - - #[test] - fn opt_regexpmatch2() { - eval(" - def test(regexp, matchee) = regexp =~ matchee - "); - assert_contains_opcode("test", YARVINSN_opt_regexpmatch2); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :=~, v12 - CheckInterrupts - Return v19 - "); - } - - #[test] - // Tests for ConstBase requires either constant or class definition, both - // of which can't be performed inside a method. - fn test_putspecialobject_vm_core_and_cbase() { - eval(" - def test - alias aliased __callee__ - end - "); - assert_contains_opcode("test", YARVINSN_putspecialobject); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Class[VMFrozenCore] = Const Value(VALUE(0x1000)) - v11:BasicObject = PutSpecialObject CBase - v12:StaticSymbol[:aliased] = Const Value(VALUE(0x1008)) - v13:StaticSymbol[:__callee__] = Const Value(VALUE(0x1010)) - v15:BasicObject = SendWithoutBlock v10, :core#set_method_alias, v11, v12, v13 - CheckInterrupts - Return v15 - "); - } - - #[test] - fn opt_reverse() { - eval(" - def reverse_odd - a, b, c = @a, @b, @c - [a, b, c] - end - - def reverse_even - a, b, c, d = @a, @b, @c, @d - [a, b, c, d] - end - "); - assert_contains_opcode("reverse_odd", YARVINSN_opt_reverse); - assert_contains_opcode("reverse_even", YARVINSN_opt_reverse); - assert_snapshot!(hir_strings!("reverse_odd", "reverse_even"), @r" - fn reverse_odd@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - v3:NilClass = Const Value(nil) - v4:NilClass = Const Value(nil) - Jump bb2(v1, v2, v3, v4) - bb1(v7:BasicObject): - EntryPoint JIT(0) - v8:NilClass = Const Value(nil) - v9:NilClass = Const Value(nil) - v10:NilClass = Const Value(nil) - Jump bb2(v7, v8, v9, v10) - bb2(v12:BasicObject, v13:NilClass, v14:NilClass, v15:NilClass): - PatchPoint SingleRactorMode - v21:BasicObject = GetIvar v12, :@a - PatchPoint SingleRactorMode - v24:BasicObject = GetIvar v12, :@b - PatchPoint SingleRactorMode - v27:BasicObject = GetIvar v12, :@c - PatchPoint NoEPEscape(reverse_odd) - v33:ArrayExact = NewArray v21, v24, v27 - CheckInterrupts - Return v33 - - fn reverse_even@:8: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - v3:NilClass = Const Value(nil) - v4:NilClass = Const Value(nil) - v5:NilClass = Const Value(nil) - Jump bb2(v1, v2, v3, v4, v5) - bb1(v8:BasicObject): - EntryPoint JIT(0) - v9:NilClass = Const Value(nil) - v10:NilClass = Const Value(nil) - v11:NilClass = Const Value(nil) - v12:NilClass = Const Value(nil) - Jump bb2(v8, v9, v10, v11, v12) - bb2(v14:BasicObject, v15:NilClass, v16:NilClass, v17:NilClass, v18:NilClass): - PatchPoint SingleRactorMode - v24:BasicObject = GetIvar v14, :@a - PatchPoint SingleRactorMode - v27:BasicObject = GetIvar v14, :@b - PatchPoint SingleRactorMode - v30:BasicObject = GetIvar v14, :@c - PatchPoint SingleRactorMode - v33:BasicObject = GetIvar v14, :@d - PatchPoint NoEPEscape(reverse_even) - v39:ArrayExact = NewArray v24, v27, v30, v33 - CheckInterrupts - Return v39 - "); - } - - #[test] - fn test_branchnil() { - eval(" - def test(x) = x&.itself - "); - assert_contains_opcode("test", YARVINSN_branchnil); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - CheckInterrupts - v15:CBool = IsNil v9 - IfTrue v15, bb3(v8, v9, v9) - v18:BasicObject = SendWithoutBlock v9, :itself - Jump bb3(v8, v9, v18) - bb3(v20:BasicObject, v21:BasicObject, v22:BasicObject): - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_invokebuiltin_delegate_annotated() { - assert_contains_opcode("Float", YARVINSN_opt_invokebuiltin_delegate_leave); - assert_snapshot!(hir_string("Float"), @r" - fn Float@: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@6 - v3:BasicObject = GetLocal l0, SP@5 - v4:BasicObject = GetLocal 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): - v20:Float = InvokeBuiltin rb_f_float, v12, v13, v14 - Jump bb3(v12, v13, v14, v15, v20) - bb3(v22:BasicObject, v23:BasicObject, v24:BasicObject, v25:BasicObject, v26:Float): - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_invokebuiltin_cexpr_annotated() { - assert_contains_opcode("class", YARVINSN_opt_invokebuiltin_delegate_leave); - assert_snapshot!(hir_string("class"), @r" - fn class@: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:Class = InvokeBuiltin leaf _bi20, v6 - Jump bb3(v6, v11) - bb3(v13:BasicObject, v14:Class): - CheckInterrupts - Return v14 - "); - } - - #[test] - fn test_invokebuiltin_delegate_with_args() { - // Using an unannotated builtin to test InvokeBuiltin generation - let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("Dir", "open")); - assert!(iseq_contains_opcode(iseq, YARVINSN_opt_invokebuiltin_delegate), "iseq Dir.open does not contain invokebuiltin"); - let function = iseq_to_hir(iseq).unwrap(); - assert_snapshot!(hir_string_function(&function), @r" - fn open@: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@8 - v3:BasicObject = GetLocal l0, SP@7 - v4:BasicObject = GetLocal l0, SP@6 - v5:BasicObject = GetLocal l0, SP@5 - v6:NilClass = Const Value(nil) - Jump bb2(v1, v2, v3, v4, v5, v6) - bb1(v9:BasicObject, v10:BasicObject, v11:BasicObject, v12:BasicObject, v13:BasicObject): - EntryPoint JIT(0) - v14:NilClass = Const Value(nil) - Jump bb2(v9, v10, v11, v12, v13, v14) - bb2(v16:BasicObject, v17:BasicObject, v18:BasicObject, v19:BasicObject, v20:BasicObject, v21:NilClass): - v26:BasicObject = InvokeBuiltin dir_s_open, v16, v17, v18 - PatchPoint NoEPEscape(open) - GuardBlockParamProxy l0 - v33:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1000)) - CheckInterrupts - v36:CBool[true] = Test v33 - IfFalse v36, bb3(v16, v17, v18, v19, v20, v26) - PatchPoint NoEPEscape(open) - v43:BasicObject = InvokeBlock, v26 - v47:BasicObject = InvokeBuiltin dir_s_close, v16, v26 - CheckInterrupts - Return v43 - bb3(v53, v54, v55, v56, v57, v58): - PatchPoint NoEPEscape(open) - CheckInterrupts - Return v58 - "); - } - - #[test] - fn test_invokebuiltin_delegate_without_args() { - let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("GC", "enable")); - assert!(iseq_contains_opcode(iseq, YARVINSN_opt_invokebuiltin_delegate_leave), "iseq GC.enable does not contain invokebuiltin"); - let function = iseq_to_hir(iseq).unwrap(); - assert_snapshot!(hir_string_function(&function), @r" - fn enable@: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:BasicObject = InvokeBuiltin gc_enable, v6 - Jump bb3(v6, v11) - bb3(v13:BasicObject, v14:BasicObject): - CheckInterrupts - Return v14 - "); - } - - #[test] - fn test_invokebuiltin_with_args() { - let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("GC", "start")); - assert!(iseq_contains_opcode(iseq, YARVINSN_invokebuiltin), "iseq GC.start does not contain invokebuiltin"); - let function = iseq_to_hir(iseq).unwrap(); - assert_snapshot!(hir_string_function(&function), @r" - fn start@: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@7 - v3:BasicObject = GetLocal l0, SP@6 - v4:BasicObject = GetLocal l0, SP@5 - v5:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3, v4, v5) - bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject, v11:BasicObject, v12:BasicObject): - EntryPoint JIT(0) - Jump bb2(v8, v9, v10, v11, v12) - bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:BasicObject, v18:BasicObject): - v22:FalseClass = Const Value(false) - v24:BasicObject = InvokeBuiltin gc_start_internal, v14, v15, v16, v17, v22 - CheckInterrupts - Return v24 - "); - } - - #[test] - fn test_invoke_leaf_builtin_symbol_name() { - let iseq = crate::cruby::with_rubyvm(|| get_instance_method_iseq("Symbol", "name")); - let function = iseq_to_hir(iseq).unwrap(); - assert_snapshot!(hir_string_function(&function), @r" - fn name@: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:StringExact = InvokeBuiltin leaf _bi28, v6 - Jump bb3(v6, v11) - bb3(v13:BasicObject, v14:StringExact): - CheckInterrupts - Return v14 - "); - } - - #[test] - fn test_invoke_leaf_builtin_symbol_to_s() { - let iseq = crate::cruby::with_rubyvm(|| get_instance_method_iseq("Symbol", "to_s")); - let function = iseq_to_hir(iseq).unwrap(); - assert_snapshot!(hir_string_function(&function), @r" - fn to_s@: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:StringExact = InvokeBuiltin leaf _bi12, v6 - Jump bb3(v6, v11) - bb3(v13:BasicObject, v14:StringExact): - CheckInterrupts - Return v14 - "); - } - - #[test] - fn dupn() { - eval(" - def test(x) = (x[0, 1] ||= 2) - "); - assert_contains_opcode("test", YARVINSN_dupn); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:NilClass = Const Value(nil) - v14:Fixnum[0] = Const Value(0) - v15:Fixnum[1] = Const Value(1) - v17:BasicObject = SendWithoutBlock v9, :[], v14, v15 - CheckInterrupts - v20:CBool = Test v17 - IfTrue v20, bb3(v8, v9, v13, v9, v14, v15, v17) - v22:Fixnum[2] = Const Value(2) - v24:BasicObject = SendWithoutBlock v9, :[]=, v14, v15, v22 - CheckInterrupts - Return v22 - bb3(v30:BasicObject, v31:BasicObject, v32:NilClass, v33:BasicObject, v34:Fixnum[0], v35:Fixnum[1], v36:BasicObject): - CheckInterrupts - Return v36 - "); - } - - #[test] - fn test_objtostring_anytostring() { - eval(" - def test = \"#{1}\" - "); - assert_contains_opcode("test", YARVINSN_objtostring); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v11:Fixnum[1] = Const Value(1) - v13:BasicObject = ObjToString v11 - v15:String = AnyToString v11, str: v13 - v17:StringExact = StringConcat v10, v15 - CheckInterrupts - Return v17 - "); - } - - #[test] - fn test_string_concat() { - eval(r##" - def test = "#{1}#{2}#{3}" - "##); - assert_contains_opcode("test", YARVINSN_concatstrings); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - v12:BasicObject = ObjToString v10 - v14:String = AnyToString v10, str: v12 - v15:Fixnum[2] = Const Value(2) - v17:BasicObject = ObjToString v15 - v19:String = AnyToString v15, str: v17 - v20:Fixnum[3] = Const Value(3) - v22:BasicObject = ObjToString v20 - v24:String = AnyToString v20, str: v22 - v26:StringExact = StringConcat v14, v19, v24 - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_string_concat_empty() { - eval(r##" - def test = "#{}" - "##); - assert_contains_opcode("test", YARVINSN_concatstrings); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v11:NilClass = Const Value(nil) - v13:BasicObject = ObjToString v11 - v15:String = AnyToString v11, str: v13 - v17:StringExact = StringConcat v10, v15 - CheckInterrupts - Return v17 - "); - } - - #[test] - fn test_toregexp() { - eval(r##" - def test = /#{1}#{2}#{3}/ - "##); - assert_contains_opcode("test", YARVINSN_toregexp); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - v12:BasicObject = ObjToString v10 - v14:String = AnyToString v10, str: v12 - v15:Fixnum[2] = Const Value(2) - v17:BasicObject = ObjToString v15 - v19:String = AnyToString v15, str: v17 - v20:Fixnum[3] = Const Value(3) - v22:BasicObject = ObjToString v20 - v24:String = AnyToString v20, str: v22 - v26:RegexpExact = ToRegexp v14, v19, v24 - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_toregexp_with_options() { - eval(r##" - def test = /#{1}#{2}/mixn - "##); - assert_contains_opcode("test", YARVINSN_toregexp); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - v12:BasicObject = ObjToString v10 - v14:String = AnyToString v10, str: v12 - v15:Fixnum[2] = Const Value(2) - v17:BasicObject = ObjToString v15 - v19:String = AnyToString v15, str: v17 - v21:RegexpExact = ToRegexp v14, v19, MULTILINE|IGNORECASE|EXTENDED|NOENCODING - CheckInterrupts - Return v21 - "); - } - - #[test] - fn throw() { - eval(" - define_method(:throw_return) { return 1 } - define_method(:throw_break) { break 2 } - "); - assert_contains_opcode("throw_return", YARVINSN_throw); - assert_contains_opcode("throw_break", YARVINSN_throw); - assert_snapshot!(hir_strings!("throw_return", "throw_break"), @r" - fn block in @:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v12:Fixnum[1] = Const Value(1) - Throw TAG_RETURN, v12 - - fn block in @:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v12:Fixnum[2] = Const Value(2) - Throw TAG_BREAK, v12 - "); - } - - #[test] - fn test_invokeblock() { - eval(r#" - def test - yield - end - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:BasicObject = InvokeBlock - CheckInterrupts - Return v11 - "); - } - - #[test] - fn test_invokeblock_with_args() { - eval(r#" - def test(x, y) - yield x, y - end - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v17:BasicObject = InvokeBlock, v11, v12 - CheckInterrupts - Return v17 - "); - } - - #[test] - fn test_expandarray_no_splat() { - eval(r#" - def test(o) - a, b = o - end - "#); - assert_contains_opcode("test", YARVINSN_expandarray); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@6 - v3:NilClass = Const Value(nil) - v4:NilClass = Const Value(nil) - Jump bb2(v1, v2, v3, v4) - bb1(v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - v9:NilClass = Const Value(nil) - v10:NilClass = Const Value(nil) - Jump bb2(v7, v8, v9, v10) - bb2(v12:BasicObject, v13:BasicObject, v14:NilClass, v15:NilClass): - v20:ArrayExact = GuardType v13, ArrayExact - v21:CInt64 = ArrayLength v20 - v22:CInt64[2] = GuardBitEquals v21, CInt64(2) - v23:Fixnum[1] = Const Value(1) - v24:BasicObject = ArrayArefFixnum v20, v23 - v25:Fixnum[0] = Const Value(0) - v26:BasicObject = ArrayArefFixnum v20, v25 - PatchPoint NoEPEscape(test) - CheckInterrupts - Return v13 - "); - } - - #[test] - fn test_expandarray_splat() { - eval(r#" - def test(o) - a, *b = o - end - "#); - assert_contains_opcode("test", YARVINSN_expandarray); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@6 - v3:NilClass = Const Value(nil) - v4:NilClass = Const Value(nil) - Jump bb2(v1, v2, v3, v4) - bb1(v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - v9:NilClass = Const Value(nil) - v10:NilClass = Const Value(nil) - Jump bb2(v7, v8, v9, v10) - bb2(v12:BasicObject, v13:BasicObject, v14:NilClass, v15:NilClass): - SideExit UnhandledYARVInsn(expandarray) - "); - } - - #[test] - fn test_expandarray_splat_post() { - eval(r#" - def test(o) - a, *b, c = o - end - "#); - assert_contains_opcode("test", YARVINSN_expandarray); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@7 - v3:NilClass = Const Value(nil) - v4:NilClass = Const Value(nil) - v5:NilClass = Const Value(nil) - Jump bb2(v1, v2, v3, v4, v5) - bb1(v8:BasicObject, v9:BasicObject): - EntryPoint JIT(0) - v10:NilClass = Const Value(nil) - v11:NilClass = Const Value(nil) - v12:NilClass = Const Value(nil) - Jump bb2(v8, v9, v10, v11, v12) - bb2(v14:BasicObject, v15:BasicObject, v16:NilClass, v17:NilClass, v18:NilClass): - SideExit UnhandledYARVInsn(expandarray) - "); - } -} - -#[cfg(test)] -mod graphviz_tests { - use super::*; - use insta::assert_snapshot; - - #[track_caller] - fn hir_string(method: &str) -> String { - let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("self", method)); - unsafe { crate::cruby::rb_zjit_profile_disable(iseq) }; - let mut function = iseq_to_hir(iseq).unwrap(); - function.optimize(); - function.validate().unwrap(); - format!("{}", FunctionGraphvizPrinter::new(&function)) - } - - #[test] - fn test_guard_fixnum_or_fixnum() { - eval(r#" - def test(x, y) = x | y - - test(1, 2) - "#); - assert_snapshot!(hir_string("test"), @r#" - digraph G { # test@<compiled>:2 - node [shape=plaintext]; - mode=hier; overlap=false; splines=true; - bb0 [label=< - - - - - - -
bb0() 
EntryPoint interpreter 
v1:BasicObject = LoadSelf 
v2:BasicObject = GetLocal l0, SP@5 
v3:BasicObject = GetLocal l0, SP@4 
Jump bb2(v1, v2, v3) 
>]; - bb0:v4 -> bb2:params:n; - bb1 [label=< - - - -
bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject) 
EntryPoint JIT(0) 
Jump bb2(v6, v7, v8) 
>]; - bb1:v9 -> bb2:params:n; - bb2 [label=< - - - - - - - - - - -
bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject) 
PatchPoint NoTracePoint 
PatchPoint NoTracePoint 
PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, 29) 
v26:Fixnum = GuardType v11, Fixnum 
v27:Fixnum = GuardType v12, Fixnum 
v28:Fixnum = FixnumOr v26, v27 
PatchPoint NoTracePoint 
CheckInterrupts 
Return v28 
>]; - } - "#); - } - - #[test] - fn test_multiple_blocks() { - eval(r#" - def test(c) - if c - 3 - else - 4 - end - end - - test(1) - test("x") - "#); - assert_snapshot!(hir_string("test"), @r#" - digraph G { # test@<compiled>:3 - node [shape=plaintext]; - mode=hier; overlap=false; splines=true; - bb0 [label=< - - - - - -
bb0() 
EntryPoint interpreter 
v1:BasicObject = LoadSelf 
v2:BasicObject = GetLocal l0, SP@4 
Jump bb2(v1, v2) 
>]; - bb0:v3 -> bb2:params:n; - bb1 [label=< - - - -
bb1(v5:BasicObject, v6:BasicObject) 
EntryPoint JIT(0) 
Jump bb2(v5, v6) 
>]; - bb1:v7 -> bb2:params:n; - bb2 [label=< - - - - - - - - - - -
bb2(v8:BasicObject, v9:BasicObject) 
PatchPoint NoTracePoint 
CheckInterrupts 
v15:CBool = Test v9 
IfFalse v15, bb3(v8, v9) 
PatchPoint NoTracePoint 
v19:Fixnum[3] = Const Value(3) 
PatchPoint NoTracePoint 
CheckInterrupts 
Return v19 
>]; - bb2:v16 -> bb3:params:n; - bb3 [label=< - - - - - - -
bb3(v25:BasicObject, v26:BasicObject) 
PatchPoint NoTracePoint 
v30:Fixnum[4] = Const Value(4) 
PatchPoint NoTracePoint 
CheckInterrupts 
Return v30 
>]; - } - "#); - } -} - -#[cfg(test)] -mod opt_tests { - use super::*; - use crate::{hir_strings, options::*}; - use insta::assert_snapshot; - use super::tests::assert_contains_opcode; - - #[track_caller] - fn hir_string_function(function: &Function) -> String { - format!("{}", FunctionPrinter::without_snapshot(function)) - } - - #[track_caller] - fn hir_string_proc(proc: &str) -> String { - let iseq = crate::cruby::with_rubyvm(|| get_proc_iseq(proc)); - unsafe { crate::cruby::rb_zjit_profile_disable(iseq) }; - let mut function = iseq_to_hir(iseq).unwrap(); - function.optimize(); - function.validate().unwrap(); - hir_string_function(&function) - } - - #[track_caller] - fn hir_string(method: &str) -> String { - hir_string_proc(&format!("{}.method(:{})", "self", method)) - } - - #[test] - fn test_fold_iftrue_away() { - eval(" - def test - cond = true - if cond - 3 - else - 4 - end - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v13:TrueClass = Const Value(true) - CheckInterrupts - v22:Fixnum[3] = Const Value(3) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_fold_iftrue_into_jump() { - eval(" - def test - cond = false - if cond - 3 - else - 4 - end - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v13:FalseClass = Const Value(false) - CheckInterrupts - v33:Fixnum[4] = Const Value(4) - CheckInterrupts - Return v33 - "); - } - - #[test] - fn test_fold_fixnum_add() { - eval(" - def test - 1 + 2 + 3 - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - v11:Fixnum[2] = Const Value(2) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) - v30:Fixnum[3] = Const Value(3) - v16:Fixnum[3] = Const Value(3) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) - v31:Fixnum[6] = Const Value(6) - CheckInterrupts - Return v31 - "); - } - - #[test] - fn test_fold_fixnum_sub() { - eval(" - def test - 5 - 3 - 1 - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[5] = Const Value(5) - v11:Fixnum[3] = Const Value(3) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MINUS) - v30:Fixnum[2] = Const Value(2) - v16:Fixnum[1] = Const Value(1) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MINUS) - v31:Fixnum[1] = Const Value(1) - CheckInterrupts - Return v31 - "); - } - - #[test] - fn test_fold_fixnum_sub_large_negative_result() { - eval(" - def test - 0 - 1073741825 - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[0] = Const Value(0) - v11:Fixnum[1073741825] = Const Value(1073741825) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MINUS) - v23:Fixnum[-1073741825] = Const Value(-1073741825) - CheckInterrupts - Return v23 - "); - } - - #[test] - fn test_fold_fixnum_mult() { - eval(" - def test - 6 * 7 - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[6] = Const Value(6) - v11:Fixnum[7] = Const Value(7) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MULT) - v23:Fixnum[42] = Const Value(42) - CheckInterrupts - Return v23 - "); - } - - #[test] - fn test_fold_fixnum_mult_zero() { - eval(" - def test(n) - 0 * n + n * 0 - end - test 1; test 2 - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[0] = Const Value(0) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MULT) - v33:Fixnum = GuardType v9, Fixnum - v40:Fixnum[0] = Const Value(0) - v18:Fixnum[0] = Const Value(0) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MULT) - v36:Fixnum = GuardType v9, Fixnum - v41:Fixnum[0] = Const Value(0) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) - v42:Fixnum[0] = Const Value(0) - CheckInterrupts - Return v42 - "); - } - - #[test] - fn test_fold_fixnum_less() { - eval(" - def test - if 1 < 2 - 3 - else - 4 - end - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - v11:Fixnum[2] = Const Value(2) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LT) - v40:TrueClass = Const Value(true) - CheckInterrupts - v22:Fixnum[3] = Const Value(3) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_fold_fixnum_less_equal() { - eval(" - def test - if 1 <= 2 && 2 <= 2 - 3 - else - 4 - end - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - v11:Fixnum[2] = Const Value(2) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LE) - v52:TrueClass = Const Value(true) - CheckInterrupts - v20:Fixnum[2] = Const Value(2) - v21:Fixnum[2] = Const Value(2) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LE) - v54:TrueClass = Const Value(true) - CheckInterrupts - v32:Fixnum[3] = Const Value(3) - CheckInterrupts - Return v32 - "); - } - - #[test] - fn test_fold_fixnum_greater() { - eval(" - def test - if 2 > 1 - 3 - else - 4 - end - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[2] = Const Value(2) - v11:Fixnum[1] = Const Value(1) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_GT) - v40:TrueClass = Const Value(true) - CheckInterrupts - v22:Fixnum[3] = Const Value(3) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_fold_fixnum_greater_equal() { - eval(" - def test - if 2 >= 1 && 2 >= 2 - 3 - else - 4 - end - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[2] = Const Value(2) - v11:Fixnum[1] = Const Value(1) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_GE) - v52:TrueClass = Const Value(true) - CheckInterrupts - v20:Fixnum[2] = Const Value(2) - v21:Fixnum[2] = Const Value(2) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_GE) - v54:TrueClass = Const Value(true) - CheckInterrupts - v32:Fixnum[3] = Const Value(3) - CheckInterrupts - Return v32 - "); - } - - #[test] - fn test_fold_fixnum_eq_false() { - eval(" - def test - if 1 == 2 - 3 - else - 4 - end - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - v11:Fixnum[2] = Const Value(2) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_EQ) - v40:FalseClass = Const Value(false) - CheckInterrupts - v32:Fixnum[4] = Const Value(4) - CheckInterrupts - Return v32 - "); - } - - #[test] - fn test_fold_fixnum_eq_true() { - eval(" - def test - if 2 == 2 - 3 - else - 4 - end - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[2] = Const Value(2) - v11:Fixnum[2] = Const Value(2) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_EQ) - v40:TrueClass = Const Value(true) - CheckInterrupts - v22:Fixnum[3] = Const Value(3) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_fold_fixnum_neq_true() { - eval(" - def test - if 1 != 2 - 3 - else - 4 - end - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - v11:Fixnum[2] = Const Value(2) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_EQ) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_NEQ) - v41:TrueClass = Const Value(true) - CheckInterrupts - v22:Fixnum[3] = Const Value(3) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_fold_fixnum_neq_false() { - eval(" - def test - if 2 != 2 - 3 - else - 4 - end - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[2] = Const Value(2) - v11:Fixnum[2] = Const Value(2) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_EQ) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_NEQ) - v41:FalseClass = Const Value(false) - CheckInterrupts - v32:Fixnum[4] = Const Value(4) - CheckInterrupts - Return v32 - "); - } - - #[test] - fn test_replace_guard_if_known_fixnum() { - eval(" - def test(a) - a + 1 - end - test(2); test(3) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[1] = Const Value(1) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) - v24:Fixnum = GuardType v9, Fixnum - v25:Fixnum = FixnumAdd v24, v13 - CheckInterrupts - Return v25 - "); - } - - #[test] - fn test_param_forms_get_bb_param() { - eval(" - def rest(*array) = array - def kw(k:) = k - def kw_rest(**k) = k - def post(*rest, post) = post - def block(&b) = nil - "); - assert_snapshot!(hir_strings!("rest", "kw", "kw_rest", "block", "post"), @r" - fn rest@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:ArrayExact = GetLocal l0, SP@4, * - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:ArrayExact): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:ArrayExact): - CheckInterrupts - Return v9 - - fn kw@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - CheckInterrupts - Return v11 - - fn kw_rest@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - CheckInterrupts - Return v9 - - fn block@:6: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:NilClass = Const Value(nil) - CheckInterrupts - Return v13 - - fn post@:5: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:ArrayExact = GetLocal l0, SP@5, * - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:ArrayExact, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:ArrayExact, v12:BasicObject): - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_optimize_top_level_call_into_send_direct() { - eval(" - def foo = [] - def test - foo - end - test; test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - v20:BasicObject = SendWithoutBlockDirect v19, :foo (0x1038) - CheckInterrupts - Return v20 - "); - } - - #[test] - fn test_optimize_nonexistent_top_level_call() { - eval(" - def foo - end - def test - foo - end - test; test - undef :foo - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:5: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:BasicObject = SendWithoutBlock v6, :foo - CheckInterrupts - Return v11 - "); - } - - #[test] - fn test_optimize_private_top_level_call() { - eval(" - def foo = [] - private :foo - def test - foo - end - test; test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:5: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - v20:BasicObject = SendWithoutBlockDirect v19, :foo (0x1038) - CheckInterrupts - Return v20 - "); - } - - #[test] - fn test_optimize_top_level_call_with_overloaded_cme() { - eval(" - def test - Integer(3) - end - test; test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Object@0x1000, Integer@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - v21:BasicObject = SendWithoutBlockDirect v20, :Integer (0x1038), v10 - CheckInterrupts - Return v21 - "); - } - - #[test] - fn test_optimize_top_level_call_with_args_into_send_direct() { - eval(" - def foo(a, b) = [] - def test - foo 1, 2 - end - test; test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - v11:Fixnum[2] = Const Value(2) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v21:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - v22:BasicObject = SendWithoutBlockDirect v21, :foo (0x1038), v10, v11 - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_optimize_top_level_sends_into_send_direct() { - eval(" - def foo = [] - def bar = [] - def test - foo - bar - end - test; test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:5: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v23:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - v24:BasicObject = SendWithoutBlockDirect v23, :foo (0x1038) - PatchPoint MethodRedefined(Object@0x1000, bar@0x1040, cme:0x1048) - PatchPoint NoSingletonClass(Object@0x1000) - v27:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - v28:BasicObject = SendWithoutBlockDirect v27, :bar (0x1038) - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_optimize_variadic_ccall() { - eval(" - def test - puts 'Hello' - end - test; test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v12:StringExact = StringCopy v10 - PatchPoint MethodRedefined(Object@0x1008, puts@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(Object@0x1008) - v23:HeapObject[class_exact*:Object@VALUE(0x1008)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1008)] - v24:BasicObject = CCallVariadic puts@0x1040, v23, v12 - CheckInterrupts - Return v24 - "); - } - - #[test] - fn test_dont_optimize_fixnum_add_if_redefined() { - eval(" - class Integer - def +(other) - 100 - end - end - def test(a, b) = a + b - test(1,2); test(3,4) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:7: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :+, v12 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_optimize_send_into_fixnum_add_both_profiled() { - eval(" - def test(a, b) = a + b - test(1,2); test(3,4) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) - v26:Fixnum = GuardType v11, Fixnum - v27:Fixnum = GuardType v12, Fixnum - v28:Fixnum = FixnumAdd v26, v27 - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_optimize_send_into_fixnum_add_left_profiled() { - eval(" - def test(a) = a + 1 - test(1); test(3) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[1] = Const Value(1) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) - v24:Fixnum = GuardType v9, Fixnum - v25:Fixnum = FixnumAdd v24, v13 - CheckInterrupts - Return v25 - "); - } - - #[test] - fn test_optimize_send_into_fixnum_add_right_profiled() { - eval(" - def test(a) = 1 + a - test(1); test(3) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[1] = Const Value(1) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) - v24:Fixnum = GuardType v9, Fixnum - v25:Fixnum = FixnumAdd v13, v24 - CheckInterrupts - Return v25 - "); - } - - #[test] - fn test_optimize_send_into_fixnum_lt_both_profiled() { - eval(" - def test(a, b) = a < b - test(1,2); test(3,4) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LT) - v26:Fixnum = GuardType v11, Fixnum - v27:Fixnum = GuardType v12, Fixnum - v28:BoolExact = FixnumLt v26, v27 - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_optimize_send_into_fixnum_lt_left_profiled() { - eval(" - def test(a) = a < 1 - test(1); test(3) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[1] = Const Value(1) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LT) - v24:Fixnum = GuardType v9, Fixnum - v25:BoolExact = FixnumLt v24, v13 - CheckInterrupts - Return v25 - "); - } - - #[test] - fn test_optimize_send_into_fixnum_lt_right_profiled() { - eval(" - def test(a) = 1 < a - test(1); test(3) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[1] = Const Value(1) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LT) - v24:Fixnum = GuardType v9, Fixnum - v25:BoolExact = FixnumLt v13, v24 - CheckInterrupts - Return v25 - "); - } - - #[test] - fn test_optimize_new_range_fixnum_inclusive_literals() { - eval(" - def test() - a = 2 - (1..a) - end - test; test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v13:Fixnum[2] = Const Value(2) - v16:Fixnum[1] = Const Value(1) - v24:RangeExact = NewRangeFixnum v16 NewRangeInclusive v13 - CheckInterrupts - Return v24 - "); - } - - - #[test] - fn test_optimize_new_range_fixnum_exclusive_literals() { - eval(" - def test() - a = 2 - (1...a) - end - test; test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v13:Fixnum[2] = Const Value(2) - v16:Fixnum[1] = Const Value(1) - v24:RangeExact = NewRangeFixnum v16 NewRangeExclusive v13 - CheckInterrupts - Return v24 - "); - } - - #[test] - fn test_optimize_new_range_fixnum_inclusive_high_guarded() { - eval(" - def test(a) - (1..a) - end - test(2); test(3) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[1] = Const Value(1) - v21:Fixnum = GuardType v9, Fixnum - v22:RangeExact = NewRangeFixnum v13 NewRangeInclusive v21 - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_optimize_new_range_fixnum_exclusive_high_guarded() { - eval(" - def test(a) - (1...a) - end - test(2); test(3) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[1] = Const Value(1) - v21:Fixnum = GuardType v9, Fixnum - v22:RangeExact = NewRangeFixnum v13 NewRangeExclusive v21 - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_optimize_new_range_fixnum_inclusive_low_guarded() { - eval(" - def test(a) - (a..10) - end - test(2); test(3) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[10] = Const Value(10) - v21:Fixnum = GuardType v9, Fixnum - v22:RangeExact = NewRangeFixnum v21 NewRangeInclusive v13 - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_optimize_new_range_fixnum_exclusive_low_guarded() { - eval(" - def test(a) - (a...10) - end - test(2); test(3) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[10] = Const Value(10) - v21:Fixnum = GuardType v9, Fixnum - v22:RangeExact = NewRangeFixnum v21 NewRangeExclusive v13 - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_eliminate_new_array() { - eval(" - def test() - c = [] - 5 - end - test; test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v14:ArrayExact = NewArray - v17:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v17 - "); - } - - #[test] - fn test_opt_aref_array() { - eval(" - arr = [1,2,3] - def test(arr) = arr[0] - test(arr) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[0] = Const Value(0) - PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - v26:ArrayExact = GuardType v9, ArrayExact - v27:BasicObject = ArrayArefFixnum v26, v13 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v27 - "); - assert_snapshot!(inspect("test [1,2,3]"), @"1"); - } - - #[test] - fn test_opt_aref_hash() { - eval(" - arr = {0 => 4} - def test(arr) = arr[0] - test(arr) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[0] = Const Value(0) - PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Hash@0x1000) - v26:HashExact = GuardType v9, HashExact - v27:BasicObject = HashAref v26, v13 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v27 - "); - assert_snapshot!(inspect("test({0 => 4})"), @"4"); - } - - #[test] - fn test_eliminate_new_range() { - eval(" - def test() - c = (1..2) - 5 - end - test; test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v13:RangeExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v16:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v16 - "); - } - - #[test] - fn test_do_not_eliminate_new_range_non_fixnum() { - eval(" - def test() - _ = (-'a'..'b') - 0 - end - test; test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS) - v15:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v16:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v18:StringExact = StringCopy v16 - v20:RangeExact = NewRange v15 NewRangeInclusive v18 - PatchPoint NoEPEscape(test) - v25:Fixnum[0] = Const Value(0) - CheckInterrupts - Return v25 - "); - } - - #[test] - fn test_eliminate_new_array_with_elements() { - eval(" - def test(a) - c = [a] - 5 - end - test(1); test(2) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:NilClass = Const Value(nil) - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject): - EntryPoint JIT(0) - v8:NilClass = Const Value(nil) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:NilClass): - v17:ArrayExact = NewArray v11 - v20:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v20 - "); - } - - #[test] - fn test_eliminate_new_hash() { - eval(" - def test() - c = {} - 5 - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v14:HashExact = NewHash - PatchPoint NoEPEscape(test) - v19:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_no_eliminate_new_hash_with_elements() { - eval(" - def test(aval, bval) - c = {a: aval, b: bval} - 5 - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@6 - v3:BasicObject = GetLocal l0, SP@5 - v4:NilClass = Const Value(nil) - Jump bb2(v1, v2, v3, v4) - bb1(v7:BasicObject, v8:BasicObject, v9:BasicObject): - EntryPoint JIT(0) - v10:NilClass = Const Value(nil) - Jump bb2(v7, v8, v9, v10) - bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:NilClass): - v19:StaticSymbol[:a] = Const Value(VALUE(0x1000)) - v20:StaticSymbol[:b] = Const Value(VALUE(0x1008)) - v22:HashExact = NewHash v19: v13, v20: v14 - PatchPoint NoEPEscape(test) - v27:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v27 - "); - } - - #[test] - fn test_eliminate_array_dup() { - eval(" - def test - c = [1, 2] - 5 - end - test; test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v13:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v15:ArrayExact = ArrayDup v13 - v18:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v18 - "); - } - - #[test] - fn test_eliminate_hash_dup() { - eval(" - def test - c = {a: 1, b: 2} - 5 - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v13:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v15:HashExact = HashDup v13 - v18:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v18 - "); - } - - #[test] - fn test_eliminate_putself() { - eval(" - def test() - c = self - 5 - end - test; test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v15:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v15 - "); - } - - #[test] - fn test_eliminate_string_copy() { - eval(r#" - def test() - c = "abc" - 5 - end - test; test - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v15:StringExact = StringCopy v13 - v18:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v18 - "); - } - - #[test] - fn test_eliminate_fixnum_add() { - eval(" - def test(a, b) - a + b - 5 - end - test(1, 2); test(3, 4) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) - v29:Fixnum = GuardType v11, Fixnum - v30:Fixnum = GuardType v12, Fixnum - v22:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_eliminate_fixnum_sub() { - eval(" - def test(a, b) - a - b - 5 - end - test(1, 2); test(3, 4) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MINUS) - v29:Fixnum = GuardType v11, Fixnum - v30:Fixnum = GuardType v12, Fixnum - v22:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_eliminate_fixnum_mul() { - eval(" - def test(a, b) - a * b - 5 - end - test(1, 2); test(3, 4) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MULT) - v29:Fixnum = GuardType v11, Fixnum - v30:Fixnum = GuardType v12, Fixnum - v22:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_do_not_eliminate_fixnum_div() { - eval(" - def test(a, b) - a / b - 5 - end - test(1, 2); test(3, 4) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_DIV) - v29:Fixnum = GuardType v11, Fixnum - v30:Fixnum = GuardType v12, Fixnum - v31:Fixnum = FixnumDiv v29, v30 - v22:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_do_not_eliminate_fixnum_mod() { - eval(" - def test(a, b) - a % b - 5 - end - test(1, 2); test(3, 4) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MOD) - v29:Fixnum = GuardType v11, Fixnum - v30:Fixnum = GuardType v12, Fixnum - v31:Fixnum = FixnumMod v29, v30 - v22:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_eliminate_fixnum_lt() { - eval(" - def test(a, b) - a < b - 5 - end - test(1, 2); test(3, 4) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LT) - v29:Fixnum = GuardType v11, Fixnum - v30:Fixnum = GuardType v12, Fixnum - v22:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_eliminate_fixnum_le() { - eval(" - def test(a, b) - a <= b - 5 - end - test(1, 2); test(3, 4) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LE) - v29:Fixnum = GuardType v11, Fixnum - v30:Fixnum = GuardType v12, Fixnum - v22:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_eliminate_fixnum_gt() { - eval(" - def test(a, b) - a > b - 5 - end - test(1, 2); test(3, 4) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_GT) - v29:Fixnum = GuardType v11, Fixnum - v30:Fixnum = GuardType v12, Fixnum - v22:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_eliminate_fixnum_ge() { - eval(" - def test(a, b) - a >= b - 5 - end - test(1, 2); test(3, 4) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_GE) - v29:Fixnum = GuardType v11, Fixnum - v30:Fixnum = GuardType v12, Fixnum - v22:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_eliminate_fixnum_eq() { - eval(" - def test(a, b) - a == b - 5 - end - test(1, 2); test(3, 4) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_EQ) - v29:Fixnum = GuardType v11, Fixnum - v30:Fixnum = GuardType v12, Fixnum - v22:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_eliminate_fixnum_neq() { - eval(" - def test(a, b) - a != b - 5 - end - test(1, 2); test(3, 4) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_EQ) - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_NEQ) - v30:Fixnum = GuardType v11, Fixnum - v31:Fixnum = GuardType v12, Fixnum - v22:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_do_not_eliminate_get_constant_path() { - eval(" - def test() - C - 5 - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:BasicObject = GetConstantPath 0x1000 - v14:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v14 - "); - } - - #[test] - fn kernel_itself_const() { - eval(" - def test(x) = x.itself - test(0) # profile - test(1) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Integer@0x1000, itself@0x1008, cme:0x1010) - v22:Fixnum = GuardType v9, Fixnum - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v22 - "); - } - - #[test] - fn kernel_itself_known_type() { - eval(" - def test = [].itself - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v11 - "); - } - - #[test] - fn eliminate_kernel_itself() { - eval(" - def test - x = [].itself - 1 - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v14:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - IncrCounter inline_cfunc_optimized_send_count - PatchPoint NoEPEscape(test) - v21:Fixnum[1] = Const Value(1) - CheckInterrupts - Return v21 - "); - } - - #[test] - fn eliminate_module_name() { - eval(" - module M; end - def test - x = M.name - 1 - end - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, M) - v29:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(Module@0x1010, name@0x1018, cme:0x1020) - PatchPoint NoSingletonClass(Module@0x1010) - IncrCounter inline_cfunc_optimized_send_count - v34:StringExact|NilClass = CCall name@0x1048, v29 - PatchPoint NoEPEscape(test) - v21:Fixnum[1] = Const Value(1) - CheckInterrupts - Return v21 - "); - } - - #[test] - fn eliminate_array_length() { - eval(" - def test - x = [].length - 5 - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v14:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - IncrCounter inline_cfunc_optimized_send_count - v31:Fixnum = CCall length@0x1038, v14 - v21:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v21 - "); - } - - #[test] - fn normal_class_type_inference() { - eval(" - class C; end - def test = C - test # Warm the constant cache - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, C) - v19:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - CheckInterrupts - Return v19 - "); - } - - #[test] - fn core_classes_type_inference() { - eval(" - def test = [String, Class, Module, BasicObject] - test # Warm the constant cache - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, String) - v27:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1010, Class) - v30:Class[VALUE(0x1018)] = Const Value(VALUE(0x1018)) - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1020, Module) - v33:Class[VALUE(0x1028)] = Const Value(VALUE(0x1028)) - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1030, BasicObject) - v36:Class[VALUE(0x1038)] = Const Value(VALUE(0x1038)) - v19:ArrayExact = NewArray v27, v30, v33, v36 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn module_instances_are_module_exact() { - eval(" - def test = [Enumerable, Kernel] - test # Warm the constant cache - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, Enumerable) - v23:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1010, Kernel) - v26:ModuleExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) - v15:ArrayExact = NewArray v23, v26 - CheckInterrupts - Return v15 - "); - } - - #[test] - fn module_subclasses_are_not_module_exact() { - eval(" - class ModuleSubclass < Module; end - MY_MODULE = ModuleSubclass.new - def test = MY_MODULE - test # Warm the constant cache - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, MY_MODULE) - v19:ModuleSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - CheckInterrupts - Return v19 - "); - } - - #[test] - fn eliminate_array_size() { - eval(" - def test - x = [].size - 5 - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v14:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - IncrCounter inline_cfunc_optimized_send_count - v31:Fixnum = CCall size@0x1038, v14 - v21:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v21 - "); - } - - #[test] - fn kernel_itself_argc_mismatch() { - eval(" - def test = 1.itself(0) - test rescue 0 - test rescue 0 - "); - // Not specialized - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - v11:Fixnum[0] = Const Value(0) - v13:BasicObject = SendWithoutBlock v10, :itself, v11 - CheckInterrupts - Return v13 - "); - } - - #[test] - fn test_inline_kernel_block_given_p() { - eval(" - def test = block_given? - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - v21:BoolExact = IsBlockGiven - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v21 - "); - } - - #[test] - fn test_inline_kernel_block_given_p_in_block() { - eval(" - TEST = proc { block_given? } - TEST.call - "); - assert_snapshot!(hir_string_proc("TEST"), @r" - fn block in @:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - v21:BoolExact = IsBlockGiven - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v21 - "); - } - - #[test] - fn test_elide_kernel_block_given_p() { - eval(" - def test - block_given? - 5 - end - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v23:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - IncrCounter inline_cfunc_optimized_send_count - v14:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v14 - "); - } - - #[test] - fn const_send_direct_integer() { - eval(" - def test(x) = 1.zero? - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Integer@0x1000, zero?@0x1008, cme:0x1010) - IncrCounter inline_iseq_optimized_send_count - v24:BasicObject = InvokeBuiltin leaf _bi285, v13 - CheckInterrupts - Return v24 - "); - } - - #[test] - fn class_known_send_direct_array() { - eval(" - def test(x) - a = [1,2,3] - a.first - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:NilClass = Const Value(nil) - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject): - EntryPoint JIT(0) - v8:NilClass = Const Value(nil) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:NilClass): - v16:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v18:ArrayExact = ArrayDup v16 - PatchPoint MethodRedefined(Array@0x1008, first@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(Array@0x1008) - IncrCounter inline_iseq_optimized_send_count - v32:BasicObject = InvokeBuiltin leaf _bi132, v18 - CheckInterrupts - Return v32 - "); - } - - #[test] - fn send_direct_to_module() { - eval(" - module M; end - def test = M.class - test - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, M) - v21:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(Module@0x1010, class@0x1018, cme:0x1020) - PatchPoint NoSingletonClass(Module@0x1010) - IncrCounter inline_iseq_optimized_send_count - v26:Class = InvokeBuiltin leaf _bi20, v21 - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_send_direct_to_instance_method() { - eval(" - class C - def foo = [] - end - - def test(c) = c.foo - c = C.new - test c - test c - "); - - assert_snapshot!(hir_string("test"), @r" - fn test@:6: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(C@0x1000) - v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - v23:BasicObject = SendWithoutBlockDirect v22, :foo (0x1038) - CheckInterrupts - Return v23 - "); - } - - #[test] - fn dont_specialize_call_to_iseq_with_opt() { - eval(" - def foo(arg=1) = 1 - def test = foo 1 - test - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - v12:BasicObject = SendWithoutBlock v6, :foo, v10 - CheckInterrupts - Return v12 - "); - } - - #[test] - fn dont_specialize_call_to_iseq_with_block() { - eval(" - def foo(&block) = 1 - def test = foo {|| } - test - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:BasicObject = Send v6, 0x1000, :foo - CheckInterrupts - Return v11 - "); - } - - #[test] - fn reload_local_across_send() { - eval(" - def foo(&block) = 1 - def test - a = 1 - foo {|| } - a - end - test - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v13:Fixnum[1] = Const Value(1) - SetLocal l0, EP@3, v13 - v18:BasicObject = Send v8, 0x1000, :foo - v19:BasicObject = GetLocal l0, EP@3 - v22:BasicObject = GetLocal l0, EP@3 - CheckInterrupts - Return v22 - "); - } - - #[test] - fn dont_specialize_call_to_iseq_with_rest() { - eval(" - def foo(*args) = 1 - def test = foo 1 - test - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - v12:BasicObject = SendWithoutBlock v6, :foo, v10 - CheckInterrupts - Return v12 - "); - } - - #[test] - fn dont_specialize_call_to_iseq_with_kw() { - eval(" - def foo(a:) = 1 - def test = foo(a: 1) - test - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - SideExit UnhandledCallType(Kwarg) - "); - } - - #[test] - fn dont_specialize_call_to_iseq_with_kwrest() { - eval(" - def foo(**args) = 1 - def test = foo(a: 1) - test - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - SideExit UnhandledCallType(Kwarg) - "); - } - - #[test] - fn string_bytesize_simple() { - eval(" - def test = 'abc'.bytesize - test - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v12:StringExact = StringCopy v10 - PatchPoint MethodRedefined(String@0x1008, bytesize@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(String@0x1008) - IncrCounter inline_cfunc_optimized_send_count - v24:Fixnum = CCall bytesize@0x1040, v12 - CheckInterrupts - Return v24 - "); - } - - #[test] - fn dont_replace_get_constant_path_with_empty_ic() { - eval(" - def test = Kernel - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:BasicObject = GetConstantPath 0x1000 - CheckInterrupts - Return v11 - "); - } - - #[test] - fn dont_replace_get_constant_path_with_invalidated_ic() { - eval(" - def test = Kernel - test - Kernel = 5 - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:BasicObject = GetConstantPath 0x1000 - CheckInterrupts - Return v11 - "); - } - - #[test] - fn replace_get_constant_path_with_const() { - eval(" - def test = Kernel - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, Kernel) - v19:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - CheckInterrupts - Return v19 - "); - } - - #[test] - fn replace_nested_get_constant_path_with_const() { - eval(" - module Foo - module Bar - class C - end - end - end - def test = Foo::Bar::C - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:8: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, Foo::Bar::C) - v19:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_opt_new_no_initialize() { - eval(" - class C; end - def test = C.new - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, C) - v40:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v12:NilClass = Const Value(nil) - PatchPoint MethodRedefined(C@0x1008, new@0x1010, cme:0x1018) - v43:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) - PatchPoint MethodRedefined(C@0x1008, initialize@0x1040, cme:0x1048) - PatchPoint NoSingletonClass(C@0x1008) - v47:NilClass = Const Value(nil) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - CheckInterrupts - Return v43 - "); - } - - #[test] - fn test_opt_new_initialize() { - eval(" - class C - def initialize x - @x = x - end - end - def test = C.new 1 - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:7: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, C) - v42:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v12:NilClass = Const Value(nil) - v13:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(C@0x1008, new@0x1010, cme:0x1018) - v45:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) - PatchPoint MethodRedefined(C@0x1008, initialize@0x1040, cme:0x1048) - PatchPoint NoSingletonClass(C@0x1008) - v48:BasicObject = SendWithoutBlockDirect v45, :initialize (0x1070), v13 - CheckInterrupts - CheckInterrupts - Return v45 - "); - } - - #[test] - fn test_opt_new_object() { - eval(" - def test = Object.new - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, Object) - v40:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v12:NilClass = Const Value(nil) - PatchPoint MethodRedefined(Object@0x1008, new@0x1010, cme:0x1018) - v43:ObjectExact = ObjectAllocClass Object:VALUE(0x1008) - PatchPoint MethodRedefined(Object@0x1008, initialize@0x1040, cme:0x1048) - PatchPoint NoSingletonClass(Object@0x1008) - v47:NilClass = Const Value(nil) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - CheckInterrupts - Return v43 - "); - } - - #[test] - fn test_opt_new_basic_object() { - eval(" - def test = BasicObject.new - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, BasicObject) - v40:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v12:NilClass = Const Value(nil) - PatchPoint MethodRedefined(BasicObject@0x1008, new@0x1010, cme:0x1018) - v43:BasicObjectExact = ObjectAllocClass BasicObject:VALUE(0x1008) - PatchPoint MethodRedefined(BasicObject@0x1008, initialize@0x1040, cme:0x1048) - PatchPoint NoSingletonClass(BasicObject@0x1008) - v47:NilClass = Const Value(nil) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - CheckInterrupts - Return v43 - "); - } - - #[test] - fn test_opt_new_hash() { - eval(" - def test = Hash.new - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, Hash) - v40:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v12:NilClass = Const Value(nil) - PatchPoint MethodRedefined(Hash@0x1008, new@0x1010, cme:0x1018) - v43:HashExact = ObjectAllocClass Hash:VALUE(0x1008) - v18:BasicObject = SendWithoutBlock v43, :initialize - CheckInterrupts - CheckInterrupts - Return v43 - "); - assert_snapshot!(inspect("test"), @"{}"); - } - - #[test] - fn test_opt_new_array() { - eval(" - def test = Array.new 1 - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, Array) - v42:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v12:NilClass = Const Value(nil) - v13:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Array@0x1008, new@0x1010, cme:0x1018) - PatchPoint MethodRedefined(Class@0x1040, new@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(Class@0x1040) - v53:BasicObject = CCallVariadic new@0x1048, v42, v13 - CheckInterrupts - Return v53 - "); - } - - #[test] - fn test_opt_new_set() { - eval(" - def test = Set.new - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, Set) - v40:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v12:NilClass = Const Value(nil) - PatchPoint MethodRedefined(Set@0x1008, new@0x1010, cme:0x1018) - v16:HeapBasicObject = ObjectAlloc v40 - PatchPoint MethodRedefined(Set@0x1008, initialize@0x1040, cme:0x1048) - PatchPoint NoSingletonClass(Set@0x1008) - v46:SetExact = GuardType v16, SetExact - v47:BasicObject = CCallVariadic initialize@0x1070, v46 - CheckInterrupts - CheckInterrupts - Return v16 - "); - } - - #[test] - fn test_opt_new_string() { - eval(" - def test = String.new - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, String) - v40:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v12:NilClass = Const Value(nil) - PatchPoint MethodRedefined(String@0x1008, new@0x1010, cme:0x1018) - PatchPoint MethodRedefined(Class@0x1040, new@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(Class@0x1040) - v51:BasicObject = CCallVariadic new@0x1048, v40 - CheckInterrupts - Return v51 - "); - } - - #[test] - fn test_opt_new_regexp() { - eval(" - def test = Regexp.new '' - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, Regexp) - v44:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v12:NilClass = Const Value(nil) - v13:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) - v15:StringExact = StringCopy v13 - PatchPoint MethodRedefined(Regexp@0x1008, new@0x1018, cme:0x1020) - v47:RegexpExact = ObjectAllocClass Regexp:VALUE(0x1008) - PatchPoint MethodRedefined(Regexp@0x1008, initialize@0x1048, cme:0x1050) - PatchPoint NoSingletonClass(Regexp@0x1008) - v51:BasicObject = CCallVariadic initialize@0x1078, v47, v15 - CheckInterrupts - CheckInterrupts - Return v47 - "); - } - - #[test] - fn test_opt_length() { - eval(" - def test(a,b) = [a,b].length - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v17:ArrayExact = NewArray v11, v12 - PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - IncrCounter inline_cfunc_optimized_send_count - v31:Fixnum = CCall length@0x1038, v17 - CheckInterrupts - Return v31 - "); - } - - #[test] - fn test_opt_size() { - eval(" - def test(a,b) = [a,b].size - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v17:ArrayExact = NewArray v11, v12 - PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - IncrCounter inline_cfunc_optimized_send_count - v31:Fixnum = CCall size@0x1038, v17 - CheckInterrupts - Return v31 - "); - } - - #[test] - fn test_getblockparamproxy() { - eval(" - def test(&block) = tap(&block) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - GuardBlockParamProxy l0 - v15:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1000)) - v17:BasicObject = Send v8, 0x1008, :tap, v15 - CheckInterrupts - Return v17 - "); - } - - #[test] - fn test_getinstancevariable() { - eval(" - def test = @foo - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - v12:BasicObject = GetIvar v6, :@foo - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_setinstancevariable() { - eval(" - def test = @foo = 1 - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - PatchPoint SingleRactorMode - SetIvar v6, :@foo, v10 - CheckInterrupts - Return v10 - "); - } - - #[test] - fn test_elide_freeze_with_frozen_hash() { - eval(" - def test = {}.freeze - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) - v12:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_dont_optimize_hash_freeze_if_redefined() { - eval(" - class Hash - def freeze; end - end - def test = {}.freeze - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:5: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - SideExit PatchPoint(BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE)) - "); - } - - #[test] - fn test_elide_freeze_with_refrozen_hash() { - eval(" - def test = {}.freeze.freeze - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) - v12:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_no_elide_freeze_with_unfrozen_hash() { - eval(" - def test = {}.dup.freeze - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:HashExact = NewHash - PatchPoint MethodRedefined(Hash@0x1000, dup@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Hash@0x1000) - v24:BasicObject = CCallWithFrame dup@0x1038, v11 - v15:BasicObject = SendWithoutBlock v24, :freeze - CheckInterrupts - Return v15 - "); - } - - #[test] - fn test_no_elide_freeze_hash_with_args() { - eval(" - def test = {}.freeze(nil) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:HashExact = NewHash - v12:NilClass = Const Value(nil) - v14:BasicObject = SendWithoutBlock v11, :freeze, v12 - CheckInterrupts - Return v14 - "); - } - - #[test] - fn test_elide_freeze_with_frozen_ary() { - eval(" - def test = [].freeze - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) - v12:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_elide_freeze_with_refrozen_ary() { - eval(" - def test = [].freeze.freeze - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) - v12:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_no_elide_freeze_with_unfrozen_ary() { - eval(" - def test = [].dup.freeze - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, dup@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - v24:BasicObject = CCallWithFrame dup@0x1038, v11 - v15:BasicObject = SendWithoutBlock v24, :freeze - CheckInterrupts - Return v15 - "); - } - - #[test] - fn test_no_elide_freeze_ary_with_args() { - eval(" - def test = [].freeze(nil) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:ArrayExact = NewArray - v12:NilClass = Const Value(nil) - v14:BasicObject = SendWithoutBlock v11, :freeze, v12 - CheckInterrupts - Return v14 - "); - } - - #[test] - fn test_elide_freeze_with_frozen_str() { - eval(" - def test = ''.freeze - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) - v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_elide_freeze_with_refrozen_str() { - eval(" - def test = ''.freeze.freeze - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) - v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_no_elide_freeze_with_unfrozen_str() { - eval(" - def test = ''.dup.freeze - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v12:StringExact = StringCopy v10 - PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(String@0x1008) - v25:BasicObject = CCallWithFrame dup@0x1040, v12 - v16:BasicObject = SendWithoutBlock v25, :freeze - CheckInterrupts - Return v16 - "); - } - - #[test] - fn test_no_elide_freeze_str_with_args() { - eval(" - def test = ''.freeze(nil) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v12:StringExact = StringCopy v10 - v13:NilClass = Const Value(nil) - v15:BasicObject = SendWithoutBlock v12, :freeze, v13 - CheckInterrupts - Return v15 - "); - } - - #[test] - fn test_elide_uminus_with_frozen_str() { - eval(" - def test = -'' - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS) - v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_elide_uminus_with_refrozen_str() { - eval(" - def test = -''.freeze - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) - v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS) - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_no_elide_uminus_with_unfrozen_str() { - eval(" - def test = -''.dup - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v12:StringExact = StringCopy v10 - PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(String@0x1008) - v25:BasicObject = CCallWithFrame dup@0x1040, v12 - v16:BasicObject = SendWithoutBlock v25, :-@ - CheckInterrupts - Return v16 - "); - } - - #[test] - fn test_objtostring_anytostring_string() { - eval(r##" - def test = "#{('foo')}" - "##); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v13:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v15:StringExact = StringCopy v13 - v21:StringExact = StringConcat v10, v15 - CheckInterrupts - Return v21 - "); - } - - #[test] - fn test_objtostring_anytostring_with_non_string() { - eval(r##" - def test = "#{1}" - "##); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v11:Fixnum[1] = Const Value(1) - v13:BasicObject = ObjToString v11 - v15:String = AnyToString v11, str: v13 - v17:StringExact = StringConcat v10, v15 - CheckInterrupts - Return v17 - "); - } - - #[test] - fn test_optimize_objtostring_anytostring_recv_profiled() { - eval(" - def test(a) - \"#{a}\" - end - test('foo'); test('foo') - "); - - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint NoSingletonClass(String@0x1008) - v26:String = GuardType v9, String - v19:StringExact = StringConcat v13, v26 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_optimize_objtostring_anytostring_recv_profiled_string_subclass() { - eval(" - class MyString < String; end - - def test(a) - \"#{a}\" - end - foo = MyString.new('foo') - test(MyString.new(foo)); test(MyString.new(foo)) - "); - - assert_snapshot!(hir_string("test"), @r" - fn test@:5: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint NoSingletonClass(MyString@0x1008) - v26:String = GuardType v9, String - v19:StringExact = StringConcat v13, v26 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_optimize_objtostring_profiled_nonstring_falls_back_to_send() { - eval(" - def test(a) - \"#{a}\" - end - test([1,2,3]); test([1,2,3]) # No fast path for array - "); - - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v25:BasicObject = GuardTypeNot v9, String - PatchPoint MethodRedefined(Array@0x1008, to_s@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(Array@0x1008) - v30:ArrayExact = GuardType v9, ArrayExact - v31:BasicObject = CCallWithFrame to_s@0x1040, v30 - v17:String = AnyToString v9, str: v31 - v19:StringExact = StringConcat v13, v17 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_branchnil_nil() { - eval(" - def test - x = nil - x&.itself - end - "); - - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v13:NilClass = Const Value(nil) - CheckInterrupts - CheckInterrupts - Return v13 - "); - } - - #[test] - fn test_branchnil_truthy() { - eval(" - def test - x = 1 - x&.itself - end - "); - - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v13:Fixnum[1] = Const Value(1) - CheckInterrupts - PatchPoint MethodRedefined(Integer@0x1000, itself@0x1008, cme:0x1010) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v13 - "); - } - - #[test] - fn test_dont_eliminate_load_from_non_frozen_array() { - eval(r##" - S = [4,5,6] - def test = S[0] - test - "##); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, S) - v24:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v12:Fixnum[0] = Const Value(0) - PatchPoint MethodRedefined(Array@0x1010, []@0x1018, cme:0x1020) - PatchPoint NoSingletonClass(Array@0x1010) - v28:BasicObject = ArrayArefFixnum v24, v12 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v28 - "); - // TODO(max): Check the result of `S[0] = 5; test` using `inspect` to make sure that we - // actually do the load at run-time. - } - - #[test] - fn test_eliminate_load_from_frozen_array_in_bounds() { - eval(r##" - def test = [4,5,6].freeze[1] - "##); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) - v12:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v13:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(Array@0x1008) - v28:Fixnum[5] = Const Value(5) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_eliminate_load_from_frozen_array_negative() { - eval(r##" - def test = [4,5,6].freeze[-3] - "##); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) - v12:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v13:Fixnum[-3] = Const Value(-3) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(Array@0x1008) - v28:Fixnum[4] = Const Value(4) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_eliminate_load_from_frozen_array_negative_out_of_bounds() { - eval(r##" - def test = [4,5,6].freeze[-10] - "##); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) - v12:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v13:Fixnum[-10] = Const Value(-10) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(Array@0x1008) - v28:NilClass = Const Value(nil) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_eliminate_load_from_frozen_array_out_of_bounds() { - eval(r##" - def test = [4,5,6].freeze[10] - "##); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) - v12:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v13:Fixnum[10] = Const Value(10) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(Array@0x1008) - v28:NilClass = Const Value(nil) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_dont_optimize_array_aref_if_redefined() { - eval(r##" - class Array - def [](index) = [] - end - def test = [4,5,6].freeze[10] - "##); - assert_snapshot!(hir_string("test"), @r" - fn test@:5: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) - v12:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v13:Fixnum[10] = Const Value(10) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(Array@0x1008) - v25:BasicObject = SendWithoutBlockDirect v12, :[] (0x1040), v13 - CheckInterrupts - Return v25 - "); - } - - #[test] - fn test_dont_optimize_array_max_if_redefined() { - eval(r##" - class Array - def max = [] - end - def test = [4,5,6].max - "##); - assert_snapshot!(hir_string("test"), @r" - fn test@:5: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v12:ArrayExact = ArrayDup v10 - PatchPoint MethodRedefined(Array@0x1008, max@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(Array@0x1008) - v22:BasicObject = SendWithoutBlockDirect v12, :max (0x1040) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_set_type_from_constant() { - eval(" - MY_SET = Set.new - - def test = MY_SET - - test - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, MY_SET) - v19:SetExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_regexp_type() { - eval(" - def test = /a/ - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:RegexpExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - CheckInterrupts - Return v10 - "); - } - - #[test] - fn test_bmethod_send_direct() { - eval(" - define_method(:zero) { :b } - define_method(:one) { |arg| arg } - - def test = one(zero) - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:5: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint MethodRedefined(Object@0x1000, zero@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v22:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - IncrCounter inline_iseq_optimized_send_count - v30:StaticSymbol[:b] = Const Value(VALUE(0x1038)) - PatchPoint SingleRactorMode - PatchPoint MethodRedefined(Object@0x1000, one@0x1040, cme:0x1048) - PatchPoint NoSingletonClass(Object@0x1000) - v27:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - IncrCounter inline_iseq_optimized_send_count - CheckInterrupts - Return v30 - "); - } - - #[test] - fn test_symbol_block_bmethod() { - eval(" - define_method(:identity, &:itself) - def test = identity(100) - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[100] = Const Value(100) - v12:BasicObject = SendWithoutBlock v6, :identity, v10 - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_call_bmethod_with_block() { - eval(" - define_method(:bmethod) { :b } - def test = (bmethod {}) - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:BasicObject = Send v6, 0x1000, :bmethod - CheckInterrupts - Return v11 - "); - } - - #[test] - fn test_call_shareable_bmethod() { - eval(" - class Foo - class << self - define_method(:identity, &(Ractor.make_shareable ->(val){val})) - end - end - def test = Foo.identity(100) - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:7: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, Foo) - v22:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v12:Fixnum[100] = Const Value(100) - PatchPoint MethodRedefined(Class@0x1010, identity@0x1018, cme:0x1020) - PatchPoint NoSingletonClass(Class@0x1010) - IncrCounter inline_iseq_optimized_send_count - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_nil_nil_specialized_to_ccall() { - eval(" - def test = nil.nil? - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:NilClass = Const Value(nil) - PatchPoint MethodRedefined(NilClass@0x1000, nil?@0x1008, cme:0x1010) - IncrCounter inline_cfunc_optimized_send_count - v23:TrueClass = CCall nil?@0x1038, v10 - CheckInterrupts - Return v23 - "); - } - - #[test] - fn test_eliminate_nil_nil_specialized_to_ccall() { - eval(" - def test - nil.nil? - 1 - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:NilClass = Const Value(nil) - PatchPoint MethodRedefined(NilClass@0x1000, nil?@0x1008, cme:0x1010) - IncrCounter inline_cfunc_optimized_send_count - v17:Fixnum[1] = Const Value(1) - CheckInterrupts - Return v17 - "); - } - - #[test] - fn test_non_nil_nil_specialized_to_ccall() { - eval(" - def test = 1.nil? - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Integer@0x1000, nil?@0x1008, cme:0x1010) - IncrCounter inline_cfunc_optimized_send_count - v23:FalseClass = CCall nil?@0x1038, v10 - CheckInterrupts - Return v23 - "); - } - - #[test] - fn test_eliminate_non_nil_nil_specialized_to_ccall() { - eval(" - def test - 1.nil? - 2 - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Integer@0x1000, nil?@0x1008, cme:0x1010) - IncrCounter inline_cfunc_optimized_send_count - v17:Fixnum[2] = Const Value(2) - CheckInterrupts - Return v17 - "); - } - - #[test] - fn test_guard_nil_for_nil_opt() { - eval(" - def test(val) = val.nil? - - test(nil) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(NilClass@0x1000, nil?@0x1008, cme:0x1010) - v24:NilClass = GuardType v9, NilClass - IncrCounter inline_cfunc_optimized_send_count - v26:TrueClass = CCall nil?@0x1038, v24 - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_guard_false_for_nil_opt() { - eval(" - def test(val) = val.nil? - - test(false) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(FalseClass@0x1000, nil?@0x1008, cme:0x1010) - v24:FalseClass = GuardType v9, FalseClass - IncrCounter inline_cfunc_optimized_send_count - v26:FalseClass = CCall nil?@0x1038, v24 - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_guard_true_for_nil_opt() { - eval(" - def test(val) = val.nil? - - test(true) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(TrueClass@0x1000, nil?@0x1008, cme:0x1010) - v24:TrueClass = GuardType v9, TrueClass - IncrCounter inline_cfunc_optimized_send_count - v26:FalseClass = CCall nil?@0x1038, v24 - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_guard_symbol_for_nil_opt() { - eval(" - def test(val) = val.nil? - - test(:foo) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Symbol@0x1000, nil?@0x1008, cme:0x1010) - v24:StaticSymbol = GuardType v9, StaticSymbol - IncrCounter inline_cfunc_optimized_send_count - v26:FalseClass = CCall nil?@0x1038, v24 - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_guard_fixnum_for_nil_opt() { - eval(" - def test(val) = val.nil? - - test(1) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Integer@0x1000, nil?@0x1008, cme:0x1010) - v24:Fixnum = GuardType v9, Fixnum - IncrCounter inline_cfunc_optimized_send_count - v26:FalseClass = CCall nil?@0x1038, v24 - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_guard_float_for_nil_opt() { - eval(" - def test(val) = val.nil? - - test(1.0) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Float@0x1000, nil?@0x1008, cme:0x1010) - v24:Flonum = GuardType v9, Flonum - IncrCounter inline_cfunc_optimized_send_count - v26:FalseClass = CCall nil?@0x1038, v24 - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_guard_string_for_nil_opt() { - eval(" - def test(val) = val.nil? - - test('foo') - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, nil?@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v25:StringExact = GuardType v9, StringExact - IncrCounter inline_cfunc_optimized_send_count - v27:FalseClass = CCall nil?@0x1038, v25 - CheckInterrupts - Return v27 - "); - } - - #[test] - fn test_specialize_basicobject_not_to_ccall() { - eval(" - def test(a) = !a - - test([]) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, !@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - v25:ArrayExact = GuardType v9, ArrayExact - IncrCounter inline_cfunc_optimized_send_count - v27:BoolExact = CCall !@0x1038, v25 - CheckInterrupts - Return v27 - "); - } - - #[test] - fn test_specialize_array_empty_p_to_ccall() { - eval(" - def test(a) = a.empty? - - test([]) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, empty?@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - v25:ArrayExact = GuardType v9, ArrayExact - IncrCounter inline_cfunc_optimized_send_count - v27:BoolExact = CCall empty?@0x1038, v25 - CheckInterrupts - Return v27 - "); - } - - #[test] - fn test_specialize_hash_empty_p_to_ccall() { - eval(" - def test(a) = a.empty? - - test({}) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Hash@0x1000, empty?@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Hash@0x1000) - v25:HashExact = GuardType v9, HashExact - IncrCounter inline_cfunc_optimized_send_count - v27:BoolExact = CCall empty?@0x1038, v25 - CheckInterrupts - Return v27 - "); - } - - #[test] - fn test_specialize_basic_object_eq_to_ccall() { - eval(" - class C; end - def test(a, b) = a == b - - test(C.new, C.new) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, ==@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(C@0x1000) - v28:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C] - IncrCounter inline_cfunc_optimized_send_count - v30:BoolExact = CCall ==@0x1038, v28, v12 - CheckInterrupts - Return v30 - "); - } - - #[test] - fn test_guard_fixnum_and_fixnum() { - eval(" - def test(x, y) = x & y - - test(1, 2) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, 28) - v26:Fixnum = GuardType v11, Fixnum - v27:Fixnum = GuardType v12, Fixnum - v28:Fixnum = FixnumAnd v26, v27 - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_guard_fixnum_or_fixnum() { - eval(" - def test(x, y) = x | y - - test(1, 2) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, 29) - v26:Fixnum = GuardType v11, Fixnum - v27:Fixnum = GuardType v12, Fixnum - v28:Fixnum = FixnumOr v26, v27 - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_method_redefinition_patch_point_on_top_level_method() { - eval(" - def foo; end - def test = foo - - test; test - "); - - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - IncrCounter inline_iseq_optimized_send_count - v22:NilClass = Const Value(nil) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_optimize_getivar_embedded() { - eval(" - class C - attr_reader :foo - def initialize - @foo = 42 - end - end - - O = C.new - def test(o) = o.foo - test O - test O - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:10: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(C@0x1000) - v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - v25:HeapObject[class_exact:C] = GuardShape v22, 0x1038 - v26:BasicObject = LoadIvarEmbedded v25, :@foo@0x1039 - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_optimize_getivar_extended() { - eval(" - class C - attr_reader :foo - def initialize - @foo = 42 - end - end - - O = C.new - def test(o) = o.foo - test O - test O - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:10: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(C@0x1000) - v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - v25:HeapObject[class_exact:C] = GuardShape v22, 0x1038 - v26:BasicObject = LoadIvarEmbedded v25, :@foo@0x1039 - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_dont_optimize_getivar_polymorphic() { - set_call_threshold(3); - eval(" - class C - attr_reader :foo, :bar - - def foo_then_bar - @foo = 1 - @bar = 2 - end - - def bar_then_foo - @bar = 3 - @foo = 4 - end - end - - O1 = C.new - O1.foo_then_bar - O2 = C.new - O2.bar_then_foo - def test(o) = o.foo - test O1 - test O2 - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:20: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v14:BasicObject = SendWithoutBlock v9, :foo - CheckInterrupts - Return v14 - "); - } - - #[test] - fn test_optimize_send_with_block() { - eval(r#" - def test = [1, 2, 3].map { |x| x * 2 } - test; test - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v12:ArrayExact = ArrayDup v10 - PatchPoint MethodRedefined(Array@0x1008, map@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(Array@0x1008) - v23:BasicObject = CCallWithFrame map@0x1040, v12, block=0x1048 - CheckInterrupts - Return v23 - "); - } - - #[test] - fn test_do_not_optimize_send_variadic_with_block() { - eval(r#" - def test = [1, 2, 3].index { |x| x == 2 } - test; test - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v12:ArrayExact = ArrayDup v10 - v14:BasicObject = Send v12, 0x1008, :index - CheckInterrupts - Return v14 - "); - } - - #[test] - fn test_do_not_optimize_send_with_block_forwarding() { - eval(r#" - def test(&block) = [].map(&block) - test; test - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v14:ArrayExact = NewArray - GuardBlockParamProxy l0 - v17:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1000)) - v19:BasicObject = Send v14, 0x1008, :map, v17 - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_do_not_optimize_send_to_iseq_method_with_block() { - eval(r#" - def foo - yield 1 - end - - def test = foo {} - test; test - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:6: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:BasicObject = Send v6, 0x1000, :foo - CheckInterrupts - Return v11 - "); - } - - #[test] - fn test_inline_attr_reader_constant() { - eval(" - class C - attr_reader :foo - end - - O = C.new - def test = O.foo - test - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:7: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, O) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) - PatchPoint NoSingletonClass(C@0x1010) - v26:HeapObject[VALUE(0x1008)] = GuardShape v21, 0x1048 - v27:NilClass = Const Value(nil) - CheckInterrupts - Return v27 - "); - } - - #[test] - fn test_inline_attr_accessor_constant() { - eval(" - class C - attr_accessor :foo - end - - O = C.new - def test = O.foo - test - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:7: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, O) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) - PatchPoint NoSingletonClass(C@0x1010) - v26:HeapObject[VALUE(0x1008)] = GuardShape v21, 0x1048 - v27:NilClass = Const Value(nil) - CheckInterrupts - Return v27 - "); - } - - #[test] - fn test_inline_attr_reader() { - eval(" - class C - attr_reader :foo - end - - def test(o) = o.foo - test C.new - test C.new - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:6: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(C@0x1000) - v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - v25:HeapObject[class_exact:C] = GuardShape v22, 0x1038 - v26:NilClass = Const Value(nil) - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_inline_attr_accessor() { - eval(" - class C - attr_accessor :foo - end - - def test(o) = o.foo - test C.new - test C.new - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:6: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(C@0x1000) - v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - v25:HeapObject[class_exact:C] = GuardShape v22, 0x1038 - v26:NilClass = Const Value(nil) - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_inline_attr_accessor_set() { - eval(" - class C - attr_accessor :foo - end - - def test(o) = o.foo = 5 - test C.new - test C.new - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:6: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v14:Fixnum[5] = Const Value(5) - PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010) - v23:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - SetIvar v23, :@foo, v14 - CheckInterrupts - Return v14 - "); - } - - #[test] - fn test_inline_attr_writer_set() { - eval(" - class C - attr_writer :foo - end - - def test(o) = o.foo = 5 - test C.new - test C.new - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:6: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v14:Fixnum[5] = Const Value(5) - PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010) - v23:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - SetIvar v23, :@foo, v14 - CheckInterrupts - Return v14 - "); - } - - #[test] - fn test_array_reverse_returns_array() { - eval(r#" - def test = [].reverse - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - v22:ArrayExact = CCallWithFrame reverse@0x1038, v11 - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_array_reverse_is_elidable() { - eval(r#" - def test - [].reverse - 5 - end - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - v16:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v16 - "); - } - - #[test] - fn test_array_join_returns_string() { - eval(r#" - def test = [].join "," - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v11:ArrayExact = NewArray - v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v14:StringExact = StringCopy v12 - PatchPoint MethodRedefined(Array@0x1008, join@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(Array@0x1008) - v25:StringExact = CCallVariadic join@0x1040, v11, v14 - CheckInterrupts - Return v25 - "); - } - - #[test] - fn test_string_to_s_returns_string() { - eval(r#" - def test = "".to_s - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v12:StringExact = StringCopy v10 - PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(String@0x1008) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_inline_string_literal_to_s() { - eval(r#" - def test = "foo".to_s - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v12:StringExact = StringCopy v10 - PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(String@0x1008) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_inline_profiled_string_to_s() { - eval(r#" - def test(o) = o.to_s - test "foo" - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, to_s@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v23:StringExact = GuardType v9, StringExact - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v23 - "); - } - - #[test] - fn test_array_aref_fixnum_literal() { - eval(" - def test - arr = [1, 2, 3] - arr[0] - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v13:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v15:ArrayExact = ArrayDup v13 - v18:Fixnum[0] = Const Value(0) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(Array@0x1008) - v31:BasicObject = ArrayArefFixnum v15, v18 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v31 - "); - } - - #[test] - fn test_array_aref_fixnum_profiled() { - eval(" - def test(arr, idx) - arr[idx] - end - test([1, 2, 3], 0) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - v28:ArrayExact = GuardType v11, ArrayExact - v29:Fixnum = GuardType v12, Fixnum - v30:BasicObject = ArrayArefFixnum v28, v29 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v30 - "); - } - - #[test] - fn test_array_aref_fixnum_array_subclass() { - eval(" - class C < Array; end - def test(arr, idx) - arr[idx] - end - test(C.new([1, 2, 3]), 0) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, []@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(C@0x1000) - v28:ArraySubclass[class_exact:C] = GuardType v11, ArraySubclass[class_exact:C] - v29:Fixnum = GuardType v12, Fixnum - v30:BasicObject = ArrayArefFixnum v28, v29 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v30 - "); - } - - #[test] - fn test_hash_aref_literal() { - eval(" - def test - arr = {1 => 3} - arr[1] - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:NilClass = Const Value(nil) - Jump bb2(v1, v2) - bb1(v5:BasicObject): - EntryPoint JIT(0) - v6:NilClass = Const Value(nil) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:NilClass): - v13:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v15:HashExact = HashDup v13 - v18:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Hash@0x1008, []@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(Hash@0x1008) - v31:BasicObject = HashAref v15, v18 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v31 - "); - } - - #[test] - fn test_hash_aref_profiled() { - eval(" - def test(hash, key) - hash[key] - end - test({1 => 3}, 1) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Hash@0x1000) - v28:HashExact = GuardType v11, HashExact - v29:BasicObject = HashAref v28, v12 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v29 - "); - } - - #[test] - fn test_hash_aref_subclass() { - eval(" - class C < Hash; end - def test(hash, key) - hash[key] - end - test(C.new({0 => 3}), 0) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, []@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(C@0x1000) - v28:HashSubclass[class_exact:C] = GuardType v11, HashSubclass[class_exact:C] - v29:BasicObject = HashAref v28, v12 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v29 - "); - } - - #[test] - fn test_does_not_fold_hash_aref_with_frozen_hash() { - eval(" - H = {a: 0}.freeze - def test = H[:a] - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, H) - v24:HashExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v12:StaticSymbol[:a] = Const Value(VALUE(0x1010)) - PatchPoint MethodRedefined(Hash@0x1018, []@0x1020, cme:0x1028) - PatchPoint NoSingletonClass(Hash@0x1018) - v28:BasicObject = HashAref v24, v12 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_optimize_thread_current() { - eval(" - def test = Thread.current - test - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, Thread) - v21:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(Class@0x1010, current@0x1018, cme:0x1020) - PatchPoint NoSingletonClass(Class@0x1010) - IncrCounter inline_cfunc_optimized_send_count - v26:BasicObject = CCall current@0x1048, v21 - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_optimize_array_aset() { - eval(" - def test(arr) - arr[1] = 10 - end - test([]) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v14:Fixnum[1] = Const Value(1) - v15:Fixnum[10] = Const Value(10) - PatchPoint MethodRedefined(Array@0x1000, []=@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - v28:ArrayExact = GuardType v9, ArrayExact - v29:BasicObject = CCallVariadic []=@0x1038, v28, v14, v15 - CheckInterrupts - Return v15 - "); - } - - #[test] - fn test_optimize_array_ltlt() { - eval(" - def test(arr) - arr << 1 - end - test([]) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Array@0x1000, <<@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - v26:ArrayExact = GuardType v9, ArrayExact - ArrayPush v26, v13 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_optimize_array_push_single_arg() { - eval(" - def test(arr) - arr.push(1) - end - test([]) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Array@0x1000, push@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - v24:ArrayExact = GuardType v9, ArrayExact - ArrayPush v24, v13 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v24 - "); - } - - #[test] - fn test_do_not_optimize_array_push_multi_arg() { - eval(" - def test(arr) - arr.push(1,2,3) - end - test([]) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:Fixnum[1] = Const Value(1) - v14:Fixnum[2] = Const Value(2) - v15:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Array@0x1000, push@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - v26:ArrayExact = GuardType v9, ArrayExact - v27:BasicObject = CCallVariadic push@0x1038, v26, v13, v14, v15 - CheckInterrupts - Return v27 - "); - } - - #[test] - fn test_optimize_array_length() { - eval(" - def test(arr) = arr.length - test([]) - "); - assert_contains_opcode("test", YARVINSN_opt_length); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - v25:ArrayExact = GuardType v9, ArrayExact - IncrCounter inline_cfunc_optimized_send_count - v27:Fixnum = CCall length@0x1038, v25 - CheckInterrupts - Return v27 - "); - } - - #[test] - fn test_optimize_array_size() { - eval(" - def test(arr) = arr.size - test([]) - "); - assert_contains_opcode("test", YARVINSN_opt_size); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - v25:ArrayExact = GuardType v9, ArrayExact - IncrCounter inline_cfunc_optimized_send_count - v27:Fixnum = CCall size@0x1038, v25 - CheckInterrupts - Return v27 - "); - } - - #[test] - fn test_optimize_regexpmatch2() { - eval(r#" - def test(s) = s =~ /a/ - test("foo") - "#); - assert_contains_opcode("test", YARVINSN_opt_regexpmatch2); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:RegexpExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(String@0x1008, =~@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(String@0x1008) - v26:StringExact = GuardType v9, StringExact - v27:BasicObject = CCallWithFrame =~@0x1040, v26, v13 - CheckInterrupts - Return v27 - "); - } - - #[test] - fn test_optimize_string_getbyte_fixnum() { - eval(r#" - def test(s, i) = s.getbyte(i) - test("foo", 0) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, getbyte@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v26:StringExact = GuardType v11, StringExact - v27:Fixnum = GuardType v12, Fixnum - v28:NilClass|Fixnum = StringGetbyteFixnum v26, v27 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_elide_string_getbyte_fixnum() { - eval(r#" - def test(s, i) - s.getbyte(i) - 5 - end - test("foo", 0) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, getbyte@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v29:StringExact = GuardType v11, StringExact - v30:Fixnum = GuardType v12, Fixnum - IncrCounter inline_cfunc_optimized_send_count - v20:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v20 - "); - } - - #[test] - fn test_specialize_string_empty() { - eval(r#" - def test(s) - s.empty? - end - test("asdf") - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, empty?@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v25:StringExact = GuardType v9, StringExact - IncrCounter inline_cfunc_optimized_send_count - v27:BoolExact = CCall empty?@0x1038, v25 - CheckInterrupts - Return v27 - "); - } - - #[test] - fn test_eliminate_string_empty() { - eval(r#" - def test(s) - s.empty? - 4 - end - test("this should get removed") - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, empty?@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v28:StringExact = GuardType v9, StringExact - IncrCounter inline_cfunc_optimized_send_count - v19:Fixnum[4] = Const Value(4) - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_inline_integer_succ_with_fixnum() { - eval(" - def test(x) = x.succ - test(4) - "); - assert_contains_opcode("test", YARVINSN_opt_succ); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Integer@0x1000, succ@0x1008, cme:0x1010) - v24:Fixnum = GuardType v9, Fixnum - v25:Fixnum[1] = Const Value(1) - v26:Fixnum = FixnumAdd v24, v25 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_dont_inline_integer_succ_with_bignum() { - eval(" - def test(x) = x.succ - test(4 << 70) - "); - assert_contains_opcode("test", YARVINSN_opt_succ); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Integer@0x1000, succ@0x1008, cme:0x1010) - v24:Integer = GuardType v9, Integer - v25:BasicObject = CCallWithFrame succ@0x1038, v24 - CheckInterrupts - Return v25 - "); - } - - #[test] - fn test_optimize_string_append() { - eval(r#" - def test(x, y) = x << y - test("iron", "fish") - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v28:StringExact = GuardType v11, StringExact - v29:String = GuardType v12, String - v30:StringExact = StringAppend v28, v29 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v28 - "); - } - - // TODO: This should be inlined just as in the interpreter - #[test] - fn test_optimize_string_append_non_string() { - eval(r#" - def test(x, y) = x << y - test("iron", 4) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v28:StringExact = GuardType v11, StringExact - v29:BasicObject = CCallWithFrame <<@0x1038, v28, v12 - CheckInterrupts - Return v29 - "); - } - - #[test] - fn test_optimize_string_append_string_subclass() { - eval(r#" - class MyString < String - end - def test(x, y) = x << y - test("iron", MyString.new) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v28:StringExact = GuardType v11, StringExact - v29:String = GuardType v12, String - v30:StringExact = StringAppend v28, v29 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_do_not_optimize_string_subclass_append_string() { - eval(r#" - class MyString < String - end - def test(x, y) = x << y - test(MyString.new, "iron") - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(MyString@0x1000, <<@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(MyString@0x1000) - v28:StringSubclass[class_exact:MyString] = GuardType v11, StringSubclass[class_exact:MyString] - v29:BasicObject = CCallWithFrame <<@0x1038, v28, v12 - CheckInterrupts - Return v29 - "); - } - - #[test] - fn test_dont_inline_integer_succ_with_args() { - eval(" - def test = 4.succ 1 - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[4] = Const Value(4) - v11:Fixnum[1] = Const Value(1) - v13:BasicObject = SendWithoutBlock v10, :succ, v11 - CheckInterrupts - Return v13 - "); - } - - #[test] - fn test_inline_integer_xor_with_fixnum() { - eval(" - def test(x, y) = x ^ y - test(1, 2) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(Integer@0x1000, ^@0x1008, cme:0x1010) - v25:Fixnum = GuardType v11, Fixnum - v26:Fixnum = GuardType v12, Fixnum - v27:Fixnum = FixnumXor v25, v26 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v27 - "); - } - - #[test] - fn test_eliminate_integer_xor() { - eval(r#" - def test(x, y) - x ^ y - 42 - end - test(1, 2) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(Integer@0x1000, ^@0x1008, cme:0x1010) - v28:Fixnum = GuardType v11, Fixnum - v29:Fixnum = GuardType v12, Fixnum - IncrCounter inline_cfunc_optimized_send_count - v20:Fixnum[42] = Const Value(42) - CheckInterrupts - Return v20 - "); - } - - #[test] - fn test_dont_inline_integer_xor_with_bignum_or_boolean() { - eval(" - def test(x, y) = x ^ y - test(4 << 70, 1) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(Integer@0x1000, ^@0x1008, cme:0x1010) - v25:Integer = GuardType v11, Integer - v26:BasicObject = CCallWithFrame ^@0x1038, v25, v12 - CheckInterrupts - Return v26 - "); - - eval(" - def test(x, y) = x ^ y - test(1, 4 << 70) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(Integer@0x1000, ^@0x1008, cme:0x1010) - v25:Fixnum = GuardType v11, Fixnum - v26:BasicObject = CCallWithFrame ^@0x1038, v25, v12 - CheckInterrupts - Return v26 - "); - - eval(" - def test(x, y) = x ^ y - test(true, 0) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(TrueClass@0x1000, ^@0x1008, cme:0x1010) - v25:TrueClass = GuardType v11, TrueClass - v26:BasicObject = CCallWithFrame ^@0x1038, v25, v12 - CheckInterrupts - Return v26 - "); - } - - #[test] - fn test_dont_inline_integer_xor_with_args() { - eval(" - def test(x, y) = x.^() - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v17:BasicObject = SendWithoutBlock v11, :^ - CheckInterrupts - Return v17 - "); - } - - #[test] - fn test_specialize_hash_size() { - eval(" - def test(hash) = hash.size - test({foo: 3, bar: 1, baz: 4}) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Hash@0x1000, size@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Hash@0x1000) - v25:HashExact = GuardType v9, HashExact - IncrCounter inline_cfunc_optimized_send_count - v27:Fixnum = CCall size@0x1038, v25 - CheckInterrupts - Return v27 - "); - } - - #[test] - fn test_eliminate_hash_size() { - eval(" - def test(hash) - hash.size - 5 - end - test({foo: 3, bar: 1, baz: 4}) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Hash@0x1000, size@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Hash@0x1000) - v28:HashExact = GuardType v9, HashExact - IncrCounter inline_cfunc_optimized_send_count - v19:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_optimize_respond_to_p_true() { - eval(r#" - class C - def foo; end - end - def test(o) = o.respond_to?(:foo) - test(C.new) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:5: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(C@0x1008) - v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) - PatchPoint NoSingletonClass(C@0x1008) - v28:TrueClass = Const Value(true) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_optimize_respond_to_p_false_no_method() { - eval(r#" - class C - end - def test(o) = o.respond_to?(:foo) - test(C.new) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(C@0x1008) - v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, respond_to_missing?@0x1040, cme:0x1048) - PatchPoint MethodRedefined(C@0x1008, foo@0x1070, cme:0x1078) - PatchPoint NoSingletonClass(C@0x1008) - v30:FalseClass = Const Value(false) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v30 - "); - } - - #[test] - fn test_optimize_respond_to_p_false_default_private() { - eval(r#" - class C - private - def foo; end - end - def test(o) = o.respond_to?(:foo) - test(C.new) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:6: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(C@0x1008) - v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) - PatchPoint NoSingletonClass(C@0x1008) - v28:FalseClass = Const Value(false) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_optimize_respond_to_p_false_private() { - eval(r#" - class C - private - def foo; end - end - def test(o) = o.respond_to?(:foo, false) - test(C.new) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:6: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - v14:FalseClass = Const Value(false) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(C@0x1008) - v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) - PatchPoint NoSingletonClass(C@0x1008) - v29:FalseClass = Const Value(false) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v29 - "); - } - - #[test] - fn test_optimize_respond_to_p_falsy_private() { - eval(r#" - class C - private - def foo; end - end - def test(o) = o.respond_to?(:foo, nil) - test(C.new) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:6: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - v14:NilClass = Const Value(nil) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(C@0x1008) - v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) - PatchPoint NoSingletonClass(C@0x1008) - v29:FalseClass = Const Value(false) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v29 - "); - } - - #[test] - fn test_optimize_respond_to_p_true_private() { - eval(r#" - class C - private - def foo; end - end - def test(o) = o.respond_to?(:foo, true) - test(C.new) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:6: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - v14:TrueClass = Const Value(true) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(C@0x1008) - v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) - PatchPoint NoSingletonClass(C@0x1008) - v29:TrueClass = Const Value(true) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v29 - "); - } - - #[test] - fn test_optimize_respond_to_p_truthy() { - eval(r#" - class C - def foo; end - end - def test(o) = o.respond_to?(:foo, 4) - test(C.new) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:5: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - v14:Fixnum[4] = Const Value(4) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(C@0x1008) - v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) - PatchPoint NoSingletonClass(C@0x1008) - v29:TrueClass = Const Value(true) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v29 - "); - } - - #[test] - fn test_optimize_respond_to_p_falsy() { - eval(r#" - class C - def foo; end - end - def test(o) = o.respond_to?(:foo, nil) - test(C.new) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:5: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - v14:NilClass = Const Value(nil) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(C@0x1008) - v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) - PatchPoint NoSingletonClass(C@0x1008) - v29:TrueClass = Const Value(true) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v29 - "); - } - - #[test] - fn test_optimize_respond_to_missing() { - eval(r#" - class C - end - def test(o) = o.respond_to?(:foo) - test(C.new) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(C@0x1008) - v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, respond_to_missing?@0x1040, cme:0x1048) - PatchPoint MethodRedefined(C@0x1008, foo@0x1070, cme:0x1078) - PatchPoint NoSingletonClass(C@0x1008) - v30:FalseClass = Const Value(false) - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v30 - "); - } - - #[test] - fn test_do_not_optimize_redefined_respond_to_missing() { - eval(r#" - class C - def respond_to_missing?(method, include_private = false) - true - end - end - def test(o) = o.respond_to?(:foo) - test(C.new) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:7: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(C@0x1008) - v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - v25:BasicObject = CCallVariadic respond_to?@0x1040, v24, v13 - CheckInterrupts - Return v25 - "); - } - - #[test] - fn test_inline_send_without_block_direct_putself() { - eval(r#" - def callee = self - def test = callee - test - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - IncrCounter inline_iseq_optimized_send_count - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_inline_send_without_block_direct_putobject_string() { - eval(r#" - # frozen_string_literal: true - def callee = "abc" - def test = callee - test - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - IncrCounter inline_iseq_optimized_send_count - v22:StringExact[VALUE(0x1038)] = Const Value(VALUE(0x1038)) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_inline_send_without_block_direct_putnil() { - eval(r#" - def callee = nil - def test = callee - test - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - IncrCounter inline_iseq_optimized_send_count - v22:NilClass = Const Value(nil) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_inline_send_without_block_direct_putobject_true() { - eval(r#" - def callee = true - def test = callee - test - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - IncrCounter inline_iseq_optimized_send_count - v22:TrueClass = Const Value(true) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_inline_send_without_block_direct_putobject_false() { - eval(r#" - def callee = false - def test = callee - test - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - IncrCounter inline_iseq_optimized_send_count - v22:FalseClass = Const Value(false) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_inline_send_without_block_direct_putobject_zero() { - eval(r#" - def callee = 0 - def test = callee - test - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - IncrCounter inline_iseq_optimized_send_count - v22:Fixnum[0] = Const Value(0) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_inline_send_without_block_direct_putobject_one() { - eval(r#" - def callee = 1 - def test = callee - test - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - IncrCounter inline_iseq_optimized_send_count - v22:Fixnum[1] = Const Value(1) - CheckInterrupts - Return v22 - "); - } - - #[test] - fn test_inline_send_without_block_direct_parameter() { - eval(r#" - def callee(x) = x - def test = callee 3 - test - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - IncrCounter inline_iseq_optimized_send_count - CheckInterrupts - Return v10 - "); - } - - #[test] - fn test_inline_send_without_block_direct_last_parameter() { - eval(r#" - def callee(x, y, z) = z - def test = callee 1, 2, 3 - test - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:Fixnum[1] = Const Value(1) - v11:Fixnum[2] = Const Value(2) - v12:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Object@0x1000) - v22:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - IncrCounter inline_iseq_optimized_send_count - CheckInterrupts - Return v12 - "); - } - - #[test] - fn test_inline_symbol_to_sym() { - eval(r#" - def test(o) = o.to_sym - test :foo - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Symbol@0x1000, to_sym@0x1008, cme:0x1010) - v21:StaticSymbol = GuardType v9, StaticSymbol - IncrCounter inline_iseq_optimized_send_count - CheckInterrupts - Return v21 - "); - } - - #[test] - fn test_inline_kernel_frozen_p() { - eval(r#" - def test(o) = o.frozen? - test :foo - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Symbol@0x1000, frozen?@0x1008, cme:0x1010) - v21:StaticSymbol = GuardType v9, StaticSymbol - IncrCounter inline_iseq_optimized_send_count - v24:BoolExact = InvokeBuiltin leaf _bi69, v21 - CheckInterrupts - Return v24 - "); - } - - #[test] - fn test_inline_integer_to_i() { - eval(r#" - def test(o) = o.to_i - test 5 - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Integer@0x1000, to_i@0x1008, cme:0x1010) - v21:Fixnum = GuardType v9, Fixnum - IncrCounter inline_iseq_optimized_send_count - CheckInterrupts - Return v21 - "); - } - - #[test] - fn test_inline_symbol_name() { - eval(" - def test(x) = x.to_s - test(:foo) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Symbol@0x1000, to_s@0x1008, cme:0x1010) - v21:StaticSymbol = GuardType v9, StaticSymbol - IncrCounter inline_iseq_optimized_send_count - v24:StringExact = InvokeBuiltin leaf _bi12, v21 - CheckInterrupts - Return v24 - "); - } - - #[test] - fn test_inline_symbol_to_s() { - eval(" - def test(x) = x.to_s - test(:foo) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Symbol@0x1000, to_s@0x1008, cme:0x1010) - v21:StaticSymbol = GuardType v9, StaticSymbol - IncrCounter inline_iseq_optimized_send_count - v24:StringExact = InvokeBuiltin leaf _bi12, v21 - CheckInterrupts - Return v24 - "); - } - - #[test] - fn test_optimize_stringexact_eq_stringexact() { - eval(r#" - def test(l, r) = l == r - test("a", "b") - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, ==@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v28:StringExact = GuardType v11, StringExact - v29:String = GuardType v12, String - v30:BoolExact = CCall String#==@0x1038, v28, v29 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v30 - "); - } - - #[test] - fn test_optimize_string_eq_string() { - eval(r#" - class C < String - end - def test(l, r) = l == r - test(C.new("a"), C.new("b")) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, ==@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(C@0x1000) - v28:StringSubclass[class_exact:C] = GuardType v11, StringSubclass[class_exact:C] - v29:String = GuardType v12, String - v30:BoolExact = CCall String#==@0x1038, v28, v29 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v30 - "); - } - - #[test] - fn test_optimize_stringexact_eq_string() { - eval(r#" - class C < String - end - def test(l, r) = l == r - test("a", C.new("b")) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, ==@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v28:StringExact = GuardType v11, StringExact - v29:String = GuardType v12, String - v30:BoolExact = CCall String#==@0x1038, v28, v29 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v30 - "); - } - - #[test] - fn test_optimize_stringexact_eqq_stringexact() { - eval(r#" - def test(l, r) = l === r - test("a", "b") - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:2: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, ===@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v26:StringExact = GuardType v11, StringExact - v27:String = GuardType v12, String - v28:BoolExact = CCall String#==@0x1038, v26, v27 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_optimize_string_eqq_string() { - eval(r#" - class C < String - end - def test(l, r) = l === r - test(C.new("a"), C.new("b")) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, ===@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(C@0x1000) - v26:StringSubclass[class_exact:C] = GuardType v11, StringSubclass[class_exact:C] - v27:String = GuardType v12, String - v28:BoolExact = CCall String#==@0x1038, v26, v27 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_optimize_stringexact_eqq_string() { - eval(r#" - class C < String - end - def test(l, r) = l === r - test("a", C.new("b")) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:4: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT(0) - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, ===@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v26:StringExact = GuardType v11, StringExact - v27:String = GuardType v12, String - v28:BoolExact = CCall String#==@0x1038, v26, v27 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v28 - "); - } - - #[test] - fn test_specialize_string_size() { - eval(r#" - def test(s) - s.size - end - test("asdf") - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, size@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v25:StringExact = GuardType v9, StringExact - IncrCounter inline_cfunc_optimized_send_count - v27:Fixnum = CCall size@0x1038, v25 - CheckInterrupts - Return v27 - "); - } - - #[test] - fn test_elide_string_size() { - eval(r#" - def test(s) - s.size - 5 - end - test("asdf") - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, size@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v28:StringExact = GuardType v9, StringExact - IncrCounter inline_cfunc_optimized_send_count - v19:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v19 - "); - } - - #[test] - fn test_specialize_string_bytesize() { - eval(r#" - def test(s) - s.bytesize - end - test("asdf") - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, bytesize@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v23:StringExact = GuardType v9, StringExact - IncrCounter inline_cfunc_optimized_send_count - v25:Fixnum = CCall bytesize@0x1038, v23 - CheckInterrupts - Return v25 - "); - } - - #[test] - fn test_elide_string_bytesize() { - eval(r#" - def test(s) - s.bytesize - 5 - end - test("asdf") - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, bytesize@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v26:StringExact = GuardType v9, StringExact - IncrCounter inline_cfunc_optimized_send_count - v17:Fixnum[5] = Const Value(5) - CheckInterrupts - Return v17 - "); - } - - #[test] - fn test_specialize_string_length() { - eval(r#" - def test(s) - s.length - end - test("asdf") + test(1) + test("x") "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v25:StringExact = GuardType v9, StringExact - IncrCounter inline_cfunc_optimized_send_count - v27:Fixnum = CCall length@0x1038, v25 - CheckInterrupts - Return v27 - "); - } - - #[test] - fn test_elide_string_length() { - eval(r#" - def test(s) - s.length - 4 - end - test("this should get removed") + assert_snapshot!(hir_string("test"), @r#" + digraph G { # test@<compiled>:3 + node [shape=plaintext]; + mode=hier; overlap=false; splines=true; + bb0 [label=< + + + + + +
bb0() 
EntryPoint interpreter 
v1:BasicObject = LoadSelf 
v2:BasicObject = GetLocal l0, SP@4 
Jump bb2(v1, v2) 
>]; + bb0:v3 -> bb2:params:n; + bb1 [label=< + + + +
bb1(v5:BasicObject, v6:BasicObject) 
EntryPoint JIT(0) 
Jump bb2(v5, v6) 
>]; + bb1:v7 -> bb2:params:n; + bb2 [label=< + + + + + + + + + + +
bb2(v8:BasicObject, v9:BasicObject) 
PatchPoint NoTracePoint 
CheckInterrupts 
v15:CBool = Test v9 
IfFalse v15, bb3(v8, v9) 
PatchPoint NoTracePoint 
v19:Fixnum[3] = Const Value(3) 
PatchPoint NoTracePoint 
CheckInterrupts 
Return v19 
>]; + bb2:v16 -> bb3:params:n; + bb3 [label=< + + + + + + +
bb3(v25:BasicObject, v26:BasicObject) 
PatchPoint NoTracePoint 
v30:Fixnum[4] = Const Value(4) 
PatchPoint NoTracePoint 
CheckInterrupts 
Return v30 
>]; + } "#); - assert_snapshot!(hir_string("test"), @r" - fn test@:3: - bb0(): - EntryPoint interpreter - v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT(0) - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(String@0x1000) - v28:StringExact = GuardType v9, StringExact - IncrCounter inline_cfunc_optimized_send_count - v19:Fixnum[4] = Const Value(4) - CheckInterrupts - Return v19 - "); } } diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs new file mode 100644 index 00000000000000..897cd6e9566966 --- /dev/null +++ b/zjit/src/hir/opt_tests.rs @@ -0,0 +1,7050 @@ +#[cfg(test)] +mod hir_opt_tests { + use crate::hir::*; + + use crate::{hir_strings, options::*}; + use insta::assert_snapshot; + use crate::hir::tests::hir_build_tests::assert_contains_opcode; + + #[track_caller] + fn hir_string_function(function: &Function) -> String { + format!("{}", FunctionPrinter::without_snapshot(function)) + } + + #[track_caller] + fn hir_string_proc(proc: &str) -> String { + let iseq = crate::cruby::with_rubyvm(|| get_proc_iseq(proc)); + unsafe { crate::cruby::rb_zjit_profile_disable(iseq) }; + let mut function = iseq_to_hir(iseq).unwrap(); + function.optimize(); + function.validate().unwrap(); + hir_string_function(&function) + } + + #[track_caller] + fn hir_string(method: &str) -> String { + hir_string_proc(&format!("{}.method(:{})", "self", method)) + } + + #[test] + fn test_fold_iftrue_away() { + eval(" + def test + cond = true + if cond + 3 + else + 4 + end + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:TrueClass = Const Value(true) + CheckInterrupts + v22:Fixnum[3] = Const Value(3) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_fold_iftrue_into_jump() { + eval(" + def test + cond = false + if cond + 3 + else + 4 + end + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:FalseClass = Const Value(false) + CheckInterrupts + v33:Fixnum[4] = Const Value(4) + CheckInterrupts + Return v33 + "); + } + + #[test] + fn test_fold_fixnum_add() { + eval(" + def test + 1 + 2 + 3 + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + v11:Fixnum[2] = Const Value(2) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) + v30:Fixnum[3] = Const Value(3) + v16:Fixnum[3] = Const Value(3) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) + v31:Fixnum[6] = Const Value(6) + CheckInterrupts + Return v31 + "); + } + + #[test] + fn test_fold_fixnum_sub() { + eval(" + def test + 5 - 3 - 1 + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[5] = Const Value(5) + v11:Fixnum[3] = Const Value(3) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MINUS) + v30:Fixnum[2] = Const Value(2) + v16:Fixnum[1] = Const Value(1) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MINUS) + v31:Fixnum[1] = Const Value(1) + CheckInterrupts + Return v31 + "); + } + + #[test] + fn test_fold_fixnum_sub_large_negative_result() { + eval(" + def test + 0 - 1073741825 + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[0] = Const Value(0) + v11:Fixnum[1073741825] = Const Value(1073741825) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MINUS) + v23:Fixnum[-1073741825] = Const Value(-1073741825) + CheckInterrupts + Return v23 + "); + } + + #[test] + fn test_fold_fixnum_mult() { + eval(" + def test + 6 * 7 + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[6] = Const Value(6) + v11:Fixnum[7] = Const Value(7) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MULT) + v23:Fixnum[42] = Const Value(42) + CheckInterrupts + Return v23 + "); + } + + #[test] + fn test_fold_fixnum_mult_zero() { + eval(" + def test(n) + 0 * n + n * 0 + end + test 1; test 2 + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[0] = Const Value(0) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MULT) + v33:Fixnum = GuardType v9, Fixnum + v40:Fixnum[0] = Const Value(0) + v18:Fixnum[0] = Const Value(0) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MULT) + v36:Fixnum = GuardType v9, Fixnum + v41:Fixnum[0] = Const Value(0) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) + v42:Fixnum[0] = Const Value(0) + CheckInterrupts + Return v42 + "); + } + + #[test] + fn test_fold_fixnum_less() { + eval(" + def test + if 1 < 2 + 3 + else + 4 + end + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + v11:Fixnum[2] = Const Value(2) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LT) + v40:TrueClass = Const Value(true) + CheckInterrupts + v22:Fixnum[3] = Const Value(3) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_fold_fixnum_less_equal() { + eval(" + def test + if 1 <= 2 && 2 <= 2 + 3 + else + 4 + end + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + v11:Fixnum[2] = Const Value(2) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LE) + v52:TrueClass = Const Value(true) + CheckInterrupts + v20:Fixnum[2] = Const Value(2) + v21:Fixnum[2] = Const Value(2) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LE) + v54:TrueClass = Const Value(true) + CheckInterrupts + v32:Fixnum[3] = Const Value(3) + CheckInterrupts + Return v32 + "); + } + + #[test] + fn test_fold_fixnum_greater() { + eval(" + def test + if 2 > 1 + 3 + else + 4 + end + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[2] = Const Value(2) + v11:Fixnum[1] = Const Value(1) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_GT) + v40:TrueClass = Const Value(true) + CheckInterrupts + v22:Fixnum[3] = Const Value(3) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_fold_fixnum_greater_equal() { + eval(" + def test + if 2 >= 1 && 2 >= 2 + 3 + else + 4 + end + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[2] = Const Value(2) + v11:Fixnum[1] = Const Value(1) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_GE) + v52:TrueClass = Const Value(true) + CheckInterrupts + v20:Fixnum[2] = Const Value(2) + v21:Fixnum[2] = Const Value(2) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_GE) + v54:TrueClass = Const Value(true) + CheckInterrupts + v32:Fixnum[3] = Const Value(3) + CheckInterrupts + Return v32 + "); + } + + #[test] + fn test_fold_fixnum_eq_false() { + eval(" + def test + if 1 == 2 + 3 + else + 4 + end + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + v11:Fixnum[2] = Const Value(2) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_EQ) + v40:FalseClass = Const Value(false) + CheckInterrupts + v32:Fixnum[4] = Const Value(4) + CheckInterrupts + Return v32 + "); + } + + #[test] + fn test_fold_fixnum_eq_true() { + eval(" + def test + if 2 == 2 + 3 + else + 4 + end + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[2] = Const Value(2) + v11:Fixnum[2] = Const Value(2) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_EQ) + v40:TrueClass = Const Value(true) + CheckInterrupts + v22:Fixnum[3] = Const Value(3) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_fold_fixnum_neq_true() { + eval(" + def test + if 1 != 2 + 3 + else + 4 + end + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + v11:Fixnum[2] = Const Value(2) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_EQ) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_NEQ) + v41:TrueClass = Const Value(true) + CheckInterrupts + v22:Fixnum[3] = Const Value(3) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_fold_fixnum_neq_false() { + eval(" + def test + if 2 != 2 + 3 + else + 4 + end + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[2] = Const Value(2) + v11:Fixnum[2] = Const Value(2) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_EQ) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_NEQ) + v41:FalseClass = Const Value(false) + CheckInterrupts + v32:Fixnum[4] = Const Value(4) + CheckInterrupts + Return v32 + "); + } + + #[test] + fn test_replace_guard_if_known_fixnum() { + eval(" + def test(a) + a + 1 + end + test(2); test(3) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[1] = Const Value(1) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) + v24:Fixnum = GuardType v9, Fixnum + v25:Fixnum = FixnumAdd v24, v13 + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_param_forms_get_bb_param() { + eval(" + def rest(*array) = array + def kw(k:) = k + def kw_rest(**k) = k + def post(*rest, post) = post + def block(&b) = nil + "); + assert_snapshot!(hir_strings!("rest", "kw", "kw_rest", "block", "post"), @r" + fn rest@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:ArrayExact = GetLocal l0, SP@4, * + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:ArrayExact): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:ArrayExact): + CheckInterrupts + Return v9 + + fn kw@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + CheckInterrupts + Return v11 + + fn kw_rest@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + CheckInterrupts + Return v9 + + fn block@:6: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:NilClass = Const Value(nil) + CheckInterrupts + Return v13 + + fn post@:5: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:ArrayExact = GetLocal l0, SP@5, * + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:ArrayExact, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:ArrayExact, v12:BasicObject): + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_optimize_top_level_call_into_send_direct() { + eval(" + def foo = [] + def test + foo + end + test; test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v20:BasicObject = SendWithoutBlockDirect v19, :foo (0x1038) + CheckInterrupts + Return v20 + "); + } + + #[test] + fn test_optimize_nonexistent_top_level_call() { + eval(" + def foo + end + def test + foo + end + test; test + undef :foo + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = SendWithoutBlock v6, :foo + CheckInterrupts + Return v11 + "); + } + + #[test] + fn test_optimize_private_top_level_call() { + eval(" + def foo = [] + private :foo + def test + foo + end + test; test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v20:BasicObject = SendWithoutBlockDirect v19, :foo (0x1038) + CheckInterrupts + Return v20 + "); + } + + #[test] + fn test_optimize_top_level_call_with_overloaded_cme() { + eval(" + def test + Integer(3) + end + test; test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[3] = Const Value(3) + PatchPoint MethodRedefined(Object@0x1000, Integer@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v21:BasicObject = SendWithoutBlockDirect v20, :Integer (0x1038), v10 + CheckInterrupts + Return v21 + "); + } + + #[test] + fn test_optimize_top_level_call_with_args_into_send_direct() { + eval(" + def foo(a, b) = [] + def test + foo 1, 2 + end + test; test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + v11:Fixnum[2] = Const Value(2) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v21:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v22:BasicObject = SendWithoutBlockDirect v21, :foo (0x1038), v10, v11 + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_optimize_top_level_sends_into_send_direct() { + eval(" + def foo = [] + def bar = [] + def test + foo + bar + end + test; test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v23:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v24:BasicObject = SendWithoutBlockDirect v23, :foo (0x1038) + PatchPoint MethodRedefined(Object@0x1000, bar@0x1040, cme:0x1048) + PatchPoint NoSingletonClass(Object@0x1000) + v27:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v28:BasicObject = SendWithoutBlockDirect v27, :bar (0x1038) + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_optimize_variadic_ccall() { + eval(" + def test + puts 'Hello' + end + test; test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v12:StringExact = StringCopy v10 + PatchPoint MethodRedefined(Object@0x1008, puts@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Object@0x1008) + v23:HeapObject[class_exact*:Object@VALUE(0x1008)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1008)] + v24:BasicObject = CCallVariadic puts@0x1040, v23, v12 + CheckInterrupts + Return v24 + "); + } + + #[test] + fn test_dont_optimize_fixnum_add_if_redefined() { + eval(" + class Integer + def +(other) + 100 + end + end + def test(a, b) = a + b + test(1,2); test(3,4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:7: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v19:BasicObject = SendWithoutBlock v11, :+, v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_optimize_send_into_fixnum_add_both_profiled() { + eval(" + def test(a, b) = a + b + test(1,2); test(3,4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) + v26:Fixnum = GuardType v11, Fixnum + v27:Fixnum = GuardType v12, Fixnum + v28:Fixnum = FixnumAdd v26, v27 + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_optimize_send_into_fixnum_add_left_profiled() { + eval(" + def test(a) = a + 1 + test(1); test(3) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[1] = Const Value(1) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) + v24:Fixnum = GuardType v9, Fixnum + v25:Fixnum = FixnumAdd v24, v13 + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_optimize_send_into_fixnum_add_right_profiled() { + eval(" + def test(a) = 1 + a + test(1); test(3) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[1] = Const Value(1) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) + v24:Fixnum = GuardType v9, Fixnum + v25:Fixnum = FixnumAdd v13, v24 + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_optimize_send_into_fixnum_lt_both_profiled() { + eval(" + def test(a, b) = a < b + test(1,2); test(3,4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LT) + v26:Fixnum = GuardType v11, Fixnum + v27:Fixnum = GuardType v12, Fixnum + v28:BoolExact = FixnumLt v26, v27 + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_optimize_send_into_fixnum_lt_left_profiled() { + eval(" + def test(a) = a < 1 + test(1); test(3) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[1] = Const Value(1) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LT) + v24:Fixnum = GuardType v9, Fixnum + v25:BoolExact = FixnumLt v24, v13 + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_optimize_send_into_fixnum_lt_right_profiled() { + eval(" + def test(a) = 1 < a + test(1); test(3) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[1] = Const Value(1) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LT) + v24:Fixnum = GuardType v9, Fixnum + v25:BoolExact = FixnumLt v13, v24 + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_optimize_new_range_fixnum_inclusive_literals() { + eval(" + def test() + a = 2 + (1..a) + end + test; test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:Fixnum[2] = Const Value(2) + v16:Fixnum[1] = Const Value(1) + v24:RangeExact = NewRangeFixnum v16 NewRangeInclusive v13 + CheckInterrupts + Return v24 + "); + } + + + #[test] + fn test_optimize_new_range_fixnum_exclusive_literals() { + eval(" + def test() + a = 2 + (1...a) + end + test; test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:Fixnum[2] = Const Value(2) + v16:Fixnum[1] = Const Value(1) + v24:RangeExact = NewRangeFixnum v16 NewRangeExclusive v13 + CheckInterrupts + Return v24 + "); + } + + #[test] + fn test_optimize_new_range_fixnum_inclusive_high_guarded() { + eval(" + def test(a) + (1..a) + end + test(2); test(3) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[1] = Const Value(1) + v21:Fixnum = GuardType v9, Fixnum + v22:RangeExact = NewRangeFixnum v13 NewRangeInclusive v21 + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_optimize_new_range_fixnum_exclusive_high_guarded() { + eval(" + def test(a) + (1...a) + end + test(2); test(3) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[1] = Const Value(1) + v21:Fixnum = GuardType v9, Fixnum + v22:RangeExact = NewRangeFixnum v13 NewRangeExclusive v21 + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_optimize_new_range_fixnum_inclusive_low_guarded() { + eval(" + def test(a) + (a..10) + end + test(2); test(3) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[10] = Const Value(10) + v21:Fixnum = GuardType v9, Fixnum + v22:RangeExact = NewRangeFixnum v21 NewRangeInclusive v13 + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_optimize_new_range_fixnum_exclusive_low_guarded() { + eval(" + def test(a) + (a...10) + end + test(2); test(3) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[10] = Const Value(10) + v21:Fixnum = GuardType v9, Fixnum + v22:RangeExact = NewRangeFixnum v21 NewRangeExclusive v13 + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_eliminate_new_array() { + eval(" + def test() + c = [] + 5 + end + test; test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v14:ArrayExact = NewArray + v17:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_opt_aref_array() { + eval(" + arr = [1,2,3] + def test(arr) = arr[0] + test(arr) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[0] = Const Value(0) + PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v26:ArrayExact = GuardType v9, ArrayExact + v27:BasicObject = ArrayArefFixnum v26, v13 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v27 + "); + assert_snapshot!(inspect("test [1,2,3]"), @"1"); + } + + #[test] + fn test_opt_aref_hash() { + eval(" + arr = {0 => 4} + def test(arr) = arr[0] + test(arr) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[0] = Const Value(0) + PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Hash@0x1000) + v26:HashExact = GuardType v9, HashExact + v27:BasicObject = HashAref v26, v13 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v27 + "); + assert_snapshot!(inspect("test({0 => 4})"), @"4"); + } + + #[test] + fn test_eliminate_new_range() { + eval(" + def test() + c = (1..2) + 5 + end + test; test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:RangeExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v16:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v16 + "); + } + + #[test] + fn test_do_not_eliminate_new_range_non_fixnum() { + eval(" + def test() + _ = (-'a'..'b') + 0 + end + test; test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS) + v15:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v16:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v18:StringExact = StringCopy v16 + v20:RangeExact = NewRange v15 NewRangeInclusive v18 + PatchPoint NoEPEscape(test) + v25:Fixnum[0] = Const Value(0) + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_eliminate_new_array_with_elements() { + eval(" + def test(a) + c = [a] + 5 + end + test(1); test(2) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject): + EntryPoint JIT(0) + v8:NilClass = Const Value(nil) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:NilClass): + v17:ArrayExact = NewArray v11 + v20:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v20 + "); + } + + #[test] + fn test_eliminate_new_hash() { + eval(" + def test() + c = {} + 5 + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v14:HashExact = NewHash + PatchPoint NoEPEscape(test) + v19:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_no_eliminate_new_hash_with_elements() { + eval(" + def test(aval, bval) + c = {a: aval, b: bval} + 5 + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@6 + v3:BasicObject = GetLocal l0, SP@5 + v4:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4) + bb1(v7:BasicObject, v8:BasicObject, v9:BasicObject): + EntryPoint JIT(0) + v10:NilClass = Const Value(nil) + Jump bb2(v7, v8, v9, v10) + bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:NilClass): + v19:StaticSymbol[:a] = Const Value(VALUE(0x1000)) + v20:StaticSymbol[:b] = Const Value(VALUE(0x1008)) + v22:HashExact = NewHash v19: v13, v20: v14 + PatchPoint NoEPEscape(test) + v27:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_eliminate_array_dup() { + eval(" + def test + c = [1, 2] + 5 + end + test; test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v15:ArrayExact = ArrayDup v13 + v18:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v18 + "); + } + + #[test] + fn test_eliminate_hash_dup() { + eval(" + def test + c = {a: 1, b: 2} + 5 + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v15:HashExact = HashDup v13 + v18:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v18 + "); + } + + #[test] + fn test_eliminate_putself() { + eval(" + def test() + c = self + 5 + end + test; test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v15:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v15 + "); + } + + #[test] + fn test_eliminate_string_copy() { + eval(r#" + def test() + c = "abc" + 5 + end + test; test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v15:StringExact = StringCopy v13 + v18:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v18 + "); + } + + #[test] + fn test_eliminate_fixnum_add() { + eval(" + def test(a, b) + a + b + 5 + end + test(1, 2); test(3, 4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) + v29:Fixnum = GuardType v11, Fixnum + v30:Fixnum = GuardType v12, Fixnum + v22:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_eliminate_fixnum_sub() { + eval(" + def test(a, b) + a - b + 5 + end + test(1, 2); test(3, 4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MINUS) + v29:Fixnum = GuardType v11, Fixnum + v30:Fixnum = GuardType v12, Fixnum + v22:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_eliminate_fixnum_mul() { + eval(" + def test(a, b) + a * b + 5 + end + test(1, 2); test(3, 4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MULT) + v29:Fixnum = GuardType v11, Fixnum + v30:Fixnum = GuardType v12, Fixnum + v22:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_do_not_eliminate_fixnum_div() { + eval(" + def test(a, b) + a / b + 5 + end + test(1, 2); test(3, 4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_DIV) + v29:Fixnum = GuardType v11, Fixnum + v30:Fixnum = GuardType v12, Fixnum + v31:Fixnum = FixnumDiv v29, v30 + v22:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_do_not_eliminate_fixnum_mod() { + eval(" + def test(a, b) + a % b + 5 + end + test(1, 2); test(3, 4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MOD) + v29:Fixnum = GuardType v11, Fixnum + v30:Fixnum = GuardType v12, Fixnum + v31:Fixnum = FixnumMod v29, v30 + v22:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_eliminate_fixnum_lt() { + eval(" + def test(a, b) + a < b + 5 + end + test(1, 2); test(3, 4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LT) + v29:Fixnum = GuardType v11, Fixnum + v30:Fixnum = GuardType v12, Fixnum + v22:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_eliminate_fixnum_le() { + eval(" + def test(a, b) + a <= b + 5 + end + test(1, 2); test(3, 4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LE) + v29:Fixnum = GuardType v11, Fixnum + v30:Fixnum = GuardType v12, Fixnum + v22:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_eliminate_fixnum_gt() { + eval(" + def test(a, b) + a > b + 5 + end + test(1, 2); test(3, 4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_GT) + v29:Fixnum = GuardType v11, Fixnum + v30:Fixnum = GuardType v12, Fixnum + v22:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_eliminate_fixnum_ge() { + eval(" + def test(a, b) + a >= b + 5 + end + test(1, 2); test(3, 4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_GE) + v29:Fixnum = GuardType v11, Fixnum + v30:Fixnum = GuardType v12, Fixnum + v22:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_eliminate_fixnum_eq() { + eval(" + def test(a, b) + a == b + 5 + end + test(1, 2); test(3, 4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_EQ) + v29:Fixnum = GuardType v11, Fixnum + v30:Fixnum = GuardType v12, Fixnum + v22:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_eliminate_fixnum_neq() { + eval(" + def test(a, b) + a != b + 5 + end + test(1, 2); test(3, 4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_EQ) + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_NEQ) + v30:Fixnum = GuardType v11, Fixnum + v31:Fixnum = GuardType v12, Fixnum + v22:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_do_not_eliminate_get_constant_path() { + eval(" + def test() + C + 5 + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = GetConstantPath 0x1000 + v14:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v14 + "); + } + + #[test] + fn kernel_itself_const() { + eval(" + def test(x) = x.itself + test(0) # profile + test(1) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Integer@0x1000, itself@0x1008, cme:0x1010) + v22:Fixnum = GuardType v9, Fixnum + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v22 + "); + } + + #[test] + fn kernel_itself_known_type() { + eval(" + def test = [].itself + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:ArrayExact = NewArray + PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v11 + "); + } + + #[test] + fn eliminate_kernel_itself() { + eval(" + def test + x = [].itself + 1 + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v14:ArrayExact = NewArray + PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + IncrCounter inline_cfunc_optimized_send_count + PatchPoint NoEPEscape(test) + v21:Fixnum[1] = Const Value(1) + CheckInterrupts + Return v21 + "); + } + + #[test] + fn eliminate_module_name() { + eval(" + module M; end + def test + x = M.name + 1 + end + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, M) + v29:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(Module@0x1010, name@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(Module@0x1010) + IncrCounter inline_cfunc_optimized_send_count + v34:StringExact|NilClass = CCall name@0x1048, v29 + PatchPoint NoEPEscape(test) + v21:Fixnum[1] = Const Value(1) + CheckInterrupts + Return v21 + "); + } + + #[test] + fn eliminate_array_length() { + eval(" + def test + x = [].length + 5 + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v14:ArrayExact = NewArray + PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + IncrCounter inline_cfunc_optimized_send_count + v31:Fixnum = CCall length@0x1038, v14 + v21:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v21 + "); + } + + #[test] + fn normal_class_type_inference() { + eval(" + class C; end + def test = C + test # Warm the constant cache + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, C) + v19:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + CheckInterrupts + Return v19 + "); + } + + #[test] + fn core_classes_type_inference() { + eval(" + def test = [String, Class, Module, BasicObject] + test # Warm the constant cache + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, String) + v27:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1010, Class) + v30:Class[VALUE(0x1018)] = Const Value(VALUE(0x1018)) + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1020, Module) + v33:Class[VALUE(0x1028)] = Const Value(VALUE(0x1028)) + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1030, BasicObject) + v36:Class[VALUE(0x1038)] = Const Value(VALUE(0x1038)) + v19:ArrayExact = NewArray v27, v30, v33, v36 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn module_instances_are_module_exact() { + eval(" + def test = [Enumerable, Kernel] + test # Warm the constant cache + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, Enumerable) + v23:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1010, Kernel) + v26:ModuleExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) + v15:ArrayExact = NewArray v23, v26 + CheckInterrupts + Return v15 + "); + } + + #[test] + fn module_subclasses_are_not_module_exact() { + eval(" + class ModuleSubclass < Module; end + MY_MODULE = ModuleSubclass.new + def test = MY_MODULE + test # Warm the constant cache + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, MY_MODULE) + v19:ModuleSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + CheckInterrupts + Return v19 + "); + } + + #[test] + fn eliminate_array_size() { + eval(" + def test + x = [].size + 5 + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v14:ArrayExact = NewArray + PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + IncrCounter inline_cfunc_optimized_send_count + v31:Fixnum = CCall size@0x1038, v14 + v21:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v21 + "); + } + + #[test] + fn kernel_itself_argc_mismatch() { + eval(" + def test = 1.itself(0) + test rescue 0 + test rescue 0 + "); + // Not specialized + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + v11:Fixnum[0] = Const Value(0) + v13:BasicObject = SendWithoutBlock v10, :itself, v11 + CheckInterrupts + Return v13 + "); + } + + #[test] + fn test_inline_kernel_block_given_p() { + eval(" + def test = block_given? + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v21:BoolExact = IsBlockGiven + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v21 + "); + } + + #[test] + fn test_inline_kernel_block_given_p_in_block() { + eval(" + TEST = proc { block_given? } + TEST.call + "); + assert_snapshot!(hir_string_proc("TEST"), @r" + fn block in @:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v21:BoolExact = IsBlockGiven + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v21 + "); + } + + #[test] + fn test_elide_kernel_block_given_p() { + eval(" + def test + block_given? + 5 + end + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v23:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + IncrCounter inline_cfunc_optimized_send_count + v14:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v14 + "); + } + + #[test] + fn const_send_direct_integer() { + eval(" + def test(x) = 1.zero? + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[1] = Const Value(1) + PatchPoint MethodRedefined(Integer@0x1000, zero?@0x1008, cme:0x1010) + IncrCounter inline_iseq_optimized_send_count + v24:BasicObject = InvokeBuiltin leaf _bi285, v13 + CheckInterrupts + Return v24 + "); + } + + #[test] + fn class_known_send_direct_array() { + eval(" + def test(x) + a = [1,2,3] + a.first + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject): + EntryPoint JIT(0) + v8:NilClass = Const Value(nil) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:NilClass): + v16:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v18:ArrayExact = ArrayDup v16 + PatchPoint MethodRedefined(Array@0x1008, first@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Array@0x1008) + IncrCounter inline_iseq_optimized_send_count + v32:BasicObject = InvokeBuiltin leaf _bi132, v18 + CheckInterrupts + Return v32 + "); + } + + #[test] + fn send_direct_to_module() { + eval(" + module M; end + def test = M.class + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, M) + v21:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(Module@0x1010, class@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(Module@0x1010) + IncrCounter inline_iseq_optimized_send_count + v26:Class = InvokeBuiltin leaf _bi20, v21 + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_send_direct_to_instance_method() { + eval(" + class C + def foo = [] + end + + def test(c) = c.foo + c = C.new + test c + test c + "); + + assert_snapshot!(hir_string("test"), @r" + fn test@:6: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(C@0x1000) + v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + v23:BasicObject = SendWithoutBlockDirect v22, :foo (0x1038) + CheckInterrupts + Return v23 + "); + } + + #[test] + fn dont_specialize_call_to_iseq_with_opt() { + eval(" + def foo(arg=1) = 1 + def test = foo 1 + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + v12:BasicObject = SendWithoutBlock v6, :foo, v10 + CheckInterrupts + Return v12 + "); + } + + #[test] + fn dont_specialize_call_to_iseq_with_block() { + eval(" + def foo(&block) = 1 + def test = foo {|| } + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = Send v6, 0x1000, :foo + CheckInterrupts + Return v11 + "); + } + + #[test] + fn reload_local_across_send() { + eval(" + def foo(&block) = 1 + def test + a = 1 + foo {|| } + a + end + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:Fixnum[1] = Const Value(1) + SetLocal l0, EP@3, v13 + v18:BasicObject = Send v8, 0x1000, :foo + v19:BasicObject = GetLocal l0, EP@3 + v22:BasicObject = GetLocal l0, EP@3 + CheckInterrupts + Return v22 + "); + } + + #[test] + fn dont_specialize_call_to_iseq_with_rest() { + eval(" + def foo(*args) = 1 + def test = foo 1 + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + v12:BasicObject = SendWithoutBlock v6, :foo, v10 + CheckInterrupts + Return v12 + "); + } + + #[test] + fn dont_specialize_call_to_iseq_with_kw() { + eval(" + def foo(a:) = 1 + def test = foo(a: 1) + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + SideExit UnhandledCallType(Kwarg) + "); + } + + #[test] + fn dont_specialize_call_to_iseq_with_kwrest() { + eval(" + def foo(**args) = 1 + def test = foo(a: 1) + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + SideExit UnhandledCallType(Kwarg) + "); + } + + #[test] + fn string_bytesize_simple() { + eval(" + def test = 'abc'.bytesize + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v12:StringExact = StringCopy v10 + PatchPoint MethodRedefined(String@0x1008, bytesize@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(String@0x1008) + IncrCounter inline_cfunc_optimized_send_count + v24:Fixnum = CCall bytesize@0x1040, v12 + CheckInterrupts + Return v24 + "); + } + + #[test] + fn dont_replace_get_constant_path_with_empty_ic() { + eval(" + def test = Kernel + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = GetConstantPath 0x1000 + CheckInterrupts + Return v11 + "); + } + + #[test] + fn dont_replace_get_constant_path_with_invalidated_ic() { + eval(" + def test = Kernel + test + Kernel = 5 + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = GetConstantPath 0x1000 + CheckInterrupts + Return v11 + "); + } + + #[test] + fn replace_get_constant_path_with_const() { + eval(" + def test = Kernel + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, Kernel) + v19:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + CheckInterrupts + Return v19 + "); + } + + #[test] + fn replace_nested_get_constant_path_with_const() { + eval(" + module Foo + module Bar + class C + end + end + end + def test = Foo::Bar::C + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:8: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, Foo::Bar::C) + v19:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_opt_new_no_initialize() { + eval(" + class C; end + def test = C.new + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, C) + v40:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) + PatchPoint MethodRedefined(C@0x1008, new@0x1010, cme:0x1018) + v43:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) + PatchPoint MethodRedefined(C@0x1008, initialize@0x1040, cme:0x1048) + PatchPoint NoSingletonClass(C@0x1008) + v47:NilClass = Const Value(nil) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + CheckInterrupts + Return v43 + "); + } + + #[test] + fn test_opt_new_initialize() { + eval(" + class C + def initialize x + @x = x + end + end + def test = C.new 1 + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:7: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, C) + v42:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) + v13:Fixnum[1] = Const Value(1) + PatchPoint MethodRedefined(C@0x1008, new@0x1010, cme:0x1018) + v45:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) + PatchPoint MethodRedefined(C@0x1008, initialize@0x1040, cme:0x1048) + PatchPoint NoSingletonClass(C@0x1008) + v48:BasicObject = SendWithoutBlockDirect v45, :initialize (0x1070), v13 + CheckInterrupts + CheckInterrupts + Return v45 + "); + } + + #[test] + fn test_opt_new_object() { + eval(" + def test = Object.new + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, Object) + v40:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) + PatchPoint MethodRedefined(Object@0x1008, new@0x1010, cme:0x1018) + v43:ObjectExact = ObjectAllocClass Object:VALUE(0x1008) + PatchPoint MethodRedefined(Object@0x1008, initialize@0x1040, cme:0x1048) + PatchPoint NoSingletonClass(Object@0x1008) + v47:NilClass = Const Value(nil) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + CheckInterrupts + Return v43 + "); + } + + #[test] + fn test_opt_new_basic_object() { + eval(" + def test = BasicObject.new + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, BasicObject) + v40:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) + PatchPoint MethodRedefined(BasicObject@0x1008, new@0x1010, cme:0x1018) + v43:BasicObjectExact = ObjectAllocClass BasicObject:VALUE(0x1008) + PatchPoint MethodRedefined(BasicObject@0x1008, initialize@0x1040, cme:0x1048) + PatchPoint NoSingletonClass(BasicObject@0x1008) + v47:NilClass = Const Value(nil) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + CheckInterrupts + Return v43 + "); + } + + #[test] + fn test_opt_new_hash() { + eval(" + def test = Hash.new + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, Hash) + v40:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) + PatchPoint MethodRedefined(Hash@0x1008, new@0x1010, cme:0x1018) + v43:HashExact = ObjectAllocClass Hash:VALUE(0x1008) + v18:BasicObject = SendWithoutBlock v43, :initialize + CheckInterrupts + CheckInterrupts + Return v43 + "); + assert_snapshot!(inspect("test"), @"{}"); + } + + #[test] + fn test_opt_new_array() { + eval(" + def test = Array.new 1 + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, Array) + v42:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) + v13:Fixnum[1] = Const Value(1) + PatchPoint MethodRedefined(Array@0x1008, new@0x1010, cme:0x1018) + PatchPoint MethodRedefined(Class@0x1040, new@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Class@0x1040) + v53:BasicObject = CCallVariadic new@0x1048, v42, v13 + CheckInterrupts + Return v53 + "); + } + + #[test] + fn test_opt_new_set() { + eval(" + def test = Set.new + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, Set) + v40:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) + PatchPoint MethodRedefined(Set@0x1008, new@0x1010, cme:0x1018) + v16:HeapBasicObject = ObjectAlloc v40 + PatchPoint MethodRedefined(Set@0x1008, initialize@0x1040, cme:0x1048) + PatchPoint NoSingletonClass(Set@0x1008) + v46:SetExact = GuardType v16, SetExact + v47:BasicObject = CCallVariadic initialize@0x1070, v46 + CheckInterrupts + CheckInterrupts + Return v16 + "); + } + + #[test] + fn test_opt_new_string() { + eval(" + def test = String.new + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, String) + v40:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) + PatchPoint MethodRedefined(String@0x1008, new@0x1010, cme:0x1018) + PatchPoint MethodRedefined(Class@0x1040, new@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Class@0x1040) + v51:BasicObject = CCallVariadic new@0x1048, v40 + CheckInterrupts + Return v51 + "); + } + + #[test] + fn test_opt_new_regexp() { + eval(" + def test = Regexp.new '' + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, Regexp) + v44:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) + v13:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) + v15:StringExact = StringCopy v13 + PatchPoint MethodRedefined(Regexp@0x1008, new@0x1018, cme:0x1020) + v47:RegexpExact = ObjectAllocClass Regexp:VALUE(0x1008) + PatchPoint MethodRedefined(Regexp@0x1008, initialize@0x1048, cme:0x1050) + PatchPoint NoSingletonClass(Regexp@0x1008) + v51:BasicObject = CCallVariadic initialize@0x1078, v47, v15 + CheckInterrupts + CheckInterrupts + Return v47 + "); + } + + #[test] + fn test_opt_length() { + eval(" + def test(a,b) = [a,b].length + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v17:ArrayExact = NewArray v11, v12 + PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + IncrCounter inline_cfunc_optimized_send_count + v31:Fixnum = CCall length@0x1038, v17 + CheckInterrupts + Return v31 + "); + } + + #[test] + fn test_opt_size() { + eval(" + def test(a,b) = [a,b].size + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v17:ArrayExact = NewArray v11, v12 + PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + IncrCounter inline_cfunc_optimized_send_count + v31:Fixnum = CCall size@0x1038, v17 + CheckInterrupts + Return v31 + "); + } + + #[test] + fn test_getblockparamproxy() { + eval(" + def test(&block) = tap(&block) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + GuardBlockParamProxy l0 + v15:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1000)) + v17:BasicObject = Send v8, 0x1008, :tap, v15 + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_getinstancevariable() { + eval(" + def test = @foo + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + v12:BasicObject = GetIvar v6, :@foo + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_setinstancevariable() { + eval(" + def test = @foo = 1 + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + PatchPoint SingleRactorMode + SetIvar v6, :@foo, v10 + CheckInterrupts + Return v10 + "); + } + + #[test] + fn test_elide_freeze_with_frozen_hash() { + eval(" + def test = {}.freeze + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) + v12:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_dont_optimize_hash_freeze_if_redefined() { + eval(" + class Hash + def freeze; end + end + def test = {}.freeze + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + SideExit PatchPoint(BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE)) + "); + } + + #[test] + fn test_elide_freeze_with_refrozen_hash() { + eval(" + def test = {}.freeze.freeze + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) + v12:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_no_elide_freeze_with_unfrozen_hash() { + eval(" + def test = {}.dup.freeze + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:HashExact = NewHash + PatchPoint MethodRedefined(Hash@0x1000, dup@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Hash@0x1000) + v24:BasicObject = CCallWithFrame dup@0x1038, v11 + v15:BasicObject = SendWithoutBlock v24, :freeze + CheckInterrupts + Return v15 + "); + } + + #[test] + fn test_no_elide_freeze_hash_with_args() { + eval(" + def test = {}.freeze(nil) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:HashExact = NewHash + v12:NilClass = Const Value(nil) + v14:BasicObject = SendWithoutBlock v11, :freeze, v12 + CheckInterrupts + Return v14 + "); + } + + #[test] + fn test_elide_freeze_with_frozen_ary() { + eval(" + def test = [].freeze + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + v12:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_elide_freeze_with_refrozen_ary() { + eval(" + def test = [].freeze.freeze + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + v12:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_no_elide_freeze_with_unfrozen_ary() { + eval(" + def test = [].dup.freeze + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:ArrayExact = NewArray + PatchPoint MethodRedefined(Array@0x1000, dup@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v24:BasicObject = CCallWithFrame dup@0x1038, v11 + v15:BasicObject = SendWithoutBlock v24, :freeze + CheckInterrupts + Return v15 + "); + } + + #[test] + fn test_no_elide_freeze_ary_with_args() { + eval(" + def test = [].freeze(nil) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:ArrayExact = NewArray + v12:NilClass = Const Value(nil) + v14:BasicObject = SendWithoutBlock v11, :freeze, v12 + CheckInterrupts + Return v14 + "); + } + + #[test] + fn test_elide_freeze_with_frozen_str() { + eval(" + def test = ''.freeze + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) + v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_elide_freeze_with_refrozen_str() { + eval(" + def test = ''.freeze.freeze + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) + v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_no_elide_freeze_with_unfrozen_str() { + eval(" + def test = ''.dup.freeze + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v12:StringExact = StringCopy v10 + PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(String@0x1008) + v25:BasicObject = CCallWithFrame dup@0x1040, v12 + v16:BasicObject = SendWithoutBlock v25, :freeze + CheckInterrupts + Return v16 + "); + } + + #[test] + fn test_no_elide_freeze_str_with_args() { + eval(" + def test = ''.freeze(nil) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v12:StringExact = StringCopy v10 + v13:NilClass = Const Value(nil) + v15:BasicObject = SendWithoutBlock v12, :freeze, v13 + CheckInterrupts + Return v15 + "); + } + + #[test] + fn test_elide_uminus_with_frozen_str() { + eval(" + def test = -'' + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS) + v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_elide_uminus_with_refrozen_str() { + eval(" + def test = -''.freeze + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) + v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS) + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_no_elide_uminus_with_unfrozen_str() { + eval(" + def test = -''.dup + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v12:StringExact = StringCopy v10 + PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(String@0x1008) + v25:BasicObject = CCallWithFrame dup@0x1040, v12 + v16:BasicObject = SendWithoutBlock v25, :-@ + CheckInterrupts + Return v16 + "); + } + + #[test] + fn test_objtostring_anytostring_string() { + eval(r##" + def test = "#{('foo')}" + "##); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v13:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v15:StringExact = StringCopy v13 + v21:StringExact = StringConcat v10, v15 + CheckInterrupts + Return v21 + "); + } + + #[test] + fn test_objtostring_anytostring_with_non_string() { + eval(r##" + def test = "#{1}" + "##); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v11:Fixnum[1] = Const Value(1) + v13:BasicObject = ObjToString v11 + v15:String = AnyToString v11, str: v13 + v17:StringExact = StringConcat v10, v15 + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_optimize_objtostring_anytostring_recv_profiled() { + eval(" + def test(a) + \"#{a}\" + end + test('foo'); test('foo') + "); + + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + PatchPoint NoSingletonClass(String@0x1008) + v26:String = GuardType v9, String + v19:StringExact = StringConcat v13, v26 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_optimize_objtostring_anytostring_recv_profiled_string_subclass() { + eval(" + class MyString < String; end + + def test(a) + \"#{a}\" + end + foo = MyString.new('foo') + test(MyString.new(foo)); test(MyString.new(foo)) + "); + + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + PatchPoint NoSingletonClass(MyString@0x1008) + v26:String = GuardType v9, String + v19:StringExact = StringConcat v13, v26 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_optimize_objtostring_profiled_nonstring_falls_back_to_send() { + eval(" + def test(a) + \"#{a}\" + end + test([1,2,3]); test([1,2,3]) # No fast path for array + "); + + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v25:BasicObject = GuardTypeNot v9, String + PatchPoint MethodRedefined(Array@0x1008, to_s@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Array@0x1008) + v30:ArrayExact = GuardType v9, ArrayExact + v31:BasicObject = CCallWithFrame to_s@0x1040, v30 + v17:String = AnyToString v9, str: v31 + v19:StringExact = StringConcat v13, v17 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_branchnil_nil() { + eval(" + def test + x = nil + x&.itself + end + "); + + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:NilClass = Const Value(nil) + CheckInterrupts + CheckInterrupts + Return v13 + "); + } + + #[test] + fn test_branchnil_truthy() { + eval(" + def test + x = 1 + x&.itself + end + "); + + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:Fixnum[1] = Const Value(1) + CheckInterrupts + PatchPoint MethodRedefined(Integer@0x1000, itself@0x1008, cme:0x1010) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v13 + "); + } + + #[test] + fn test_dont_eliminate_load_from_non_frozen_array() { + eval(r##" + S = [4,5,6] + def test = S[0] + test + "##); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, S) + v24:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:Fixnum[0] = Const Value(0) + PatchPoint MethodRedefined(Array@0x1010, []@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(Array@0x1010) + v28:BasicObject = ArrayArefFixnum v24, v12 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v28 + "); + // TODO(max): Check the result of `S[0] = 5; test` using `inspect` to make sure that we + // actually do the load at run-time. + } + + #[test] + fn test_eliminate_load_from_frozen_array_in_bounds() { + eval(r##" + def test = [4,5,6].freeze[1] + "##); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + v12:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v13:Fixnum[1] = Const Value(1) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Array@0x1008) + v28:Fixnum[5] = Const Value(5) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_eliminate_load_from_frozen_array_negative() { + eval(r##" + def test = [4,5,6].freeze[-3] + "##); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + v12:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v13:Fixnum[-3] = Const Value(-3) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Array@0x1008) + v28:Fixnum[4] = Const Value(4) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_eliminate_load_from_frozen_array_negative_out_of_bounds() { + eval(r##" + def test = [4,5,6].freeze[-10] + "##); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + v12:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v13:Fixnum[-10] = Const Value(-10) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Array@0x1008) + v28:NilClass = Const Value(nil) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_eliminate_load_from_frozen_array_out_of_bounds() { + eval(r##" + def test = [4,5,6].freeze[10] + "##); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + v12:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v13:Fixnum[10] = Const Value(10) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Array@0x1008) + v28:NilClass = Const Value(nil) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_dont_optimize_array_aref_if_redefined() { + eval(r##" + class Array + def [](index) = [] + end + def test = [4,5,6].freeze[10] + "##); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + v12:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v13:Fixnum[10] = Const Value(10) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Array@0x1008) + v25:BasicObject = SendWithoutBlockDirect v12, :[] (0x1040), v13 + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_dont_optimize_array_max_if_redefined() { + eval(r##" + class Array + def max = [] + end + def test = [4,5,6].max + "##); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v12:ArrayExact = ArrayDup v10 + PatchPoint MethodRedefined(Array@0x1008, max@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Array@0x1008) + v22:BasicObject = SendWithoutBlockDirect v12, :max (0x1040) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_set_type_from_constant() { + eval(" + MY_SET = Set.new + + def test = MY_SET + + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, MY_SET) + v19:SetExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_regexp_type() { + eval(" + def test = /a/ + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:RegexpExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + CheckInterrupts + Return v10 + "); + } + + #[test] + fn test_bmethod_send_direct() { + eval(" + define_method(:zero) { :b } + define_method(:one) { |arg| arg } + + def test = one(zero) + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint MethodRedefined(Object@0x1000, zero@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v22:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + IncrCounter inline_iseq_optimized_send_count + v30:StaticSymbol[:b] = Const Value(VALUE(0x1038)) + PatchPoint SingleRactorMode + PatchPoint MethodRedefined(Object@0x1000, one@0x1040, cme:0x1048) + PatchPoint NoSingletonClass(Object@0x1000) + v27:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + IncrCounter inline_iseq_optimized_send_count + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_symbol_block_bmethod() { + eval(" + define_method(:identity, &:itself) + def test = identity(100) + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[100] = Const Value(100) + v12:BasicObject = SendWithoutBlock v6, :identity, v10 + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_call_bmethod_with_block() { + eval(" + define_method(:bmethod) { :b } + def test = (bmethod {}) + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = Send v6, 0x1000, :bmethod + CheckInterrupts + Return v11 + "); + } + + #[test] + fn test_call_shareable_bmethod() { + eval(" + class Foo + class << self + define_method(:identity, &(Ractor.make_shareable ->(val){val})) + end + end + def test = Foo.identity(100) + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:7: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, Foo) + v22:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:Fixnum[100] = Const Value(100) + PatchPoint MethodRedefined(Class@0x1010, identity@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(Class@0x1010) + IncrCounter inline_iseq_optimized_send_count + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_nil_nil_specialized_to_ccall() { + eval(" + def test = nil.nil? + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:NilClass = Const Value(nil) + PatchPoint MethodRedefined(NilClass@0x1000, nil?@0x1008, cme:0x1010) + v22:TrueClass = Const Value(true) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_eliminate_nil_nil_specialized_to_ccall() { + eval(" + def test + nil.nil? + 1 + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:NilClass = Const Value(nil) + PatchPoint MethodRedefined(NilClass@0x1000, nil?@0x1008, cme:0x1010) + IncrCounter inline_cfunc_optimized_send_count + v17:Fixnum[1] = Const Value(1) + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_non_nil_nil_specialized_to_ccall() { + eval(" + def test = 1.nil? + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + PatchPoint MethodRedefined(Integer@0x1000, nil?@0x1008, cme:0x1010) + v22:FalseClass = Const Value(false) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_eliminate_non_nil_nil_specialized_to_ccall() { + eval(" + def test + 1.nil? + 2 + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + PatchPoint MethodRedefined(Integer@0x1000, nil?@0x1008, cme:0x1010) + IncrCounter inline_cfunc_optimized_send_count + v17:Fixnum[2] = Const Value(2) + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_guard_nil_for_nil_opt() { + eval(" + def test(val) = val.nil? + + test(nil) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(NilClass@0x1000, nil?@0x1008, cme:0x1010) + v24:NilClass = GuardType v9, NilClass + v25:TrueClass = Const Value(true) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_guard_false_for_nil_opt() { + eval(" + def test(val) = val.nil? + + test(false) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(FalseClass@0x1000, nil?@0x1008, cme:0x1010) + v24:FalseClass = GuardType v9, FalseClass + v25:FalseClass = Const Value(false) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_guard_true_for_nil_opt() { + eval(" + def test(val) = val.nil? + + test(true) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(TrueClass@0x1000, nil?@0x1008, cme:0x1010) + v24:TrueClass = GuardType v9, TrueClass + v25:FalseClass = Const Value(false) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_guard_symbol_for_nil_opt() { + eval(" + def test(val) = val.nil? + + test(:foo) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Symbol@0x1000, nil?@0x1008, cme:0x1010) + v24:StaticSymbol = GuardType v9, StaticSymbol + v25:FalseClass = Const Value(false) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_guard_fixnum_for_nil_opt() { + eval(" + def test(val) = val.nil? + + test(1) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Integer@0x1000, nil?@0x1008, cme:0x1010) + v24:Fixnum = GuardType v9, Fixnum + v25:FalseClass = Const Value(false) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_guard_float_for_nil_opt() { + eval(" + def test(val) = val.nil? + + test(1.0) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Float@0x1000, nil?@0x1008, cme:0x1010) + v24:Flonum = GuardType v9, Flonum + v25:FalseClass = Const Value(false) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_guard_string_for_nil_opt() { + eval(" + def test(val) = val.nil? + + test('foo') + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(String@0x1000, nil?@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v25:StringExact = GuardType v9, StringExact + v26:FalseClass = Const Value(false) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_specialize_basicobject_not_to_ccall() { + eval(" + def test(a) = !a + + test([]) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Array@0x1000, !@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v25:ArrayExact = GuardType v9, ArrayExact + IncrCounter inline_cfunc_optimized_send_count + v27:BoolExact = CCall !@0x1038, v25 + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_specialize_array_empty_p_to_ccall() { + eval(" + def test(a) = a.empty? + + test([]) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Array@0x1000, empty?@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v25:ArrayExact = GuardType v9, ArrayExact + IncrCounter inline_cfunc_optimized_send_count + v27:BoolExact = CCall empty?@0x1038, v25 + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_specialize_hash_empty_p_to_ccall() { + eval(" + def test(a) = a.empty? + + test({}) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Hash@0x1000, empty?@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Hash@0x1000) + v25:HashExact = GuardType v9, HashExact + IncrCounter inline_cfunc_optimized_send_count + v27:BoolExact = CCall empty?@0x1038, v25 + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_specialize_basic_object_eq_to_ccall() { + eval(" + class C; end + def test(a, b) = a == b + + test(C.new, C.new) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(C@0x1000, ==@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(C@0x1000) + v28:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C] + v29:CBool = IsBitEqual v28, v12 + v30:BoolExact = BoxBool v29 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_guard_fixnum_and_fixnum() { + eval(" + def test(x, y) = x & y + + test(1, 2) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, 28) + v26:Fixnum = GuardType v11, Fixnum + v27:Fixnum = GuardType v12, Fixnum + v28:Fixnum = FixnumAnd v26, v27 + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_guard_fixnum_or_fixnum() { + eval(" + def test(x, y) = x | y + + test(1, 2) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, 29) + v26:Fixnum = GuardType v11, Fixnum + v27:Fixnum = GuardType v12, Fixnum + v28:Fixnum = FixnumOr v26, v27 + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_method_redefinition_patch_point_on_top_level_method() { + eval(" + def foo; end + def test = foo + + test; test + "); + + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + IncrCounter inline_iseq_optimized_send_count + v22:NilClass = Const Value(nil) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_optimize_getivar_embedded() { + eval(" + class C + attr_reader :foo + def initialize + @foo = 42 + end + end + + O = C.new + def test(o) = o.foo + test O + test O + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:10: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(C@0x1000) + v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + v25:HeapObject[class_exact:C] = GuardShape v22, 0x1038 + v26:BasicObject = LoadIvarEmbedded v25, :@foo@0x1039 + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_optimize_getivar_extended() { + eval(" + class C + attr_reader :foo + def initialize + @foo = 42 + end + end + + O = C.new + def test(o) = o.foo + test O + test O + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:10: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(C@0x1000) + v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + v25:HeapObject[class_exact:C] = GuardShape v22, 0x1038 + v26:BasicObject = LoadIvarEmbedded v25, :@foo@0x1039 + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_dont_optimize_getivar_polymorphic() { + set_call_threshold(3); + eval(" + class C + attr_reader :foo, :bar + + def foo_then_bar + @foo = 1 + @bar = 2 + end + + def bar_then_foo + @bar = 3 + @foo = 4 + end + end + + O1 = C.new + O1.foo_then_bar + O2 = C.new + O2.bar_then_foo + def test(o) = o.foo + test O1 + test O2 + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:20: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:BasicObject = SendWithoutBlock v9, :foo + CheckInterrupts + Return v14 + "); + } + + #[test] + fn test_optimize_send_with_block() { + eval(r#" + def test = [1, 2, 3].map { |x| x * 2 } + test; test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v12:ArrayExact = ArrayDup v10 + PatchPoint MethodRedefined(Array@0x1008, map@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Array@0x1008) + v23:BasicObject = CCallWithFrame map@0x1040, v12, block=0x1048 + CheckInterrupts + Return v23 + "); + } + + #[test] + fn test_do_not_optimize_send_variadic_with_block() { + eval(r#" + def test = [1, 2, 3].index { |x| x == 2 } + test; test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v12:ArrayExact = ArrayDup v10 + v14:BasicObject = Send v12, 0x1008, :index + CheckInterrupts + Return v14 + "); + } + + #[test] + fn test_do_not_optimize_send_with_block_forwarding() { + eval(r#" + def test(&block) = [].map(&block) + test; test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:ArrayExact = NewArray + GuardBlockParamProxy l0 + v17:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1000)) + v19:BasicObject = Send v14, 0x1008, :map, v17 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_do_not_optimize_send_to_iseq_method_with_block() { + eval(r#" + def foo + yield 1 + end + + def test = foo {} + test; test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:6: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = Send v6, 0x1000, :foo + CheckInterrupts + Return v11 + "); + } + + #[test] + fn test_inline_attr_reader_constant() { + eval(" + class C + attr_reader :foo + end + + O = C.new + def test = O.foo + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:7: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, O) + v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(C@0x1010) + v26:HeapObject[VALUE(0x1008)] = GuardShape v21, 0x1048 + v27:NilClass = Const Value(nil) + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_inline_attr_accessor_constant() { + eval(" + class C + attr_accessor :foo + end + + O = C.new + def test = O.foo + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:7: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, O) + v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(C@0x1010) + v26:HeapObject[VALUE(0x1008)] = GuardShape v21, 0x1048 + v27:NilClass = Const Value(nil) + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_inline_attr_reader() { + eval(" + class C + attr_reader :foo + end + + def test(o) = o.foo + test C.new + test C.new + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:6: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(C@0x1000) + v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + v25:HeapObject[class_exact:C] = GuardShape v22, 0x1038 + v26:NilClass = Const Value(nil) + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_inline_attr_accessor() { + eval(" + class C + attr_accessor :foo + end + + def test(o) = o.foo + test C.new + test C.new + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:6: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(C@0x1000) + v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + v25:HeapObject[class_exact:C] = GuardShape v22, 0x1038 + v26:NilClass = Const Value(nil) + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_inline_attr_accessor_set() { + eval(" + class C + attr_accessor :foo + end + + def test(o) = o.foo = 5 + test C.new + test C.new + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:6: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:Fixnum[5] = Const Value(5) + PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010) + v23:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + SetIvar v23, :@foo, v14 + CheckInterrupts + Return v14 + "); + } + + #[test] + fn test_inline_attr_writer_set() { + eval(" + class C + attr_writer :foo + end + + def test(o) = o.foo = 5 + test C.new + test C.new + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:6: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:Fixnum[5] = Const Value(5) + PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010) + v23:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + SetIvar v23, :@foo, v14 + CheckInterrupts + Return v14 + "); + } + + #[test] + fn test_array_reverse_returns_array() { + eval(r#" + def test = [].reverse + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:ArrayExact = NewArray + PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v22:ArrayExact = CCallWithFrame reverse@0x1038, v11 + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_array_reverse_is_elidable() { + eval(r#" + def test + [].reverse + 5 + end + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:ArrayExact = NewArray + PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v16:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v16 + "); + } + + #[test] + fn test_array_join_returns_string() { + eval(r#" + def test = [].join "," + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:ArrayExact = NewArray + v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v14:StringExact = StringCopy v12 + PatchPoint MethodRedefined(Array@0x1008, join@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Array@0x1008) + v25:StringExact = CCallVariadic join@0x1040, v11, v14 + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_string_to_s_returns_string() { + eval(r#" + def test = "".to_s + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v12:StringExact = StringCopy v10 + PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(String@0x1008) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_inline_string_literal_to_s() { + eval(r#" + def test = "foo".to_s + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v12:StringExact = StringCopy v10 + PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(String@0x1008) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_inline_profiled_string_to_s() { + eval(r#" + def test(o) = o.to_s + test "foo" + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(String@0x1000, to_s@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v23:StringExact = GuardType v9, StringExact + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v23 + "); + } + + #[test] + fn test_array_aref_fixnum_literal() { + eval(" + def test + arr = [1, 2, 3] + arr[0] + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v15:ArrayExact = ArrayDup v13 + v18:Fixnum[0] = Const Value(0) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Array@0x1008) + v31:BasicObject = ArrayArefFixnum v15, v18 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v31 + "); + } + + #[test] + fn test_array_aref_fixnum_profiled() { + eval(" + def test(arr, idx) + arr[idx] + end + test([1, 2, 3], 0) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v28:ArrayExact = GuardType v11, ArrayExact + v29:Fixnum = GuardType v12, Fixnum + v30:BasicObject = ArrayArefFixnum v28, v29 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_array_aref_fixnum_array_subclass() { + eval(" + class C < Array; end + def test(arr, idx) + arr[idx] + end + test(C.new([1, 2, 3]), 0) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(C@0x1000, []@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(C@0x1000) + v28:ArraySubclass[class_exact:C] = GuardType v11, ArraySubclass[class_exact:C] + v29:Fixnum = GuardType v12, Fixnum + v30:BasicObject = ArrayArefFixnum v28, v29 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_hash_aref_literal() { + eval(" + def test + arr = {1 => 3} + arr[1] + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v15:HashExact = HashDup v13 + v18:Fixnum[1] = Const Value(1) + PatchPoint MethodRedefined(Hash@0x1008, []@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Hash@0x1008) + v31:BasicObject = HashAref v15, v18 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v31 + "); + } + + #[test] + fn test_hash_aref_profiled() { + eval(" + def test(hash, key) + hash[key] + end + test({1 => 3}, 1) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Hash@0x1000) + v28:HashExact = GuardType v11, HashExact + v29:BasicObject = HashAref v28, v12 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v29 + "); + } + + #[test] + fn test_hash_aref_subclass() { + eval(" + class C < Hash; end + def test(hash, key) + hash[key] + end + test(C.new({0 => 3}), 0) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(C@0x1000, []@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(C@0x1000) + v28:HashSubclass[class_exact:C] = GuardType v11, HashSubclass[class_exact:C] + v29:BasicObject = HashAref v28, v12 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v29 + "); + } + + #[test] + fn test_does_not_fold_hash_aref_with_frozen_hash() { + eval(" + H = {a: 0}.freeze + def test = H[:a] + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, H) + v24:HashExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:StaticSymbol[:a] = Const Value(VALUE(0x1010)) + PatchPoint MethodRedefined(Hash@0x1018, []@0x1020, cme:0x1028) + PatchPoint NoSingletonClass(Hash@0x1018) + v28:BasicObject = HashAref v24, v12 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_optimize_thread_current() { + eval(" + def test = Thread.current + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, Thread) + v21:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(Class@0x1010, current@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(Class@0x1010) + IncrCounter inline_cfunc_optimized_send_count + v26:BasicObject = CCall current@0x1048, v21 + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_optimize_array_aset() { + eval(" + def test(arr) + arr[1] = 10 + end + test([]) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:Fixnum[1] = Const Value(1) + v15:Fixnum[10] = Const Value(10) + PatchPoint MethodRedefined(Array@0x1000, []=@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v28:ArrayExact = GuardType v9, ArrayExact + v29:BasicObject = CCallVariadic []=@0x1038, v28, v14, v15 + CheckInterrupts + Return v15 + "); + } + + #[test] + fn test_optimize_array_ltlt() { + eval(" + def test(arr) + arr << 1 + end + test([]) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[1] = Const Value(1) + PatchPoint MethodRedefined(Array@0x1000, <<@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v26:ArrayExact = GuardType v9, ArrayExact + ArrayPush v26, v13 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_optimize_array_push_single_arg() { + eval(" + def test(arr) + arr.push(1) + end + test([]) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[1] = Const Value(1) + PatchPoint MethodRedefined(Array@0x1000, push@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v24:ArrayExact = GuardType v9, ArrayExact + ArrayPush v24, v13 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v24 + "); + } + + #[test] + fn test_do_not_optimize_array_push_multi_arg() { + eval(" + def test(arr) + arr.push(1,2,3) + end + test([]) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[1] = Const Value(1) + v14:Fixnum[2] = Const Value(2) + v15:Fixnum[3] = Const Value(3) + PatchPoint MethodRedefined(Array@0x1000, push@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v26:ArrayExact = GuardType v9, ArrayExact + v27:BasicObject = CCallVariadic push@0x1038, v26, v13, v14, v15 + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_optimize_array_length() { + eval(" + def test(arr) = arr.length + test([]) + "); + assert_contains_opcode("test", YARVINSN_opt_length); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v25:ArrayExact = GuardType v9, ArrayExact + IncrCounter inline_cfunc_optimized_send_count + v27:Fixnum = CCall length@0x1038, v25 + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_optimize_array_size() { + eval(" + def test(arr) = arr.size + test([]) + "); + assert_contains_opcode("test", YARVINSN_opt_size); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v25:ArrayExact = GuardType v9, ArrayExact + IncrCounter inline_cfunc_optimized_send_count + v27:Fixnum = CCall size@0x1038, v25 + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_optimize_regexpmatch2() { + eval(r#" + def test(s) = s =~ /a/ + test("foo") + "#); + assert_contains_opcode("test", YARVINSN_opt_regexpmatch2); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:RegexpExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + PatchPoint MethodRedefined(String@0x1008, =~@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(String@0x1008) + v26:StringExact = GuardType v9, StringExact + v27:BasicObject = CCallWithFrame =~@0x1040, v26, v13 + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_optimize_string_getbyte_fixnum() { + eval(r#" + def test(s, i) = s.getbyte(i) + test("foo", 0) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(String@0x1000, getbyte@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v26:StringExact = GuardType v11, StringExact + v27:Fixnum = GuardType v12, Fixnum + v28:NilClass|Fixnum = StringGetbyteFixnum v26, v27 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_elide_string_getbyte_fixnum() { + eval(r#" + def test(s, i) + s.getbyte(i) + 5 + end + test("foo", 0) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(String@0x1000, getbyte@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v29:StringExact = GuardType v11, StringExact + v30:Fixnum = GuardType v12, Fixnum + IncrCounter inline_cfunc_optimized_send_count + v20:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v20 + "); + } + + #[test] + fn test_specialize_string_empty() { + eval(r#" + def test(s) + s.empty? + end + test("asdf") + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(String@0x1000, empty?@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v25:StringExact = GuardType v9, StringExact + IncrCounter inline_cfunc_optimized_send_count + v27:BoolExact = CCall empty?@0x1038, v25 + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_eliminate_string_empty() { + eval(r#" + def test(s) + s.empty? + 4 + end + test("this should get removed") + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(String@0x1000, empty?@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v28:StringExact = GuardType v9, StringExact + IncrCounter inline_cfunc_optimized_send_count + v19:Fixnum[4] = Const Value(4) + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_inline_integer_succ_with_fixnum() { + eval(" + def test(x) = x.succ + test(4) + "); + assert_contains_opcode("test", YARVINSN_opt_succ); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Integer@0x1000, succ@0x1008, cme:0x1010) + v24:Fixnum = GuardType v9, Fixnum + v25:Fixnum[1] = Const Value(1) + v26:Fixnum = FixnumAdd v24, v25 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_dont_inline_integer_succ_with_bignum() { + eval(" + def test(x) = x.succ + test(4 << 70) + "); + assert_contains_opcode("test", YARVINSN_opt_succ); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Integer@0x1000, succ@0x1008, cme:0x1010) + v24:Integer = GuardType v9, Integer + v25:BasicObject = CCallWithFrame succ@0x1038, v24 + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_optimize_string_append() { + eval(r#" + def test(x, y) = x << y + test("iron", "fish") + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v28:StringExact = GuardType v11, StringExact + v29:String = GuardType v12, String + v30:StringExact = StringAppend v28, v29 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v28 + "); + } + + // TODO: This should be inlined just as in the interpreter + #[test] + fn test_optimize_string_append_non_string() { + eval(r#" + def test(x, y) = x << y + test("iron", 4) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v28:StringExact = GuardType v11, StringExact + v29:BasicObject = CCallWithFrame <<@0x1038, v28, v12 + CheckInterrupts + Return v29 + "); + } + + #[test] + fn test_optimize_string_append_string_subclass() { + eval(r#" + class MyString < String + end + def test(x, y) = x << y + test("iron", MyString.new) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v28:StringExact = GuardType v11, StringExact + v29:String = GuardType v12, String + v30:StringExact = StringAppend v28, v29 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_do_not_optimize_string_subclass_append_string() { + eval(r#" + class MyString < String + end + def test(x, y) = x << y + test(MyString.new, "iron") + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(MyString@0x1000, <<@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(MyString@0x1000) + v28:StringSubclass[class_exact:MyString] = GuardType v11, StringSubclass[class_exact:MyString] + v29:BasicObject = CCallWithFrame <<@0x1038, v28, v12 + CheckInterrupts + Return v29 + "); + } + + #[test] + fn test_dont_inline_integer_succ_with_args() { + eval(" + def test = 4.succ 1 + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[4] = Const Value(4) + v11:Fixnum[1] = Const Value(1) + v13:BasicObject = SendWithoutBlock v10, :succ, v11 + CheckInterrupts + Return v13 + "); + } + + #[test] + fn test_inline_integer_xor_with_fixnum() { + eval(" + def test(x, y) = x ^ y + test(1, 2) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(Integer@0x1000, ^@0x1008, cme:0x1010) + v25:Fixnum = GuardType v11, Fixnum + v26:Fixnum = GuardType v12, Fixnum + v27:Fixnum = FixnumXor v25, v26 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_eliminate_integer_xor() { + eval(r#" + def test(x, y) + x ^ y + 42 + end + test(1, 2) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(Integer@0x1000, ^@0x1008, cme:0x1010) + v28:Fixnum = GuardType v11, Fixnum + v29:Fixnum = GuardType v12, Fixnum + IncrCounter inline_cfunc_optimized_send_count + v20:Fixnum[42] = Const Value(42) + CheckInterrupts + Return v20 + "); + } + + #[test] + fn test_dont_inline_integer_xor_with_bignum_or_boolean() { + eval(" + def test(x, y) = x ^ y + test(4 << 70, 1) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(Integer@0x1000, ^@0x1008, cme:0x1010) + v25:Integer = GuardType v11, Integer + v26:BasicObject = CCallWithFrame ^@0x1038, v25, v12 + CheckInterrupts + Return v26 + "); + + eval(" + def test(x, y) = x ^ y + test(1, 4 << 70) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(Integer@0x1000, ^@0x1008, cme:0x1010) + v25:Fixnum = GuardType v11, Fixnum + v26:BasicObject = CCallWithFrame ^@0x1038, v25, v12 + CheckInterrupts + Return v26 + "); + + eval(" + def test(x, y) = x ^ y + test(true, 0) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(TrueClass@0x1000, ^@0x1008, cme:0x1010) + v25:TrueClass = GuardType v11, TrueClass + v26:BasicObject = CCallWithFrame ^@0x1038, v25, v12 + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_dont_inline_integer_xor_with_args() { + eval(" + def test(x, y) = x.^() + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v17:BasicObject = SendWithoutBlock v11, :^ + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_specialize_hash_size() { + eval(" + def test(hash) = hash.size + test({foo: 3, bar: 1, baz: 4}) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Hash@0x1000, size@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Hash@0x1000) + v25:HashExact = GuardType v9, HashExact + IncrCounter inline_cfunc_optimized_send_count + v27:Fixnum = CCall size@0x1038, v25 + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_eliminate_hash_size() { + eval(" + def test(hash) + hash.size + 5 + end + test({foo: 3, bar: 1, baz: 4}) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Hash@0x1000, size@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Hash@0x1000) + v28:HashExact = GuardType v9, HashExact + IncrCounter inline_cfunc_optimized_send_count + v19:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_optimize_respond_to_p_true() { + eval(r#" + class C + def foo; end + end + def test(o) = o.respond_to?(:foo) + test(C.new) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(C@0x1008) + v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) + PatchPoint NoSingletonClass(C@0x1008) + v28:TrueClass = Const Value(true) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_optimize_respond_to_p_false_no_method() { + eval(r#" + class C + end + def test(o) = o.respond_to?(:foo) + test(C.new) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(C@0x1008) + v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + PatchPoint MethodRedefined(C@0x1008, respond_to_missing?@0x1040, cme:0x1048) + PatchPoint MethodRedefined(C@0x1008, foo@0x1070, cme:0x1078) + PatchPoint NoSingletonClass(C@0x1008) + v30:FalseClass = Const Value(false) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_optimize_respond_to_p_false_default_private() { + eval(r#" + class C + private + def foo; end + end + def test(o) = o.respond_to?(:foo) + test(C.new) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:6: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(C@0x1008) + v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) + PatchPoint NoSingletonClass(C@0x1008) + v28:FalseClass = Const Value(false) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_optimize_respond_to_p_false_private() { + eval(r#" + class C + private + def foo; end + end + def test(o) = o.respond_to?(:foo, false) + test(C.new) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:6: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) + v14:FalseClass = Const Value(false) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(C@0x1008) + v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) + PatchPoint NoSingletonClass(C@0x1008) + v29:FalseClass = Const Value(false) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v29 + "); + } + + #[test] + fn test_optimize_respond_to_p_falsy_private() { + eval(r#" + class C + private + def foo; end + end + def test(o) = o.respond_to?(:foo, nil) + test(C.new) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:6: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) + v14:NilClass = Const Value(nil) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(C@0x1008) + v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) + PatchPoint NoSingletonClass(C@0x1008) + v29:FalseClass = Const Value(false) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v29 + "); + } + + #[test] + fn test_optimize_respond_to_p_true_private() { + eval(r#" + class C + private + def foo; end + end + def test(o) = o.respond_to?(:foo, true) + test(C.new) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:6: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) + v14:TrueClass = Const Value(true) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(C@0x1008) + v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) + PatchPoint NoSingletonClass(C@0x1008) + v29:TrueClass = Const Value(true) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v29 + "); + } + + #[test] + fn test_optimize_respond_to_p_truthy() { + eval(r#" + class C + def foo; end + end + def test(o) = o.respond_to?(:foo, 4) + test(C.new) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) + v14:Fixnum[4] = Const Value(4) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(C@0x1008) + v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) + PatchPoint NoSingletonClass(C@0x1008) + v29:TrueClass = Const Value(true) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v29 + "); + } + + #[test] + fn test_optimize_respond_to_p_falsy() { + eval(r#" + class C + def foo; end + end + def test(o) = o.respond_to?(:foo, nil) + test(C.new) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) + v14:NilClass = Const Value(nil) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(C@0x1008) + v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) + PatchPoint NoSingletonClass(C@0x1008) + v29:TrueClass = Const Value(true) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v29 + "); + } + + #[test] + fn test_optimize_respond_to_missing() { + eval(r#" + class C + end + def test(o) = o.respond_to?(:foo) + test(C.new) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(C@0x1008) + v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + PatchPoint MethodRedefined(C@0x1008, respond_to_missing?@0x1040, cme:0x1048) + PatchPoint MethodRedefined(C@0x1008, foo@0x1070, cme:0x1078) + PatchPoint NoSingletonClass(C@0x1008) + v30:FalseClass = Const Value(false) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_do_not_optimize_redefined_respond_to_missing() { + eval(r#" + class C + def respond_to_missing?(method, include_private = false) + true + end + end + def test(o) = o.respond_to?(:foo) + test(C.new) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:7: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(C@0x1008) + v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + v25:BasicObject = CCallVariadic respond_to?@0x1040, v24, v13 + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_inline_send_without_block_direct_putself() { + eval(r#" + def callee = self + def test = callee + test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + IncrCounter inline_iseq_optimized_send_count + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_inline_send_without_block_direct_putobject_string() { + eval(r#" + # frozen_string_literal: true + def callee = "abc" + def test = callee + test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + IncrCounter inline_iseq_optimized_send_count + v22:StringExact[VALUE(0x1038)] = Const Value(VALUE(0x1038)) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_inline_send_without_block_direct_putnil() { + eval(r#" + def callee = nil + def test = callee + test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + IncrCounter inline_iseq_optimized_send_count + v22:NilClass = Const Value(nil) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_inline_send_without_block_direct_putobject_true() { + eval(r#" + def callee = true + def test = callee + test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + IncrCounter inline_iseq_optimized_send_count + v22:TrueClass = Const Value(true) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_inline_send_without_block_direct_putobject_false() { + eval(r#" + def callee = false + def test = callee + test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + IncrCounter inline_iseq_optimized_send_count + v22:FalseClass = Const Value(false) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_inline_send_without_block_direct_putobject_zero() { + eval(r#" + def callee = 0 + def test = callee + test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + IncrCounter inline_iseq_optimized_send_count + v22:Fixnum[0] = Const Value(0) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_inline_send_without_block_direct_putobject_one() { + eval(r#" + def callee = 1 + def test = callee + test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + IncrCounter inline_iseq_optimized_send_count + v22:Fixnum[1] = Const Value(1) + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_inline_send_without_block_direct_parameter() { + eval(r#" + def callee(x) = x + def test = callee 3 + test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[3] = Const Value(3) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + IncrCounter inline_iseq_optimized_send_count + CheckInterrupts + Return v10 + "); + } + + #[test] + fn test_inline_send_without_block_direct_last_parameter() { + eval(r#" + def callee(x, y, z) = z + def test = callee 1, 2, 3 + test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + v11:Fixnum[2] = Const Value(2) + v12:Fixnum[3] = Const Value(3) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v22:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + IncrCounter inline_iseq_optimized_send_count + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_inline_symbol_to_sym() { + eval(r#" + def test(o) = o.to_sym + test :foo + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Symbol@0x1000, to_sym@0x1008, cme:0x1010) + v21:StaticSymbol = GuardType v9, StaticSymbol + IncrCounter inline_iseq_optimized_send_count + CheckInterrupts + Return v21 + "); + } + + #[test] + fn test_inline_integer_to_i() { + eval(r#" + def test(o) = o.to_i + test 5 + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Integer@0x1000, to_i@0x1008, cme:0x1010) + v21:Fixnum = GuardType v9, Fixnum + IncrCounter inline_iseq_optimized_send_count + CheckInterrupts + Return v21 + "); + } + + #[test] + fn test_optimize_stringexact_eq_stringexact() { + eval(r#" + def test(l, r) = l == r + test("a", "b") + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(String@0x1000, ==@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v28:StringExact = GuardType v11, StringExact + v29:String = GuardType v12, String + v30:BoolExact = CCall String#==@0x1038, v28, v29 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_optimize_string_eq_string() { + eval(r#" + class C < String + end + def test(l, r) = l == r + test(C.new("a"), C.new("b")) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(C@0x1000, ==@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(C@0x1000) + v28:StringSubclass[class_exact:C] = GuardType v11, StringSubclass[class_exact:C] + v29:String = GuardType v12, String + v30:BoolExact = CCall String#==@0x1038, v28, v29 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_optimize_stringexact_eq_string() { + eval(r#" + class C < String + end + def test(l, r) = l == r + test("a", C.new("b")) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(String@0x1000, ==@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v28:StringExact = GuardType v11, StringExact + v29:String = GuardType v12, String + v30:BoolExact = CCall String#==@0x1038, v28, v29 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_optimize_stringexact_eqq_stringexact() { + eval(r#" + def test(l, r) = l === r + test("a", "b") + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(String@0x1000, ===@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v26:StringExact = GuardType v11, StringExact + v27:String = GuardType v12, String + v28:BoolExact = CCall String#==@0x1038, v26, v27 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_optimize_string_eqq_string() { + eval(r#" + class C < String + end + def test(l, r) = l === r + test(C.new("a"), C.new("b")) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(C@0x1000, ===@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(C@0x1000) + v26:StringSubclass[class_exact:C] = GuardType v11, StringSubclass[class_exact:C] + v27:String = GuardType v12, String + v28:BoolExact = CCall String#==@0x1038, v26, v27 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_optimize_stringexact_eqq_string() { + eval(r#" + class C < String + end + def test(l, r) = l === r + test("a", C.new("b")) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(String@0x1000, ===@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v26:StringExact = GuardType v11, StringExact + v27:String = GuardType v12, String + v28:BoolExact = CCall String#==@0x1038, v26, v27 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v28 + "); + } + + #[test] + fn test_specialize_string_size() { + eval(r#" + def test(s) + s.size + end + test("asdf") + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(String@0x1000, size@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v25:StringExact = GuardType v9, StringExact + IncrCounter inline_cfunc_optimized_send_count + v27:Fixnum = CCall size@0x1038, v25 + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_elide_string_size() { + eval(r#" + def test(s) + s.size + 5 + end + test("asdf") + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(String@0x1000, size@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v28:StringExact = GuardType v9, StringExact + IncrCounter inline_cfunc_optimized_send_count + v19:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_specialize_string_bytesize() { + eval(r#" + def test(s) + s.bytesize + end + test("asdf") + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(String@0x1000, bytesize@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v23:StringExact = GuardType v9, StringExact + IncrCounter inline_cfunc_optimized_send_count + v25:Fixnum = CCall bytesize@0x1038, v23 + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_elide_string_bytesize() { + eval(r#" + def test(s) + s.bytesize + 5 + end + test("asdf") + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(String@0x1000, bytesize@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v26:StringExact = GuardType v9, StringExact + IncrCounter inline_cfunc_optimized_send_count + v17:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_specialize_string_length() { + eval(r#" + def test(s) + s.length + end + test("asdf") + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v25:StringExact = GuardType v9, StringExact + IncrCounter inline_cfunc_optimized_send_count + v27:Fixnum = CCall length@0x1038, v25 + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_elide_string_length() { + eval(r#" + def test(s) + s.length + 4 + end + test("this should get removed") + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v28:StringExact = GuardType v9, StringExact + IncrCounter inline_cfunc_optimized_send_count + v19:Fixnum[4] = Const Value(4) + CheckInterrupts + Return v19 + "); + } +} diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs new file mode 100644 index 00000000000000..df14ba5a7c2026 --- /dev/null +++ b/zjit/src/hir/tests.rs @@ -0,0 +1,3207 @@ +#[cfg(test)] +use super::*; + +#[cfg(test)] +mod snapshot_tests { + use super::*; + use insta::assert_snapshot; + + #[track_caller] + fn hir_string(method: &str) -> String { + let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("self", method)); + unsafe { crate::cruby::rb_zjit_profile_disable(iseq) }; + let function = iseq_to_hir(iseq).unwrap(); + format!("{}", FunctionPrinter::with_snapshot(&function)) + } + + #[test] + fn test_new_array_with_elements() { + eval("def test(a, b) = [a, b]"); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v13:Any = Snapshot FrameState { pc: 0x1000, stack: [], locals: [a=v11, b=v12] } + v14:Any = Snapshot FrameState { pc: 0x1008, stack: [], locals: [a=v11, b=v12] } + PatchPoint NoTracePoint + v16:Any = Snapshot FrameState { pc: 0x1010, stack: [v11, v12], locals: [a=v11, b=v12] } + v17:ArrayExact = NewArray v11, v12 + v18:Any = Snapshot FrameState { pc: 0x1018, stack: [v17], locals: [a=v11, b=v12] } + PatchPoint NoTracePoint + v20:Any = Snapshot FrameState { pc: 0x1018, stack: [v17], locals: [a=v11, b=v12] } + CheckInterrupts + Return v17 + "); + } +} + +#[cfg(test)] +pub mod hir_build_tests { + use super::*; + use insta::assert_snapshot; + + fn iseq_contains_opcode(iseq: IseqPtr, expected_opcode: u32) -> bool { + let iseq_size = unsafe { get_iseq_encoded_size(iseq) }; + let mut insn_idx = 0; + while insn_idx < iseq_size { + // Get the current pc and opcode + let pc = unsafe { rb_iseq_pc_at_idx(iseq, insn_idx) }; + + // try_into() call below is unfortunate. Maybe pick i32 instead of usize for opcodes. + let opcode: u32 = unsafe { rb_iseq_opcode_at_pc(iseq, pc) } + .try_into() + .unwrap(); + if opcode == expected_opcode { + return true; + } + insn_idx += insn_len(opcode as usize); + } + false + } + + #[track_caller] + pub fn assert_contains_opcode(method: &str, opcode: u32) { + let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("self", method)); + unsafe { crate::cruby::rb_zjit_profile_disable(iseq) }; + assert!(iseq_contains_opcode(iseq, opcode), "iseq {method} does not contain {}", insn_name(opcode as usize)); + } + + #[track_caller] + fn assert_contains_opcodes(method: &str, opcodes: &[u32]) { + let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("self", method)); + unsafe { crate::cruby::rb_zjit_profile_disable(iseq) }; + for &opcode in opcodes { + assert!(iseq_contains_opcode(iseq, opcode), "iseq {method} does not contain {}", insn_name(opcode as usize)); + } + } + + /// Combine multiple hir_string() results to match all of them at once, which allows + /// us to avoid running the set of zjit-test -> zjit-test-update multiple times. + #[macro_export] + macro_rules! hir_strings { + ($( $s:expr ),+ $(,)?) => {{ + vec![$( hir_string($s) ),+].join("\n") + }}; + } + + #[track_caller] + fn hir_string(method: &str) -> String { + hir_string_proc(&format!("{}.method(:{})", "self", method)) + } + + #[track_caller] + fn hir_string_proc(proc: &str) -> String { + let iseq = crate::cruby::with_rubyvm(|| get_proc_iseq(proc)); + unsafe { crate::cruby::rb_zjit_profile_disable(iseq) }; + let function = iseq_to_hir(iseq).unwrap(); + hir_string_function(&function) + } + + #[track_caller] + fn hir_string_function(function: &Function) -> String { + format!("{}", FunctionPrinter::without_snapshot(function)) + } + + #[track_caller] + fn assert_compile_fails(method: &str, reason: ParseError) { + let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("self", method)); + unsafe { crate::cruby::rb_zjit_profile_disable(iseq) }; + let result = iseq_to_hir(iseq); + assert!(result.is_err(), "Expected an error but successfully compiled to HIR: {}", FunctionPrinter::without_snapshot(&result.unwrap())); + assert_eq!(result.unwrap_err(), reason); + } + + #[test] + fn test_compile_optional() { + eval("def test(x=1) = 123"); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + v3:CPtr = LoadPC + v4:CPtr[CPtr(0x1000)] = Const CPtr(0x1008) + v5:CBool = IsBitEqual v3, v4 + IfTrue v5, bb2(v1, v2) + Jump bb4(v1, v2) + bb1(v9:BasicObject, v10:BasicObject): + EntryPoint JIT(0) + Jump bb2(v9, v10) + bb2(v12:BasicObject, v13:BasicObject): + v15:Fixnum[1] = Const Value(1) + Jump bb4(v12, v15) + bb3(v18:BasicObject, v19:BasicObject): + EntryPoint JIT(1) + Jump bb4(v18, v19) + bb4(v21:BasicObject, v22:BasicObject): + v26:Fixnum[123] = Const Value(123) + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_putobject() { + eval("def test = 123"); + assert_contains_opcode("test", YARVINSN_putobject); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[123] = Const Value(123) + CheckInterrupts + Return v10 + "); + } + + #[test] + fn test_new_array() { + eval("def test = []"); + assert_contains_opcode("test", YARVINSN_newarray); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:ArrayExact = NewArray + CheckInterrupts + Return v11 + "); + } + + #[test] + fn test_new_array_with_element() { + eval("def test(a) = [a]"); + assert_contains_opcode("test", YARVINSN_newarray); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:ArrayExact = NewArray v9 + CheckInterrupts + Return v14 + "); + } + + #[test] + fn test_new_array_with_elements() { + eval("def test(a, b) = [a, b]"); + assert_contains_opcode("test", YARVINSN_newarray); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v17:ArrayExact = NewArray v11, v12 + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_new_range_inclusive_with_one_element() { + eval("def test(a) = (a..10)"); + assert_contains_opcode("test", YARVINSN_newrange); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[10] = Const Value(10) + v15:RangeExact = NewRange v9 NewRangeInclusive v13 + CheckInterrupts + Return v15 + "); + } + + #[test] + fn test_new_range_inclusive_with_two_elements() { + eval("def test(a, b) = (a..b)"); + assert_contains_opcode("test", YARVINSN_newrange); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v17:RangeExact = NewRange v11 NewRangeInclusive v12 + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_new_range_exclusive_with_one_element() { + eval("def test(a) = (a...10)"); + assert_contains_opcode("test", YARVINSN_newrange); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[10] = Const Value(10) + v15:RangeExact = NewRange v9 NewRangeExclusive v13 + CheckInterrupts + Return v15 + "); + } + + #[test] + fn test_new_range_exclusive_with_two_elements() { + eval("def test(a, b) = (a...b)"); + assert_contains_opcode("test", YARVINSN_newrange); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v17:RangeExact = NewRange v11 NewRangeExclusive v12 + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_array_dup() { + eval("def test = [1, 2, 3]"); + assert_contains_opcode("test", YARVINSN_duparray); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v12:ArrayExact = ArrayDup v10 + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_hash_dup() { + eval("def test = {a: 1, b: 2}"); + assert_contains_opcode("test", YARVINSN_duphash); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v12:HashExact = HashDup v10 + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_new_hash_empty() { + eval("def test = {}"); + assert_contains_opcode("test", YARVINSN_newhash); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:HashExact = NewHash + CheckInterrupts + Return v11 + "); + } + + #[test] + fn test_new_hash_with_elements() { + eval("def test(aval, bval) = {a: aval, b: bval}"); + assert_contains_opcode("test", YARVINSN_newhash); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v16:StaticSymbol[:a] = Const Value(VALUE(0x1000)) + v17:StaticSymbol[:b] = Const Value(VALUE(0x1008)) + v19:HashExact = NewHash v16: v11, v17: v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_string_copy() { + eval("def test = \"hello\""); + assert_contains_opcode("test", YARVINSN_putchilledstring); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v12:StringExact = StringCopy v10 + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_bignum() { + eval("def test = 999999999999999999999999999999999999"); + assert_contains_opcode("test", YARVINSN_putobject); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Bignum[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + CheckInterrupts + Return v10 + "); + } + + #[test] + fn test_flonum() { + eval("def test = 1.5"); + assert_contains_opcode("test", YARVINSN_putobject); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Flonum[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + CheckInterrupts + Return v10 + "); + } + + #[test] + fn test_heap_float() { + eval("def test = 1.7976931348623157e+308"); + assert_contains_opcode("test", YARVINSN_putobject); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:HeapFloat[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + CheckInterrupts + Return v10 + "); + } + + #[test] + fn test_static_sym() { + eval("def test = :foo"); + assert_contains_opcode("test", YARVINSN_putobject); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) + CheckInterrupts + Return v10 + "); + } + + #[test] + fn test_opt_plus() { + eval("def test = 1+2"); + assert_contains_opcode("test", YARVINSN_opt_plus); + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + v11:Fixnum[2] = Const Value(2) + v15:BasicObject = SendWithoutBlock v10, :+, v11 + CheckInterrupts + Return v15 + "); + } + + #[test] + fn test_opt_hash_freeze() { + eval(" + def test = {}.freeze + "); + assert_contains_opcode("test", YARVINSN_opt_hash_freeze); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) + v12:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_opt_hash_freeze_rewritten() { + eval(" + class Hash + def freeze; 5; end + end + def test = {}.freeze + "); + assert_contains_opcode("test", YARVINSN_opt_hash_freeze); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + SideExit PatchPoint(BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE)) + "); + } + + #[test] + fn test_opt_ary_freeze() { + eval(" + def test = [].freeze + "); + assert_contains_opcode("test", YARVINSN_opt_ary_freeze); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + v12:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_opt_ary_freeze_rewritten() { + eval(" + class Array + def freeze; 5; end + end + def test = [].freeze + "); + assert_contains_opcode("test", YARVINSN_opt_ary_freeze); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + SideExit PatchPoint(BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE)) + "); + } + + #[test] + fn test_opt_str_freeze() { + eval(" + def test = ''.freeze + "); + assert_contains_opcode("test", YARVINSN_opt_str_freeze); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) + v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_opt_str_freeze_rewritten() { + eval(" + class String + def freeze; 5; end + end + def test = ''.freeze + "); + assert_contains_opcode("test", YARVINSN_opt_str_freeze); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + SideExit PatchPoint(BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE)) + "); + } + + #[test] + fn test_opt_str_uminus() { + eval(" + def test = -'' + "); + assert_contains_opcode("test", YARVINSN_opt_str_uminus); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS) + v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_opt_str_uminus_rewritten() { + eval(" + class String + def -@; 5; end + end + def test = -'' + "); + assert_contains_opcode("test", YARVINSN_opt_str_uminus); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + SideExit PatchPoint(BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS)) + "); + } + + #[test] + fn test_setlocal_getlocal() { + eval(" + def test + a = 1 + a + end + "); + assert_contains_opcodes("test", &[YARVINSN_getlocal_WC_0, YARVINSN_setlocal_WC_0]); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:Fixnum[1] = Const Value(1) + CheckInterrupts + Return v13 + "); + } + + #[test] + fn test_nested_setlocal_getlocal() { + eval(" + l3 = 3 + _unused = _unused1 = nil + 1.times do |l2| + _ = nil + l2 = 2 + 1.times do |l1| + l1 = 1 + define_method(:test) do + l1 = l2 + l2 = l1 + l2 + l3 = l2 + l3 + end + end + end + "); + assert_contains_opcodes( + "test", + &[YARVINSN_getlocal_WC_1, YARVINSN_setlocal_WC_1, + YARVINSN_getlocal, YARVINSN_setlocal]); + assert_snapshot!(hir_string("test"), @r" + fn block (3 levels) in @:10: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:BasicObject = GetLocal l2, EP@4 + SetLocal l1, EP@3, v10 + v14:BasicObject = GetLocal l1, EP@3 + v15:BasicObject = GetLocal l2, EP@4 + v19:BasicObject = SendWithoutBlock v14, :+, v15 + SetLocal l2, EP@4, v19 + v23:BasicObject = GetLocal l2, EP@4 + v24:BasicObject = GetLocal l3, EP@5 + v28:BasicObject = SendWithoutBlock v23, :+, v24 + SetLocal l3, EP@5, v28 + CheckInterrupts + Return v28 + " + ); + } + + #[test] + fn test_setlocal_in_default_args() { + eval(" + def test(a = (b = 1)) = [a, b] + "); + assert_contains_opcode("test", YARVINSN_setlocal_WC_0); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:NilClass = Const Value(nil) + v4:CPtr = LoadPC + v5:CPtr[CPtr(0x1000)] = Const CPtr(0x1008) + v6:CBool = IsBitEqual v4, v5 + IfTrue v6, bb2(v1, v2, v3) + Jump bb4(v1, v2, v3) + bb1(v10:BasicObject, v11:BasicObject): + EntryPoint JIT(0) + v12:NilClass = Const Value(nil) + Jump bb2(v10, v11, v12) + bb2(v14:BasicObject, v15:BasicObject, v16:NilClass): + v20:Fixnum[1] = Const Value(1) + Jump bb4(v14, v20, v20) + bb3(v23:BasicObject, v24:BasicObject): + EntryPoint JIT(1) + v25:NilClass = Const Value(nil) + Jump bb4(v23, v24, v25) + bb4(v27:BasicObject, v28:BasicObject, v29:NilClass|Fixnum): + v34:ArrayExact = NewArray v28, v29 + CheckInterrupts + Return v34 + "); + } + + #[test] + fn test_setlocal_in_default_args_with_tracepoint() { + eval(" + def test(a = (b = 1)) = [a, b] + TracePoint.new(:line) {}.enable + test + "); + assert_compile_fails("test", ParseError::FailedOptionalArguments); + } + + #[test] + fn test_setlocal_in_default_args_with_side_exit() { + eval(" + def test(a = (def foo = nil)) = a + "); + assert_compile_fails("test", ParseError::FailedOptionalArguments); + } + + #[test] + fn test_setlocal_cyclic_default_args() { + eval(" + def test = proc { |a=a| a } + "); + assert_snapshot!(hir_string_proc("test"), @r" + fn block in test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + CheckInterrupts + Return v9 + "); + } + + #[test] + fn defined_ivar() { + eval(" + def test = defined?(@foo) + "); + assert_contains_opcode("test", YARVINSN_definedivar); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:StringExact|NilClass = DefinedIvar v6, :@foo + CheckInterrupts + Return v11 + "); + } + + #[test] + fn if_defined_ivar() { + eval(" + def test + if defined?(@foo) + 3 + else + 4 + end + end + "); + assert_contains_opcode("test", YARVINSN_definedivar); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:TrueClass|NilClass = DefinedIvar v6, :@foo + CheckInterrupts + v14:CBool = Test v11 + IfFalse v14, bb3(v6) + v18:Fixnum[3] = Const Value(3) + CheckInterrupts + Return v18 + bb3(v24:BasicObject): + v28:Fixnum[4] = Const Value(4) + CheckInterrupts + Return v28 + "); + } + + #[test] + fn defined() { + eval(" + def test = return defined?(SeaChange), defined?(favourite), defined?($ruby) + "); + assert_contains_opcode("test", YARVINSN_defined); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:NilClass = Const Value(nil) + v12:StringExact|NilClass = Defined constant, v10 + v14:StringExact|NilClass = Defined func, v6 + v15:NilClass = Const Value(nil) + v17:StringExact|NilClass = Defined global-variable, v15 + v19:ArrayExact = NewArray v12, v14, v17 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_return_const() { + eval(" + def test(cond) + if cond + 3 + else + 4 + end + end + "); + assert_contains_opcode("test", YARVINSN_leave); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + CheckInterrupts + v15:CBool = Test v9 + IfFalse v15, bb3(v8, v9) + v19:Fixnum[3] = Const Value(3) + CheckInterrupts + Return v19 + bb3(v25:BasicObject, v26:BasicObject): + v30:Fixnum[4] = Const Value(4) + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_merge_const() { + eval(" + def test(cond) + if cond + result = 3 + else + result = 4 + end + result + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject): + EntryPoint JIT(0) + v8:NilClass = Const Value(nil) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:NilClass): + CheckInterrupts + v18:CBool = Test v11 + IfFalse v18, bb3(v10, v11, v12) + v22:Fixnum[3] = Const Value(3) + PatchPoint NoEPEscape(test) + CheckInterrupts + Jump bb4(v10, v11, v22) + bb3(v28:BasicObject, v29:BasicObject, v30:NilClass): + v34:Fixnum[4] = Const Value(4) + PatchPoint NoEPEscape(test) + Jump bb4(v28, v29, v34) + bb4(v38:BasicObject, v39:BasicObject, v40:Fixnum): + PatchPoint NoEPEscape(test) + CheckInterrupts + Return v40 + "); + } + + #[test] + fn test_opt_plus_fixnum() { + eval(" + def test(a, b) = a + b + test(1, 2); test(1, 2) + "); + assert_contains_opcode("test", YARVINSN_opt_plus); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v19:BasicObject = SendWithoutBlock v11, :+, v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_opt_minus_fixnum() { + eval(" + def test(a, b) = a - b + test(1, 2); test(1, 2) + "); + assert_contains_opcode("test", YARVINSN_opt_minus); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v19:BasicObject = SendWithoutBlock v11, :-, v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_opt_mult_fixnum() { + eval(" + def test(a, b) = a * b + test(1, 2); test(1, 2) + "); + assert_contains_opcode("test", YARVINSN_opt_mult); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v19:BasicObject = SendWithoutBlock v11, :*, v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_opt_div_fixnum() { + eval(" + def test(a, b) = a / b + test(1, 2); test(1, 2) + "); + assert_contains_opcode("test", YARVINSN_opt_div); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v19:BasicObject = SendWithoutBlock v11, :/, v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_opt_mod_fixnum() { + eval(" + def test(a, b) = a % b + test(1, 2); test(1, 2) + "); + assert_contains_opcode("test", YARVINSN_opt_mod); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v19:BasicObject = SendWithoutBlock v11, :%, v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_opt_eq_fixnum() { + eval(" + def test(a, b) = a == b + test(1, 2); test(1, 2) + "); + assert_contains_opcode("test", YARVINSN_opt_eq); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v19:BasicObject = SendWithoutBlock v11, :==, v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_opt_neq_fixnum() { + eval(" + def test(a, b) = a != b + test(1, 2); test(1, 2) + "); + assert_contains_opcode("test", YARVINSN_opt_neq); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v19:BasicObject = SendWithoutBlock v11, :!=, v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_opt_lt_fixnum() { + eval(" + def test(a, b) = a < b + test(1, 2); test(1, 2) + "); + assert_contains_opcode("test", YARVINSN_opt_lt); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v19:BasicObject = SendWithoutBlock v11, :<, v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_opt_le_fixnum() { + eval(" + def test(a, b) = a <= b + test(1, 2); test(1, 2) + "); + assert_contains_opcode("test", YARVINSN_opt_le); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v19:BasicObject = SendWithoutBlock v11, :<=, v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_opt_gt_fixnum() { + eval(" + def test(a, b) = a > b + test(1, 2); test(1, 2) + "); + assert_contains_opcode("test", YARVINSN_opt_gt); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v19:BasicObject = SendWithoutBlock v11, :>, v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_loop() { + eval(" + def test + result = 0 + times = 10 + while times > 0 + result = result + 1 + times = times - 1 + end + result + end + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + v3:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject): + EntryPoint JIT(0) + v7:NilClass = Const Value(nil) + v8:NilClass = Const Value(nil) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:NilClass, v12:NilClass): + v16:Fixnum[0] = Const Value(0) + v19:Fixnum[10] = Const Value(10) + CheckInterrupts + Jump bb4(v10, v16, v19) + bb4(v25:BasicObject, v26:BasicObject, v27:BasicObject): + PatchPoint NoEPEscape(test) + v31:Fixnum[0] = Const Value(0) + v35:BasicObject = SendWithoutBlock v27, :>, v31 + CheckInterrupts + v38:CBool = Test v35 + IfTrue v38, bb3(v25, v26, v27) + v40:NilClass = Const Value(nil) + PatchPoint NoEPEscape(test) + CheckInterrupts + Return v26 + bb3(v50:BasicObject, v51:BasicObject, v52:BasicObject): + PatchPoint NoEPEscape(test) + v58:Fixnum[1] = Const Value(1) + v62:BasicObject = SendWithoutBlock v51, :+, v58 + v65:Fixnum[1] = Const Value(1) + v69:BasicObject = SendWithoutBlock v52, :-, v65 + Jump bb4(v50, v62, v69) + "); + } + + #[test] + fn test_opt_ge_fixnum() { + eval(" + def test(a, b) = a >= b + test(1, 2); test(1, 2) + "); + assert_contains_opcode("test", YARVINSN_opt_ge); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v19:BasicObject = SendWithoutBlock v11, :>=, v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_display_types() { + eval(" + def test + cond = true + if cond + 3 + else + 4 + end + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:TrueClass = Const Value(true) + CheckInterrupts + v18:CBool[true] = Test v13 + IfFalse v18, bb3(v8, v13) + v22:Fixnum[3] = Const Value(3) + CheckInterrupts + Return v22 + bb3(v28, v29): + v33 = Const Value(4) + CheckInterrupts + Return v33 + "); + } + + #[test] + fn test_send_without_block() { + eval(" + def bar(a, b) + a+b + end + def test + bar(2, 3) + end + "); + assert_contains_opcode("test", YARVINSN_opt_send_without_block); + assert_snapshot!(hir_string("test"), @r" + fn test@:6: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[2] = Const Value(2) + v11:Fixnum[3] = Const Value(3) + v13:BasicObject = SendWithoutBlock v6, :bar, v10, v11 + CheckInterrupts + Return v13 + "); + } + + #[test] + fn test_send_with_block() { + eval(" + def test(a) + a.each {|item| + item + } + end + test([1,2,3]) + "); + assert_contains_opcode("test", YARVINSN_send); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:BasicObject = GetLocal l0, EP@3 + v15:BasicObject = Send v13, 0x1000, :each + v16:BasicObject = GetLocal l0, EP@3 + CheckInterrupts + Return v15 + "); + } + + #[test] + fn test_intern_interpolated_symbol() { + eval(r#" + def test + :"foo#{123}" + end + "#); + assert_contains_opcode("test", YARVINSN_intern); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v11:Fixnum[123] = Const Value(123) + v13:BasicObject = ObjToString v11 + v15:String = AnyToString v11, str: v13 + v17:StringExact = StringConcat v10, v15 + v19:Symbol = StringIntern v17 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn different_objects_get_addresses() { + eval("def test = unknown_method([0], [1], '2', '2')"); + + // The 2 string literals have the same address because they're deduped. + assert_snapshot!(hir_string("test"), @r" + fn test@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v12:ArrayExact = ArrayDup v10 + v13:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v15:ArrayExact = ArrayDup v13 + v16:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) + v18:StringExact = StringCopy v16 + v19:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) + v21:StringExact = StringCopy v19 + v23:BasicObject = SendWithoutBlock v6, :unknown_method, v12, v15, v18, v21 + CheckInterrupts + Return v23 + "); + } + + #[test] + fn test_cant_compile_splat() { + eval(" + def test(a) = foo(*a) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:ArrayExact = ToArray v9 + SideExit UnhandledCallType(Splat) + "); + } + + #[test] + fn test_compile_block_arg() { + eval(" + def test(a) = foo(&a) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:BasicObject = Send v8, 0x1000, :foo, v9 + CheckInterrupts + Return v14 + "); + } + + #[test] + fn test_cant_compile_kwarg() { + eval(" + def test(a) = foo(a: 1) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[1] = Const Value(1) + SideExit UnhandledCallType(Kwarg) + "); + } + + #[test] + fn test_cant_compile_kw_splat() { + eval(" + def test(a) = foo(**a) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:BasicObject = SendWithoutBlock v8, :foo, v9 + CheckInterrupts + Return v14 + "); + } + + // TODO(max): Figure out how to generate a call with TAILCALL flag + + #[test] + fn test_compile_super() { + eval(" + def test = super() + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = InvokeSuper v6, 0x1000 + CheckInterrupts + Return v11 + "); + } + + #[test] + fn test_compile_zsuper() { + eval(" + def test = super + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = InvokeSuper v6, 0x1000 + CheckInterrupts + Return v11 + "); + } + + #[test] + fn test_cant_compile_super_nil_blockarg() { + eval(" + def test = super(&nil) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:NilClass = Const Value(nil) + v12:BasicObject = InvokeSuper v6, 0x1000, v10 + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_cant_compile_super_forward() { + eval(" + def test(...) = super(...) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + SideExit UnhandledYARVInsn(invokesuperforward) + "); + } + + #[test] + fn test_compile_forwardable() { + eval("def forwardable(...) = nil"); + assert_snapshot!(hir_string("forwardable"), @r" + fn forwardable@:1: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:NilClass = Const Value(nil) + CheckInterrupts + Return v13 + "); + } + + // TODO(max): Figure out how to generate a call with OPT_SEND flag + + #[test] + fn test_cant_compile_kw_splat_mut() { + eval(" + def test(a) = foo **a, b: 1 + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Class[VMFrozenCore] = Const Value(VALUE(0x1000)) + v15:HashExact = NewHash + PatchPoint NoEPEscape(test) + v19:BasicObject = SendWithoutBlock v13, :core#hash_merge_kwd, v15, v9 + v20:Class[VMFrozenCore] = Const Value(VALUE(0x1000)) + v21:StaticSymbol[:b] = Const Value(VALUE(0x1008)) + v22:Fixnum[1] = Const Value(1) + v24:BasicObject = SendWithoutBlock v20, :core#hash_merge_ptr, v19, v21, v22 + v26:BasicObject = SendWithoutBlock v8, :foo, v24 + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_cant_compile_splat_mut() { + eval(" + def test(*) = foo *, 1 + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:ArrayExact = GetLocal l0, SP@4, * + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:ArrayExact): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:ArrayExact): + v14:ArrayExact = ToNewArray v9 + v15:Fixnum[1] = Const Value(1) + ArrayPush v14, v15 + SideExit UnhandledCallType(Splat) + "); + } + + #[test] + fn test_compile_forwarding() { + eval(" + def test(...) = foo(...) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:BasicObject = SendForward 0x1000, :foo, v9 + CheckInterrupts + Return v14 + "); + } + + #[test] + fn test_compile_triple_dots_with_positional_args() { + eval(" + def test(a, ...) = foo(a, ...) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@8 + v3:ArrayExact = GetLocal l0, SP@7, * + v4:BasicObject = GetLocal l0, SP@6 + v5:BasicObject = GetLocal l0, SP@5 + v6:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5, v6) + bb1(v9:BasicObject, v10:BasicObject, v11:ArrayExact, v12:BasicObject, v13:BasicObject): + EntryPoint JIT(0) + v14:NilClass = Const Value(nil) + Jump bb2(v9, v10, v11, v12, v13, v14) + bb2(v16:BasicObject, v17:BasicObject, v18:ArrayExact, v19:BasicObject, v20:BasicObject, v21:NilClass): + v26:ArrayExact = ToArray v18 + PatchPoint NoEPEscape(test) + GuardBlockParamProxy l0 + v31:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1000)) + SideExit UnhandledYARVInsn(splatkw) + "); + } + + #[test] + fn test_opt_new() { + eval(" + class C; end + def test = C.new + "); + assert_contains_opcode("test", YARVINSN_opt_new); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = GetConstantPath 0x1000 + v12:NilClass = Const Value(nil) + v14:CBool = IsMethodCFunc v11, :new + IfFalse v14, bb3(v6, v12, v11) + v16:HeapBasicObject = ObjectAlloc v11 + v18:BasicObject = SendWithoutBlock v16, :initialize + CheckInterrupts + Jump bb4(v6, v16, v18) + bb3(v22:BasicObject, v23:NilClass, v24:BasicObject): + v27:BasicObject = SendWithoutBlock v24, :new + Jump bb4(v22, v27, v23) + bb4(v29:BasicObject, v30:BasicObject, v31:BasicObject): + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_opt_newarray_send_max_no_elements() { + eval(" + def test = [].max + "); + // TODO(max): Rewrite to nil + assert_contains_opcode("test", YARVINSN_opt_newarray_send); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_MAX) + v12:BasicObject = ArrayMax + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_opt_newarray_send_max() { + eval(" + def test(a,b) = [a,b].max + "); + assert_contains_opcode("test", YARVINSN_opt_newarray_send); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_MAX) + v18:BasicObject = ArrayMax v11, v12 + CheckInterrupts + Return v18 + "); + } + + #[test] + fn test_opt_newarray_send_min() { + eval(" + def test(a,b) + sum = a+b + result = [a,b].min + puts [1,2,3] + result + end + "); + assert_contains_opcode("test", YARVINSN_opt_newarray_send); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@7 + v3:BasicObject = GetLocal l0, SP@6 + v4:NilClass = Const Value(nil) + v5:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5) + bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject): + EntryPoint JIT(0) + v11:NilClass = Const Value(nil) + v12:NilClass = Const Value(nil) + Jump bb2(v8, v9, v10, v11, v12) + bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): + v25:BasicObject = SendWithoutBlock v15, :+, v16 + SideExit UnknownNewarraySend(MIN) + "); + } + + #[test] + fn test_opt_newarray_send_hash() { + eval(" + def test(a,b) + sum = a+b + result = [a,b].hash + puts [1,2,3] + result + end + "); + assert_contains_opcode("test", YARVINSN_opt_newarray_send); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@7 + v3:BasicObject = GetLocal l0, SP@6 + v4:NilClass = Const Value(nil) + v5:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5) + bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject): + EntryPoint JIT(0) + v11:NilClass = Const Value(nil) + v12:NilClass = Const Value(nil) + Jump bb2(v8, v9, v10, v11, v12) + bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): + v25:BasicObject = SendWithoutBlock v15, :+, v16 + SideExit UnknownNewarraySend(HASH) + "); + } + + #[test] + fn test_opt_newarray_send_pack() { + eval(" + def test(a,b) + sum = a+b + result = [a,b].pack 'C' + puts [1,2,3] + result + end + "); + assert_contains_opcode("test", YARVINSN_opt_newarray_send); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@7 + v3:BasicObject = GetLocal l0, SP@6 + v4:NilClass = Const Value(nil) + v5:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5) + bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject): + EntryPoint JIT(0) + v11:NilClass = Const Value(nil) + v12:NilClass = Const Value(nil) + Jump bb2(v8, v9, v10, v11, v12) + bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): + v25:BasicObject = SendWithoutBlock v15, :+, v16 + v28:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v30:StringExact = StringCopy v28 + SideExit UnknownNewarraySend(PACK) + "); + } + + // TODO(max): Add a test for VM_OPT_NEWARRAY_SEND_PACK_BUFFER + + #[test] + fn test_opt_newarray_send_include_p() { + eval(" + def test(a,b) + sum = a+b + result = [a,b].include? b + puts [1,2,3] + result + end + "); + assert_contains_opcode("test", YARVINSN_opt_newarray_send); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@7 + v3:BasicObject = GetLocal l0, SP@6 + v4:NilClass = Const Value(nil) + v5:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5) + bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject): + EntryPoint JIT(0) + v11:NilClass = Const Value(nil) + v12:NilClass = Const Value(nil) + Jump bb2(v8, v9, v10, v11, v12) + bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): + v25:BasicObject = SendWithoutBlock v15, :+, v16 + SideExit UnknownNewarraySend(INCLUDE_P) + "); + } + + #[test] + fn test_opt_length() { + eval(" + def test(a,b) = [a,b].length + "); + assert_contains_opcode("test", YARVINSN_opt_length); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v17:ArrayExact = NewArray v11, v12 + v21:BasicObject = SendWithoutBlock v17, :length + CheckInterrupts + Return v21 + "); + } + + #[test] + fn test_opt_size() { + eval(" + def test(a,b) = [a,b].size + "); + assert_contains_opcode("test", YARVINSN_opt_size); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v17:ArrayExact = NewArray v11, v12 + v21:BasicObject = SendWithoutBlock v17, :size + CheckInterrupts + Return v21 + "); + } + + #[test] + fn test_getinstancevariable() { + eval(" + def test = @foo + test + "); + assert_contains_opcode("test", YARVINSN_getinstancevariable); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + v12:BasicObject = GetIvar v6, :@foo + CheckInterrupts + Return v12 + "); + } + + #[test] + fn test_setinstancevariable() { + eval(" + def test = @foo = 1 + test + "); + assert_contains_opcode("test", YARVINSN_setinstancevariable); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + PatchPoint SingleRactorMode + SetIvar v6, :@foo, v10 + CheckInterrupts + Return v10 + "); + } + + #[test] + fn test_set_ivar_rescue_frozen() { + let result = eval(" + class Foo + attr_accessor :bar + def initialize + @bar = 1 + freeze + end + end + + def test(foo) + begin + foo.bar = 2 + rescue FrozenError + end + end + + foo = Foo.new + test(foo) + test(foo) + + foo.bar + "); + assert_eq!(VALUE::fixnum_from_usize(1), result); + } + + #[test] + fn test_getclassvariable() { + eval(" + class Foo + def self.test = @@foo + end + "); + let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("Foo", "test")); + assert!(iseq_contains_opcode(iseq, YARVINSN_getclassvariable), "iseq Foo.test does not contain getclassvariable"); + let function = iseq_to_hir(iseq).unwrap(); + assert_snapshot!(hir_string_function(&function), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = GetClassVar :@@foo + CheckInterrupts + Return v11 + "); + } + + #[test] + fn test_setclassvariable() { + eval(" + class Foo + def self.test = @@foo = 42 + end + "); + let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("Foo", "test")); + assert!(iseq_contains_opcode(iseq, YARVINSN_setclassvariable), "iseq Foo.test does not contain setclassvariable"); + let function = iseq_to_hir(iseq).unwrap(); + assert_snapshot!(hir_string_function(&function), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[42] = Const Value(42) + SetClassVar :@@foo, v10 + CheckInterrupts + Return v10 + "); + } + + #[test] + fn test_setglobal() { + eval(" + def test = $foo = 1 + test + "); + assert_contains_opcode("test", YARVINSN_setglobal); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + SetGlobal :$foo, v10 + CheckInterrupts + Return v10 + "); + } + + #[test] + fn test_getglobal() { + eval(" + def test = $foo + test + "); + assert_contains_opcode("test", YARVINSN_getglobal); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = GetGlobal :$foo + CheckInterrupts + Return v11 + "); + } + + #[test] + fn test_splatarray_mut() { + eval(" + def test(a) = [*a] + "); + assert_contains_opcode("test", YARVINSN_splatarray); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:ArrayExact = ToNewArray v9 + CheckInterrupts + Return v14 + "); + } + + #[test] + fn test_concattoarray() { + eval(" + def test(a) = [1, *a] + "); + assert_contains_opcode("test", YARVINSN_concattoarray); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[1] = Const Value(1) + v15:ArrayExact = NewArray v13 + v17:ArrayExact = ToArray v9 + ArrayExtend v15, v17 + CheckInterrupts + Return v15 + "); + } + + #[test] + fn test_pushtoarray_one_element() { + eval(" + def test(a) = [*a, 1] + "); + assert_contains_opcode("test", YARVINSN_pushtoarray); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:ArrayExact = ToNewArray v9 + v15:Fixnum[1] = Const Value(1) + ArrayPush v14, v15 + CheckInterrupts + Return v14 + "); + } + + #[test] + fn test_pushtoarray_multiple_elements() { + eval(" + def test(a) = [*a, 1, 2, 3] + "); + assert_contains_opcode("test", YARVINSN_pushtoarray); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:ArrayExact = ToNewArray v9 + v15:Fixnum[1] = Const Value(1) + v16:Fixnum[2] = Const Value(2) + v17:Fixnum[3] = Const Value(3) + ArrayPush v14, v15 + ArrayPush v14, v16 + ArrayPush v14, v17 + CheckInterrupts + Return v14 + "); + } + + #[test] + fn test_aset() { + eval(" + def test(a, b) = a[b] = 1 + "); + assert_contains_opcode("test", YARVINSN_opt_aset); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v16:NilClass = Const Value(nil) + v17:Fixnum[1] = Const Value(1) + v21:BasicObject = SendWithoutBlock v11, :[]=, v12, v17 + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_aref() { + eval(" + def test(a, b) = a[b] + "); + assert_contains_opcode("test", YARVINSN_opt_aref); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v19:BasicObject = SendWithoutBlock v11, :[], v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn opt_empty_p() { + eval(" + def test(x) = x.empty? + "); + assert_contains_opcode("test", YARVINSN_opt_empty_p); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal 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:BasicObject = SendWithoutBlock v9, :empty? + CheckInterrupts + Return v16 + "); + } + + #[test] + fn opt_succ() { + eval(" + def test(x) = x.succ + "); + assert_contains_opcode("test", YARVINSN_opt_succ); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal 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:BasicObject = SendWithoutBlock v9, :succ + CheckInterrupts + Return v16 + "); + } + + #[test] + fn opt_and() { + eval(" + def test(x, y) = x & y + "); + assert_contains_opcode("test", YARVINSN_opt_and); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v19:BasicObject = SendWithoutBlock v11, :&, v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn opt_or() { + eval(" + def test(x, y) = x | y + "); + assert_contains_opcode("test", YARVINSN_opt_or); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v19:BasicObject = SendWithoutBlock v11, :|, v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn opt_not() { + eval(" + def test(x) = !x + "); + assert_contains_opcode("test", YARVINSN_opt_not); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal 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:BasicObject = SendWithoutBlock v9, :! + CheckInterrupts + Return v16 + "); + } + + #[test] + fn opt_regexpmatch2() { + eval(" + def test(regexp, matchee) = regexp =~ matchee + "); + assert_contains_opcode("test", YARVINSN_opt_regexpmatch2); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v19:BasicObject = SendWithoutBlock v11, :=~, v12 + CheckInterrupts + Return v19 + "); + } + + #[test] + // Tests for ConstBase requires either constant or class definition, both + // of which can't be performed inside a method. + fn test_putspecialobject_vm_core_and_cbase() { + eval(" + def test + alias aliased __callee__ + end + "); + assert_contains_opcode("test", YARVINSN_putspecialobject); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Class[VMFrozenCore] = Const Value(VALUE(0x1000)) + v11:BasicObject = PutSpecialObject CBase + v12:StaticSymbol[:aliased] = Const Value(VALUE(0x1008)) + v13:StaticSymbol[:__callee__] = Const Value(VALUE(0x1010)) + v15:BasicObject = SendWithoutBlock v10, :core#set_method_alias, v11, v12, v13 + CheckInterrupts + Return v15 + "); + } + + #[test] + fn opt_reverse() { + eval(" + def reverse_odd + a, b, c = @a, @b, @c + [a, b, c] + end + + def reverse_even + a, b, c, d = @a, @b, @c, @d + [a, b, c, d] + end + "); + assert_contains_opcode("reverse_odd", YARVINSN_opt_reverse); + assert_contains_opcode("reverse_even", YARVINSN_opt_reverse); + assert_snapshot!(hir_strings!("reverse_odd", "reverse_even"), @r" + fn reverse_odd@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + v3:NilClass = Const Value(nil) + v4:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4) + bb1(v7:BasicObject): + EntryPoint JIT(0) + v8:NilClass = Const Value(nil) + v9:NilClass = Const Value(nil) + v10:NilClass = Const Value(nil) + Jump bb2(v7, v8, v9, v10) + bb2(v12:BasicObject, v13:NilClass, v14:NilClass, v15:NilClass): + PatchPoint SingleRactorMode + v21:BasicObject = GetIvar v12, :@a + PatchPoint SingleRactorMode + v24:BasicObject = GetIvar v12, :@b + PatchPoint SingleRactorMode + v27:BasicObject = GetIvar v12, :@c + PatchPoint NoEPEscape(reverse_odd) + v33:ArrayExact = NewArray v21, v24, v27 + CheckInterrupts + Return v33 + + fn reverse_even@:8: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + v3:NilClass = Const Value(nil) + v4:NilClass = Const Value(nil) + v5:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5) + bb1(v8:BasicObject): + EntryPoint JIT(0) + v9:NilClass = Const Value(nil) + v10:NilClass = Const Value(nil) + v11:NilClass = Const Value(nil) + v12:NilClass = Const Value(nil) + Jump bb2(v8, v9, v10, v11, v12) + bb2(v14:BasicObject, v15:NilClass, v16:NilClass, v17:NilClass, v18:NilClass): + PatchPoint SingleRactorMode + v24:BasicObject = GetIvar v14, :@a + PatchPoint SingleRactorMode + v27:BasicObject = GetIvar v14, :@b + PatchPoint SingleRactorMode + v30:BasicObject = GetIvar v14, :@c + PatchPoint SingleRactorMode + v33:BasicObject = GetIvar v14, :@d + PatchPoint NoEPEscape(reverse_even) + v39:ArrayExact = NewArray v24, v27, v30, v33 + CheckInterrupts + Return v39 + "); + } + + #[test] + fn test_branchnil() { + eval(" + def test(x) = x&.itself + "); + assert_contains_opcode("test", YARVINSN_branchnil); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + CheckInterrupts + v15:CBool = IsNil v9 + IfTrue v15, bb3(v8, v9, v9) + v18:BasicObject = SendWithoutBlock v9, :itself + Jump bb3(v8, v9, v18) + bb3(v20:BasicObject, v21:BasicObject, v22:BasicObject): + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_invokebuiltin_delegate_annotated() { + assert_contains_opcode("Float", YARVINSN_opt_invokebuiltin_delegate_leave); + assert_snapshot!(hir_string("Float"), @r" + fn Float@: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@6 + v3:BasicObject = GetLocal l0, SP@5 + v4:BasicObject = GetLocal 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): + v20:Float = InvokeBuiltin rb_f_float, v12, v13, v14 + Jump bb3(v12, v13, v14, v15, v20) + bb3(v22:BasicObject, v23:BasicObject, v24:BasicObject, v25:BasicObject, v26:Float): + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_invokebuiltin_cexpr_annotated() { + assert_contains_opcode("class", YARVINSN_opt_invokebuiltin_delegate_leave); + assert_snapshot!(hir_string("class"), @r" + fn class@: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:Class = InvokeBuiltin leaf _bi20, v6 + Jump bb3(v6, v11) + bb3(v13:BasicObject, v14:Class): + CheckInterrupts + Return v14 + "); + } + + #[test] + fn test_invokebuiltin_delegate_with_args() { + // Using an unannotated builtin to test InvokeBuiltin generation + let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("Dir", "open")); + assert!(iseq_contains_opcode(iseq, YARVINSN_opt_invokebuiltin_delegate), "iseq Dir.open does not contain invokebuiltin"); + let function = iseq_to_hir(iseq).unwrap(); + assert_snapshot!(hir_string_function(&function), @r" + fn open@: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@8 + v3:BasicObject = GetLocal l0, SP@7 + v4:BasicObject = GetLocal l0, SP@6 + v5:BasicObject = GetLocal l0, SP@5 + v6:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5, v6) + bb1(v9:BasicObject, v10:BasicObject, v11:BasicObject, v12:BasicObject, v13:BasicObject): + EntryPoint JIT(0) + v14:NilClass = Const Value(nil) + Jump bb2(v9, v10, v11, v12, v13, v14) + bb2(v16:BasicObject, v17:BasicObject, v18:BasicObject, v19:BasicObject, v20:BasicObject, v21:NilClass): + v26:BasicObject = InvokeBuiltin dir_s_open, v16, v17, v18 + PatchPoint NoEPEscape(open) + GuardBlockParamProxy l0 + v33:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1000)) + CheckInterrupts + v36:CBool[true] = Test v33 + IfFalse v36, bb3(v16, v17, v18, v19, v20, v26) + PatchPoint NoEPEscape(open) + v43:BasicObject = InvokeBlock, v26 + v47:BasicObject = InvokeBuiltin dir_s_close, v16, v26 + CheckInterrupts + Return v43 + bb3(v53, v54, v55, v56, v57, v58): + PatchPoint NoEPEscape(open) + CheckInterrupts + Return v58 + "); + } + + #[test] + fn test_invokebuiltin_delegate_without_args() { + let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("GC", "enable")); + assert!(iseq_contains_opcode(iseq, YARVINSN_opt_invokebuiltin_delegate_leave), "iseq GC.enable does not contain invokebuiltin"); + let function = iseq_to_hir(iseq).unwrap(); + assert_snapshot!(hir_string_function(&function), @r" + fn enable@: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = InvokeBuiltin gc_enable, v6 + Jump bb3(v6, v11) + bb3(v13:BasicObject, v14:BasicObject): + CheckInterrupts + Return v14 + "); + } + + #[test] + fn test_invokebuiltin_with_args() { + let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("GC", "start")); + assert!(iseq_contains_opcode(iseq, YARVINSN_invokebuiltin), "iseq GC.start does not contain invokebuiltin"); + let function = iseq_to_hir(iseq).unwrap(); + assert_snapshot!(hir_string_function(&function), @r" + fn start@: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@7 + v3:BasicObject = GetLocal l0, SP@6 + v4:BasicObject = GetLocal l0, SP@5 + v5:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3, v4, v5) + bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject, v11:BasicObject, v12:BasicObject): + EntryPoint JIT(0) + Jump bb2(v8, v9, v10, v11, v12) + bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:BasicObject, v18:BasicObject): + v22:FalseClass = Const Value(false) + v24:BasicObject = InvokeBuiltin gc_start_internal, v14, v15, v16, v17, v22 + CheckInterrupts + Return v24 + "); + } + + #[test] + fn test_invoke_leaf_builtin_symbol_name() { + let iseq = crate::cruby::with_rubyvm(|| get_instance_method_iseq("Symbol", "name")); + let function = iseq_to_hir(iseq).unwrap(); + assert_snapshot!(hir_string_function(&function), @r" + fn name@: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:StringExact = InvokeBuiltin leaf _bi28, v6 + Jump bb3(v6, v11) + bb3(v13:BasicObject, v14:StringExact): + CheckInterrupts + Return v14 + "); + } + + #[test] + fn test_invoke_leaf_builtin_symbol_to_s() { + let iseq = crate::cruby::with_rubyvm(|| get_instance_method_iseq("Symbol", "to_s")); + let function = iseq_to_hir(iseq).unwrap(); + assert_snapshot!(hir_string_function(&function), @r" + fn to_s@: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:StringExact = InvokeBuiltin leaf _bi12, v6 + Jump bb3(v6, v11) + bb3(v13:BasicObject, v14:StringExact): + CheckInterrupts + Return v14 + "); + } + + #[test] + fn dupn() { + eval(" + def test(x) = (x[0, 1] ||= 2) + "); + assert_contains_opcode("test", YARVINSN_dupn); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:NilClass = Const Value(nil) + v14:Fixnum[0] = Const Value(0) + v15:Fixnum[1] = Const Value(1) + v17:BasicObject = SendWithoutBlock v9, :[], v14, v15 + CheckInterrupts + v20:CBool = Test v17 + IfTrue v20, bb3(v8, v9, v13, v9, v14, v15, v17) + v22:Fixnum[2] = Const Value(2) + v24:BasicObject = SendWithoutBlock v9, :[]=, v14, v15, v22 + CheckInterrupts + Return v22 + bb3(v30:BasicObject, v31:BasicObject, v32:NilClass, v33:BasicObject, v34:Fixnum[0], v35:Fixnum[1], v36:BasicObject): + CheckInterrupts + Return v36 + "); + } + + #[test] + fn test_objtostring_anytostring() { + eval(" + def test = \"#{1}\" + "); + assert_contains_opcode("test", YARVINSN_objtostring); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v11:Fixnum[1] = Const Value(1) + v13:BasicObject = ObjToString v11 + v15:String = AnyToString v11, str: v13 + v17:StringExact = StringConcat v10, v15 + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_string_concat() { + eval(r##" + def test = "#{1}#{2}#{3}" + "##); + assert_contains_opcode("test", YARVINSN_concatstrings); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + v12:BasicObject = ObjToString v10 + v14:String = AnyToString v10, str: v12 + v15:Fixnum[2] = Const Value(2) + v17:BasicObject = ObjToString v15 + v19:String = AnyToString v15, str: v17 + v20:Fixnum[3] = Const Value(3) + v22:BasicObject = ObjToString v20 + v24:String = AnyToString v20, str: v22 + v26:StringExact = StringConcat v14, v19, v24 + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_string_concat_empty() { + eval(r##" + def test = "#{}" + "##); + assert_contains_opcode("test", YARVINSN_concatstrings); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v11:NilClass = Const Value(nil) + v13:BasicObject = ObjToString v11 + v15:String = AnyToString v11, str: v13 + v17:StringExact = StringConcat v10, v15 + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_toregexp() { + eval(r##" + def test = /#{1}#{2}#{3}/ + "##); + assert_contains_opcode("test", YARVINSN_toregexp); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + v12:BasicObject = ObjToString v10 + v14:String = AnyToString v10, str: v12 + v15:Fixnum[2] = Const Value(2) + v17:BasicObject = ObjToString v15 + v19:String = AnyToString v15, str: v17 + v20:Fixnum[3] = Const Value(3) + v22:BasicObject = ObjToString v20 + v24:String = AnyToString v20, str: v22 + v26:RegexpExact = ToRegexp v14, v19, v24 + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_toregexp_with_options() { + eval(r##" + def test = /#{1}#{2}/mixn + "##); + assert_contains_opcode("test", YARVINSN_toregexp); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + v12:BasicObject = ObjToString v10 + v14:String = AnyToString v10, str: v12 + v15:Fixnum[2] = Const Value(2) + v17:BasicObject = ObjToString v15 + v19:String = AnyToString v15, str: v17 + v21:RegexpExact = ToRegexp v14, v19, MULTILINE|IGNORECASE|EXTENDED|NOENCODING + CheckInterrupts + Return v21 + "); + } + + #[test] + fn throw() { + eval(" + define_method(:throw_return) { return 1 } + define_method(:throw_break) { break 2 } + "); + assert_contains_opcode("throw_return", YARVINSN_throw); + assert_contains_opcode("throw_break", YARVINSN_throw); + assert_snapshot!(hir_strings!("throw_return", "throw_break"), @r" + fn block in @:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v12:Fixnum[1] = Const Value(1) + Throw TAG_RETURN, v12 + + fn block in @:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v12:Fixnum[2] = Const Value(2) + Throw TAG_BREAK, v12 + "); + } + + #[test] + fn test_invokeblock() { + eval(r#" + def test + yield + end + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = InvokeBlock + CheckInterrupts + Return v11 + "); + } + + #[test] + fn test_invokeblock_with_args() { + eval(r#" + def test(x, y) + yield x, y + end + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + v17:BasicObject = InvokeBlock, v11, v12 + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_expandarray_no_splat() { + eval(r#" + def test(o) + a, b = o + end + "#); + assert_contains_opcode("test", YARVINSN_expandarray); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@6 + v3:NilClass = Const Value(nil) + v4:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4) + bb1(v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + v9:NilClass = Const Value(nil) + v10:NilClass = Const Value(nil) + Jump bb2(v7, v8, v9, v10) + bb2(v12:BasicObject, v13:BasicObject, v14:NilClass, v15:NilClass): + v20:ArrayExact = GuardType v13, ArrayExact + v21:CInt64 = ArrayLength v20 + v22:CInt64[2] = GuardBitEquals v21, CInt64(2) + v23:Fixnum[1] = Const Value(1) + v24:BasicObject = ArrayArefFixnum v20, v23 + v25:Fixnum[0] = Const Value(0) + v26:BasicObject = ArrayArefFixnum v20, v25 + PatchPoint NoEPEscape(test) + CheckInterrupts + Return v13 + "); + } + + #[test] + fn test_expandarray_splat() { + eval(r#" + def test(o) + a, *b = o + end + "#); + assert_contains_opcode("test", YARVINSN_expandarray); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@6 + v3:NilClass = Const Value(nil) + v4:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4) + bb1(v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + v9:NilClass = Const Value(nil) + v10:NilClass = Const Value(nil) + Jump bb2(v7, v8, v9, v10) + bb2(v12:BasicObject, v13:BasicObject, v14:NilClass, v15:NilClass): + SideExit UnhandledYARVInsn(expandarray) + "); + } + + #[test] + fn test_expandarray_splat_post() { + eval(r#" + def test(o) + a, *b, c = o + end + "#); + assert_contains_opcode("test", YARVINSN_expandarray); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@7 + v3:NilClass = Const Value(nil) + v4:NilClass = Const Value(nil) + v5:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5) + bb1(v8:BasicObject, v9:BasicObject): + EntryPoint JIT(0) + v10:NilClass = Const Value(nil) + v11:NilClass = Const Value(nil) + v12:NilClass = Const Value(nil) + Jump bb2(v8, v9, v10, v11, v12) + bb2(v14:BasicObject, v15:BasicObject, v16:NilClass, v17:NilClass, v18:NilClass): + SideExit UnhandledYARVInsn(expandarray) + "); + } +} diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index 4dd87d269ad4e7..4874d0fe64146b 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -143,6 +143,7 @@ make_counters! { exit_guard_bit_equals_failure, exit_guard_int_equals_failure, exit_guard_shape_failure, + exit_guard_not_frozen_failure, exit_patchpoint_bop_redefined, exit_patchpoint_method_redefined, exit_patchpoint_stable_constant_names, @@ -248,6 +249,9 @@ make_counters! { vm_read_from_parent_iseq_local_count, // TODO(max): Implement // vm_reify_stack_count, + + // The number of times we ran a dynamic check + guard_type_count, } /// Increase a counter by a specified amount @@ -342,6 +346,7 @@ pub fn side_exit_counter(reason: crate::hir::SideExitReason) -> Counter { GuardTypeNot(_) => exit_guard_type_not_failure, GuardBitEquals(_) => exit_guard_bit_equals_failure, GuardShape(_) => exit_guard_shape_failure, + GuardNotFrozen => exit_guard_not_frozen_failure, CalleeSideExit => exit_callee_side_exit, ObjToStringFallback => exit_obj_to_string_fallback, Interrupt => exit_interrupt,