diff options
| -rw-r--r-- | zjit/src/hir.rs | 10655 | ||||
| -rw-r--r-- | zjit/src/hir/opt_tests.rs | 7050 | ||||
| -rw-r--r-- | zjit/src/hir/tests.rs | 3207 |
3 files changed, 10260 insertions, 10652 deletions
diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 0f532cb459..3f68764722 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`]. @@ -5384,3234 +5387,6 @@ mod infer_tests { } #[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@<compiled>: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)); - } - - #[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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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 <compiled>@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<internal:kernel>: - 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@<internal:kernel>: - 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@<internal:dir>: - 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@<internal:gc>: - 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@<internal:gc>: - 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@<internal:symbol>: - 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@<internal:symbol>: - 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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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 <compiled>@<compiled>: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 <compiled>@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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; @@ -8725,7427 +5500,3 @@ mod graphviz_tests { "#); } } - -#[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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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_specialize_basic_object_neq() { - eval(" - class C; end - def test(a, b) = a != b - test(C.new, 5) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@<compiled>: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] - PatchPoint MethodRedefined(C@0x1000, ==@0x1038, cme:0x1040) - PatchPoint NoSingletonClass(C@0x1000) - v32:CBool = IsBitNotEqual v28, v12 - v33:BoolExact = BoxBool v32 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v33 - "); - } - - #[test] - fn test_do_not_eliminate_get_constant_path() { - eval(" - def test() - C - 5 - end - "); - assert_snapshot!(hir_string("test"), @r" - fn test@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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 <compiled>@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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() { - eval(" - class C; end - def test(a, b) = a == b - - test(C.new, C.new) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@<compiled>: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_specialize_basic_object_eqq() { - eval(" - class C; end - def test(a, b) = a === b - - test(C.new, C.new) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@<compiled>: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) - v26:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1000, ==@0x1038, cme:0x1040) - PatchPoint NoSingletonClass(C@0x1000) - v30:CBool = IsBitEqual v26, v12 - v31:BoolExact = BoxBool v30 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v31 - "); - } - - #[test] - fn test_specialize_nil_eq() { - eval(" - def test(a, b) = a == b - - test(nil, 5) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@<compiled>: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(NilClass@0x1000, ==@0x1008, cme:0x1010) - v27:NilClass = GuardType v11, NilClass - v28:CBool = IsBitEqual v27, v12 - v29:BoolExact = BoxBool v28 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v29 - "); - } - - #[test] - fn test_specialize_nil_eqq() { - eval(" - def test(a, b) = a === b - test(nil, 5) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@<compiled>: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(NilClass@0x1000, ===@0x1008, cme:0x1010) - v25:NilClass = GuardType v11, NilClass - PatchPoint MethodRedefined(NilClass@0x1000, ==@0x1038, cme:0x1040) - v28:CBool = IsBitEqual v25, v12 - v29:BoolExact = BoxBool v28 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v29 - "); - } - - #[test] - fn test_specialize_true_eqq() { - eval(" - def test(a, b) = a === b - test(true, 5) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@<compiled>: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 - PatchPoint MethodRedefined(TrueClass@0x1000, ==@0x1038, cme:0x1040) - v28:CBool = IsBitEqual v25, v12 - v29:BoolExact = BoxBool v28 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v29 - "); - } - - #[test] - fn test_specialize_false_eqq() { - eval(" - def test(a, b) = a === b - test(true, 5) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@<compiled>: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 - PatchPoint MethodRedefined(TrueClass@0x1000, ==@0x1038, cme:0x1040) - v28:CBool = IsBitEqual v25, v12 - v29:BoolExact = BoxBool v28 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v29 - "); - } - - #[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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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_string_subclass_to_s_returns_string_exact() { - eval(r#" - class C < String; end - def test(o) = o.to_s - test(C.new) - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@<compiled>: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(C@0x1000, to_s@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(C@0x1000) - v23:StringSubclass[class_exact:C] = GuardType v9, StringSubclass[class_exact:C] - v24:StringExact = CCallWithFrame to_s@0x1038, v23 - CheckInterrupts - Return v24 - "); - } - - #[test] - fn test_inline_string_literal_to_s() { - eval(r#" - def test = "foo".to_s - "#); - assert_snapshot!(hir_string("test"), @r" - fn test@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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_array_pop_no_arg() { - eval(" - def test(arr) = arr.pop - test([1]) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@<compiled>: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, pop@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - v23:ArrayExact = GuardType v9, ArrayExact - v24:ArrayExact = GuardNotFrozen v23 - v25:BasicObject = ArrayPop v24 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v25 - "); - } - - #[test] - fn test_do_not_optimize_array_pop_arg() { - eval(" - def test(arr) = arr.pop(4) - test([1]) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@<compiled>: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[4] = Const Value(4) - PatchPoint MethodRedefined(Array@0x1000, pop@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - v24:ArrayExact = GuardType v9, ArrayExact - v25:BasicObject = CCallVariadic pop@0x1038, v24, v13 - CheckInterrupts - Return v25 - "); - } - - #[test] - fn test_guard_array_pop_frozen() { - eval(" - def test(arr) - arr.pop - rescue FrozenError - nil - end - arr = [1].freeze - test(arr) - "); - assert_snapshot!(hir_string("test"), @r" - fn test@<compiled>: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(Array@0x1000, pop@0x1008, cme:0x1010) - PatchPoint NoSingletonClass(Array@0x1000) - v23:ArrayExact = GuardType v9, ArrayExact - v24:ArrayExact = GuardNotFrozen v23 - v25:BasicObject = ArrayPop v24 - IncrCounter inline_cfunc_optimized_send_count - CheckInterrupts - Return v25 - "); - } - - #[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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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 0000000000..897cd6e956 --- /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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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 <compiled>@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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 0000000000..df14ba5a7c --- /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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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 <compiled>@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<internal:kernel>: + 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@<internal:kernel>: + 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@<internal:dir>: + 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@<internal:gc>: + 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@<internal:gc>: + 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@<internal:symbol>: + 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@<internal:symbol>: + 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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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 <compiled>@<compiled>: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 <compiled>@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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@<compiled>: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) + "); + } +} |
