diff --git a/doc/contributing/building_ruby.md b/doc/contributing/building_ruby.md index 286bd1f1161635..a283a2f3dbd3ef 100644 --- a/doc/contributing/building_ruby.md +++ b/doc/contributing/building_ruby.md @@ -21,7 +21,7 @@ * We can upgrade this version to system ruby version of the latest Ubuntu LTS. * git - 2.32 or later - * Anterior versions may work; 2.32 or later will prevent build + * Earlier versions may work; 2.32 or later will prevent build errors in case your system `.gitconfig` uses `$HOME` paths. 2. Install optional, recommended dependencies: @@ -151,7 +151,7 @@ ruby ├── build # Created in step 2 and populated in step 4 │ ├── GNUmakefile # Generated by `../configure` │ ├── Makefile # Generated by `../configure` -│ ├── object.o # Compiled object file, built my `make` +│ ├── object.o # Compiled object file, built by `make` │ └── ... other compiled `.o` object files │ │ # Other interesting files: diff --git a/internal/numeric.h b/internal/numeric.h index 280c008952ed7d..a24432df1e6450 100644 --- a/internal/numeric.h +++ b/internal/numeric.h @@ -70,6 +70,7 @@ VALUE rb_float_minus(VALUE x, VALUE y); VALUE rb_int_mul(VALUE x, VALUE y); VALUE rb_float_mul(VALUE x, VALUE y); VALUE rb_float_div(VALUE x, VALUE y); +VALUE rb_flo_to_i(VALUE num); VALUE rb_int_idiv(VALUE x, VALUE y); VALUE rb_int_modulo(VALUE x, VALUE y); VALUE rb_int2str(VALUE num, int base); diff --git a/numeric.c b/numeric.c index 287294f9b5a899..02a7fbed0cd4c8 100644 --- a/numeric.c +++ b/numeric.c @@ -2565,6 +2565,12 @@ flo_to_i(VALUE num) return dbl2ival(f); } +VALUE +rb_flo_to_i(VALUE num) +{ + return flo_to_i(num); +} + /* * call-seq: * truncate(ndigits = 0) -> float or integer diff --git a/test/date/test_date_parse.rb b/test/date/test_date_parse.rb index 720624c02e338b..8308f258d50e39 100644 --- a/test/date/test_date_parse.rb +++ b/test/date/test_date_parse.rb @@ -591,13 +591,7 @@ def test__parse_odd_offset end def test__parse_too_long_year - # Math.log10 does not support so big numbers like 10^100_000 on TruffleRuby - unless RUBY_ENGINE == 'truffleruby' - str = "Jan 1" + "0" * 100_000 - h = EnvUtil.timeout(3) {Date._parse(str, limit: 100_010)} - assert_equal(100_000, Math.log10(h[:year])) - assert_equal(1, h[:mon]) - end + omit 'transient' if RUBY_ENGINE == 'truffleruby' str = "Jan - 1" + "0" * 100_000 h = EnvUtil.timeout(3) {Date._parse(str, limit: 100_010)} diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 84ded50300b114..a56fea6d51a417 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -418,6 +418,18 @@ def run(n) = n.times { make } RUBY end + def test_float_arithmetic + assert_compiles '4.0', 'def test = 1.5 + 2.5; test' + assert_compiles '6.0', 'def test = 2.0 * 3.0; test' + assert_compiles '1.5', 'def test = 3.5 - 2.0; test' + assert_compiles '2.5', 'def test = 5.0 / 2.0; test' + assert_compiles '4.5', 'def test = 1.5 * 3; test' # Float * Fixnum + assert_compiles 'true', 'def test = (Float::NAN + 1.0).nan?; test' + assert_compiles 'Infinity', 'def test = Float::INFINITY * 2.0; test' + assert_compiles '3', 'def test = 3.7.to_i; test' + assert_compiles '-2', 'def test = (-2.9).to_i; test' + end + private # Assert that every method call in `test_script` can be compiled by ZJIT diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index b3f390026bc144..ba6d91a4fe8480 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -201,6 +201,7 @@ fn main() { .allowlist_function("rb_float_minus") .allowlist_function("rb_float_mul") .allowlist_function("rb_float_div") + .allowlist_function("rb_flo_to_i") .allowlist_type("ruby_rstring_private_flags") .allowlist_function("rb_ec_str_resurrect") .allowlist_function("rb_str_concat_literals") diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index da15d30d03c741..72068c1e5b1775 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -675,6 +675,11 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::FixnumSub { left, right, state } => gen_fixnum_sub(jit, asm, opnd!(left), opnd!(right), &function.frame_state(*state)), Insn::FixnumMult { left, right, state } => gen_fixnum_mult(jit, asm, opnd!(left), opnd!(right), &function.frame_state(*state)), Insn::FixnumDiv { left, right, state } => gen_fixnum_div(jit, asm, opnd!(left), opnd!(right), &function.frame_state(*state)), + Insn::FloatAdd { recv, other, state } => gen_float_add(asm, opnd!(recv), opnd!(other), &function.frame_state(*state)), + Insn::FloatSub { recv, other, state } => gen_float_sub(asm, opnd!(recv), opnd!(other), &function.frame_state(*state)), + Insn::FloatMul { recv, other, state } => gen_float_mul(asm, opnd!(recv), opnd!(other), &function.frame_state(*state)), + Insn::FloatDiv { recv, other, state } => gen_float_div(asm, opnd!(recv), opnd!(other), &function.frame_state(*state)), + Insn::FloatToInt { recv, state } => gen_float_to_int(asm, opnd!(recv), &function.frame_state(*state)), Insn::FixnumEq { left, right } => gen_fixnum_eq(asm, opnd!(left), opnd!(right)), Insn::FixnumNeq { left, right } => gen_fixnum_neq(asm, opnd!(left), opnd!(right)), Insn::FixnumLt { left, right } => gen_fixnum_lt(asm, opnd!(left), opnd!(right)), @@ -2301,6 +2306,36 @@ fn gen_fixnum_div(jit: &mut JITState, asm: &mut Assembler, left: lir::Opnd, righ asm_ccall!(asm, rb_jit_fix_div_fix, left, right) } +/// Compile Float + Float +fn gen_float_add(asm: &mut Assembler, recv: lir::Opnd, other: lir::Opnd, state: &FrameState) -> lir::Opnd { + gen_prepare_leaf_call_with_gc(asm, state); + asm_ccall!(asm, rb_float_plus, recv, other) +} + +/// Compile Float - Float +fn gen_float_sub(asm: &mut Assembler, recv: lir::Opnd, other: lir::Opnd, state: &FrameState) -> lir::Opnd { + gen_prepare_leaf_call_with_gc(asm, state); + asm_ccall!(asm, rb_float_minus, recv, other) +} + +/// Compile Float * Float +fn gen_float_mul(asm: &mut Assembler, recv: lir::Opnd, other: lir::Opnd, state: &FrameState) -> lir::Opnd { + gen_prepare_leaf_call_with_gc(asm, state); + asm_ccall!(asm, rb_float_mul, recv, other) +} + +/// Compile Float / Float +fn gen_float_div(asm: &mut Assembler, recv: lir::Opnd, other: lir::Opnd, state: &FrameState) -> lir::Opnd { + gen_prepare_leaf_call_with_gc(asm, state); + asm_ccall!(asm, rb_float_div, recv, other) +} + +/// Compile Float#to_i (truncate to integer) +fn gen_float_to_int(asm: &mut Assembler, recv: lir::Opnd, state: &FrameState) -> lir::Opnd { + gen_prepare_leaf_call_with_gc(asm, state); + asm_ccall!(asm, rb_flo_to_i, recv) +} + /// Compile Fixnum == Fixnum fn gen_fixnum_eq(asm: &mut Assembler, left: lir::Opnd, right: lir::Opnd) -> lir::Opnd { asm.cmp(left, right); diff --git a/zjit/src/codegen_tests.rs b/zjit/src/codegen_tests.rs index 474cbee5b5b6ba..b2cc401694f083 100644 --- a/zjit/src/codegen_tests.rs +++ b/zjit/src/codegen_tests.rs @@ -5632,3 +5632,19 @@ fn test_loop_terminates() { end "#), @"3"); } + +// Regression test: getlocal with level=0 after setlocal_WC_0 was loading stale EP +// memory, causing Array#pack with buffer: keyword to receive the wrong buffer VALUE. +// See https://github.com/ruby/ruby/pull/16736 +#[test] +fn test_getlocal_level_zero_after_setlocal_wc_0() { + assert_snapshot!(inspect(r#" + def test + b = +"x" + v = 2 + [v].pack("C*", buffer: b) + b.size + end + test + "#), @"2"); +} diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index e110c7a5ef125f..1ef1ee15d51997 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -2139,6 +2139,7 @@ unsafe extern "C" { pub fn rb_float_minus(x: VALUE, y: VALUE) -> VALUE; pub fn rb_float_mul(x: VALUE, y: VALUE) -> VALUE; pub fn rb_float_div(x: VALUE, y: VALUE) -> VALUE; + pub fn rb_flo_to_i(num: VALUE) -> VALUE; pub fn rb_fix_aref(fix: VALUE, idx: VALUE) -> VALUE; pub fn rb_vm_insn_addr2opcode(addr: *const ::std::os::raw::c_void) -> ::std::os::raw::c_int; pub fn rb_iseq_line_no(iseq: *const rb_iseq_t, pos: usize) -> ::std::os::raw::c_uint; diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index 96db1386f1c8f1..93ba7da6403321 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -265,6 +265,12 @@ pub fn init() -> Annotations { annotate!(rb_cInteger, ">>", inline_integer_rshift); annotate!(rb_cInteger, "[]", inline_integer_aref); annotate!(rb_cInteger, "to_s", types::StringExact); + annotate!(rb_cFloat, "+", inline_float_plus); + annotate!(rb_cFloat, "-", inline_float_minus); + annotate!(rb_cFloat, "*", inline_float_mul); + annotate!(rb_cFloat, "/", inline_float_div); + annotate!(rb_cFloat, "to_i", inline_float_to_i); + annotate!(rb_cFloat, "to_int", inline_float_to_i); annotate!(rb_cString, "to_s", inline_string_to_s, types::StringExact); annotate!(rb_cFloat, "nan?", types::BoolExact, no_gc, leaf, elidable); annotate!(rb_cFloat, "finite?", types::BoolExact, no_gc, leaf, elidable); @@ -629,6 +635,54 @@ fn inline_integer_xor(fun: &mut hir::Function, block: hir::BlockId, recv: hir::I None } +fn try_inline_float_op(fun: &mut hir::Function, block: hir::BlockId, f: &dyn Fn(hir::InsnId, hir::InsnId) -> hir::Insn, bop: u32, recv: hir::InsnId, other: hir::InsnId, state: hir::InsnId) -> Option { + if !unsafe { rb_BASIC_OP_UNREDEFINED_P(bop, FLOAT_REDEFINED_OP_FLAG) } { + return None; + } + // Receiver must be Flonum (cheap tag check: (val & 3) == 2). + // The other operand can be Flonum or Fixnum since rb_float_plus/minus/mul/div + // handle both via fast paths (FIXNUM_P check + cast to double). + // HeapFloat falls back to CCallWithFrame via the default Send path. + if fun.likely_a(recv, types::Flonum, state) + && (fun.likely_a(other, types::Flonum, state) || fun.likely_a(other, types::Fixnum, state)) + { + let recv = fun.coerce_to(block, recv, types::Flonum, state); + let other_type = if fun.likely_a(other, types::Flonum, state) { types::Flonum } else { types::Fixnum }; + let other = fun.coerce_to(block, other, other_type, state); + return Some(fun.push_insn(block, f(recv, other))); + } + None +} + +fn inline_float_plus(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { + let &[other] = args else { return None; }; + try_inline_float_op(fun, block, &|recv, other| hir::Insn::FloatAdd { recv, other, state }, BOP_PLUS, recv, other, state) +} + +fn inline_float_minus(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { + let &[other] = args else { return None; }; + try_inline_float_op(fun, block, &|recv, other| hir::Insn::FloatSub { recv, other, state }, BOP_MINUS, recv, other, state) +} + +fn inline_float_mul(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { + let &[other] = args else { return None; }; + try_inline_float_op(fun, block, &|recv, other| hir::Insn::FloatMul { recv, other, state }, BOP_MULT, recv, other, state) +} + +fn inline_float_div(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { + let &[other] = args else { return None; }; + try_inline_float_op(fun, block, &|recv, other| hir::Insn::FloatDiv { recv, other, state }, BOP_DIV, recv, other, state) +} + +fn inline_float_to_i(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { + let &[] = args else { return None; }; + if fun.likely_a(recv, types::Flonum, state) { + let recv = fun.coerce_to(block, recv, types::Flonum, state); + return Some(fun.push_insn(block, hir::Insn::FloatToInt { recv, state })); + } + None +} + fn try_inline_fixnum_op(fun: &mut hir::Function, block: hir::BlockId, f: &dyn Fn(hir::InsnId, hir::InsnId) -> hir::Insn, bop: u32, left: hir::InsnId, right: hir::InsnId, state: hir::InsnId) -> Option { if !unsafe { rb_BASIC_OP_UNREDEFINED_P(bop, INTEGER_REDEFINED_OP_FLAG) } { // If the basic operation is already redefined, we cannot optimize it. diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 786a994d8b0146..671249f7010cca 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -1092,6 +1092,14 @@ pub enum Insn { FixnumLShift { left: InsnId, right: InsnId, state: InsnId }, FixnumRShift { left: InsnId, right: InsnId }, + /// Float arithmetic: delegates to rb_float_plus/minus/mul/div with GC preparation + FloatAdd { recv: InsnId, other: InsnId, state: InsnId }, + FloatSub { recv: InsnId, other: InsnId, state: InsnId }, + FloatMul { recv: InsnId, other: InsnId, state: InsnId }, + FloatDiv { recv: InsnId, other: InsnId, state: InsnId }, + /// Float#to_i: truncate float to integer via rb_jit_flo_to_i + FloatToInt { recv: InsnId, state: InsnId }, + // Distinct from `Send` with `mid:to_s` because does not have a patch point for String to_s being redefined ObjToString { val: InsnId, cd: *const rb_call_data, state: InsnId }, AnyToString { val: InsnId, str: InsnId, state: InsnId }, @@ -1284,6 +1292,18 @@ macro_rules! for_each_operand_impl { $visit_one!(right); $visit_one!(state); } + Insn::FloatAdd { recv, other, state } + | Insn::FloatSub { recv, other, state } + | Insn::FloatMul { recv, other, state } + | Insn::FloatDiv { recv, other, state } => { + $visit_one!(recv); + $visit_one!(other); + $visit_one!(state); + } + Insn::FloatToInt { recv, state } => { + $visit_one!(recv); + $visit_one!(state); + } Insn::FixnumLt { left, right } | Insn::FixnumLe { left, right } | Insn::FixnumGt { left, right } @@ -1627,6 +1647,11 @@ impl Insn { Insn::FixnumMult { .. } => effects::Empty, Insn::FixnumDiv { .. } => effects::Any, Insn::FixnumMod { .. } => effects::Any, + Insn::FloatAdd { .. } => effects::Any, + Insn::FloatSub { .. } => effects::Any, + Insn::FloatMul { .. } => effects::Any, + Insn::FloatDiv { .. } => effects::Any, + Insn::FloatToInt { .. } => effects::Any, Insn::FixnumEq { .. } => effects::Empty, Insn::FixnumNeq { .. } => effects::Empty, Insn::FixnumLt { .. } => effects::Empty, @@ -2021,6 +2046,11 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::FixnumMult { left, right, .. } => { write!(f, "FixnumMult {left}, {right}") }, Insn::FixnumDiv { left, right, .. } => { write!(f, "FixnumDiv {left}, {right}") }, Insn::FixnumMod { left, right, .. } => { write!(f, "FixnumMod {left}, {right}") }, + Insn::FloatAdd { recv, other, .. } => { write!(f, "FloatAdd {recv}, {other}") }, + Insn::FloatSub { recv, other, .. } => { write!(f, "FloatSub {recv}, {other}") }, + Insn::FloatMul { recv, other, .. } => { write!(f, "FloatMul {recv}, {other}") }, + Insn::FloatDiv { recv, other, .. } => { write!(f, "FloatDiv {recv}, {other}") }, + Insn::FloatToInt { recv, .. } => { write!(f, "FloatToInt {recv}") }, Insn::FixnumEq { left, right, .. } => { write!(f, "FixnumEq {left}, {right}") }, Insn::FixnumNeq { left, right, .. } => { write!(f, "FixnumNeq {left}, {right}") }, Insn::FixnumLt { left, right, .. } => { write!(f, "FixnumLt {left}, {right}") }, @@ -2840,6 +2870,11 @@ impl Function { &FixnumMult { left, right, state } => FixnumMult { left: find!(left), right: find!(right), state }, &FixnumDiv { left, right, state } => FixnumDiv { left: find!(left), right: find!(right), state }, &FixnumMod { left, right, state } => FixnumMod { left: find!(left), right: find!(right), state }, + &FloatAdd { recv, other, state } => FloatAdd { recv: find!(recv), other: find!(other), state }, + &FloatSub { recv, other, state } => FloatSub { recv: find!(recv), other: find!(other), state }, + &FloatMul { recv, other, state } => FloatMul { recv: find!(recv), other: find!(other), state }, + &FloatDiv { recv, other, state } => FloatDiv { recv: find!(recv), other: find!(other), state }, + &FloatToInt { recv, state } => FloatToInt { recv: find!(recv), state }, &FixnumNeq { left, right } => FixnumNeq { left: find!(left), right: find!(right) }, &FixnumEq { left, right } => FixnumEq { left: find!(left), right: find!(right) }, &FixnumGt { left, right } => FixnumGt { left: find!(left), right: find!(right) }, @@ -3103,6 +3138,11 @@ impl Function { Insn::FixnumMult { .. } => types::Fixnum, Insn::FixnumDiv { .. } => types::Fixnum, Insn::FixnumMod { .. } => types::Fixnum, + Insn::FloatAdd { .. } => types::Float, + Insn::FloatSub { .. } => types::Float, + Insn::FloatMul { .. } => types::Float, + Insn::FloatDiv { .. } => types::Float, + Insn::FloatToInt { .. } => types::Integer, Insn::FixnumEq { .. } => types::BoolExact, Insn::FixnumNeq { .. } => types::BoolExact, Insn::FixnumLt { .. } => types::BoolExact, @@ -6211,6 +6251,18 @@ impl Function { self.assert_subtype(insn_id, left, types::Fixnum)?; self.assert_subtype(insn_id, right, types::Fixnum) } + Insn::FloatAdd { recv, other, .. } + | Insn::FloatSub { recv, other, .. } + | Insn::FloatMul { recv, other, .. } + | Insn::FloatDiv { recv, other, .. } + => { + self.assert_subtype(insn_id, recv, types::Flonum)?; + // other can be Flonum or Fixnum (rb_float_plus etc. handle both) + self.assert_subtype(insn_id, other, types::Flonum.union(types::Fixnum)) + } + Insn::FloatToInt { recv, .. } => { + self.assert_subtype(insn_id, recv, types::Flonum) + } Insn::FixnumLShift { left, right, .. } | Insn::FixnumRShift { left, right, .. } => { self.assert_subtype(insn_id, left, types::Fixnum)?; @@ -7363,8 +7415,18 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { YARVINSN_getlocal => { let ep_offset = get_arg(pc, 0).as_u32(); let level = get_arg(pc, 1).as_u32(); - let ep = fun.push_insn(block, Insn::GetEP { level }); - state.stack_push(fun.get_local_from_ep(block, ep, ep_offset, level, types::BasicObject)); + if level == 0 && !local_inval { + // Same optimization as getlocal_WC_0: use FrameState + let val = state.getlocal(ep_offset); + state.stack_push(val); + } else { + let ep = fun.push_insn(block, Insn::GetEP { level }); + let val = fun.get_local_from_ep(block, ep, ep_offset, level, types::BasicObject); + if level == 0 { + state.setlocal(ep_offset, val); + } + state.stack_push(val); + } } YARVINSN_setlocal => { let ep_offset = get_arg(pc, 0).as_u32(); diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index b14df62461772e..08b2136f86b04b 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -15871,4 +15871,187 @@ mod hir_opt_tests { Return v24 "); } + #[test] + fn test_float_add_inline() { + eval(r#" + def test(a, b) = a + b + test(1.0, 2.0) + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:2: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :a@0x1000 + v4:BasicObject = LoadField v2, :b@0x1001 + Jump bb3(v1, v3, v4) + bb2(): + EntryPoint JIT(0) + v7:BasicObject = LoadArg :self@0 + v8:BasicObject = LoadArg :a@1 + v9:BasicObject = LoadArg :b@2 + Jump bb3(v7, v8, v9) + bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): + PatchPoint MethodRedefined(Float@0x1008, +@0x1010, cme:0x1018) + v28:Flonum = GuardType v12, Flonum + v29:Flonum = GuardType v13, Flonum + v30:Float = FloatAdd v28, v29 + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_float_mul_inline() { + eval(r#" + def test(a, b) = a * b + test(1.5, 2.5) + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:2: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :a@0x1000 + v4:BasicObject = LoadField v2, :b@0x1001 + Jump bb3(v1, v3, v4) + bb2(): + EntryPoint JIT(0) + v7:BasicObject = LoadArg :self@0 + v8:BasicObject = LoadArg :a@1 + v9:BasicObject = LoadArg :b@2 + Jump bb3(v7, v8, v9) + bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): + PatchPoint MethodRedefined(Float@0x1008, *@0x1010, cme:0x1018) + v28:Flonum = GuardType v12, Flonum + v29:Flonum = GuardType v13, Flonum + v30:Float = FloatMul v28, v29 + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_float_sub_inline() { + eval(r#" + def test(a, b) = a - b + test(5.0, 3.0) + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:2: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :a@0x1000 + v4:BasicObject = LoadField v2, :b@0x1001 + Jump bb3(v1, v3, v4) + bb2(): + EntryPoint JIT(0) + v7:BasicObject = LoadArg :self@0 + v8:BasicObject = LoadArg :a@1 + v9:BasicObject = LoadArg :b@2 + Jump bb3(v7, v8, v9) + bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): + PatchPoint MethodRedefined(Float@0x1008, -@0x1010, cme:0x1018) + v28:Flonum = GuardType v12, Flonum + v29:Flonum = GuardType v13, Flonum + v30:Float = FloatSub v28, v29 + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_float_div_inline() { + eval(r#" + def test(a, b) = a / b + test(10.0, 3.0) + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:2: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :a@0x1000 + v4:BasicObject = LoadField v2, :b@0x1001 + Jump bb3(v1, v3, v4) + bb2(): + EntryPoint JIT(0) + v7:BasicObject = LoadArg :self@0 + v8:BasicObject = LoadArg :a@1 + v9:BasicObject = LoadArg :b@2 + Jump bb3(v7, v8, v9) + bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): + PatchPoint MethodRedefined(Float@0x1008, /@0x1010, cme:0x1018) + v28:Flonum = GuardType v12, Flonum + v29:Flonum = GuardType v13, Flonum + v30:Float = FloatDiv v28, v29 + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_float_to_i_inline() { + eval(r#" + def test(a) = a.to_i + test(3.7) + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:2: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :a@0x1000 + Jump bb3(v1, v3) + bb2(): + EntryPoint JIT(0) + v6:BasicObject = LoadArg :self@0 + v7:BasicObject = LoadArg :a@1 + Jump bb3(v6, v7) + bb3(v9:BasicObject, v10:BasicObject): + PatchPoint MethodRedefined(Float@0x1008, to_i@0x1010, cme:0x1018) + v23:Flonum = GuardType v10, Flonum + v24:Integer = FloatToInt v23 + CheckInterrupts + Return v24 + "); + } + + #[test] + fn test_float_mul_fixnum_inline() { + eval(r#" + def test(a, b) = a * b + test(1.5, 3) + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:2: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :a@0x1000 + v4:BasicObject = LoadField v2, :b@0x1001 + Jump bb3(v1, v3, v4) + bb2(): + EntryPoint JIT(0) + v7:BasicObject = LoadArg :self@0 + v8:BasicObject = LoadArg :a@1 + v9:BasicObject = LoadArg :b@2 + Jump bb3(v7, v8, v9) + bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): + PatchPoint MethodRedefined(Float@0x1008, *@0x1010, cme:0x1018) + v28:Flonum = GuardType v12, Flonum + v29:Fixnum = GuardType v13, Fixnum + v30:Float = FloatMul v28, v29 + CheckInterrupts + Return v30 + "); + } + } diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 4cb01d02c89635..dae3619f78f11e 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -2900,10 +2900,8 @@ pub(crate) mod hir_build_tests { v31:StringExact = StringCopy v30 v37:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) v38:StringExact = StringCopy v37 - v40:CPtr = GetEP 0 - v41:BasicObject = LoadField v40, :buf@0x1018 PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_PACK) - v44:String = ArrayPackBuffer v16, v17, fmt: v38, buf: v41 + v42:String = ArrayPackBuffer v16, v17, fmt: v38, buf: v31 PatchPoint NoEPEscape(test) CheckInterrupts Return v31 @@ -2949,8 +2947,6 @@ pub(crate) mod hir_build_tests { v31:StringExact = StringCopy v30 v37:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) v38:StringExact = StringCopy v37 - v40:CPtr = GetEP 0 - v41:BasicObject = LoadField v40, :buf@0x1018 SideExit PatchPoint(BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_PACK)) "); }