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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions doc/contributing/building_ruby.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions internal/numeric.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions numeric.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 1 addition & 7 deletions test/date/test_date_parse.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)}
Expand Down
12 changes: 12 additions & 0 deletions test/ruby/test_zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions zjit/bindgen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
35 changes: 35 additions & 0 deletions zjit/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down Expand Up @@ -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);
Expand Down
16 changes: 16 additions & 0 deletions zjit/src/codegen_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
1 change: 1 addition & 0 deletions zjit/src/cruby_bindings.inc.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 54 additions & 0 deletions zjit/src/cruby_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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<hir::InsnId> {
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<hir::InsnId> {
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<hir::InsnId> {
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<hir::InsnId> {
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<hir::InsnId> {
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<hir::InsnId> {
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<hir::InsnId> {
if !unsafe { rb_BASIC_OP_UNREDEFINED_P(bop, INTEGER_REDEFINED_OP_FLAG) } {
// If the basic operation is already redefined, we cannot optimize it.
Expand Down
66 changes: 64 additions & 2 deletions zjit/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down Expand Up @@ -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 }
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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}") },
Expand Down Expand Up @@ -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) },
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -7363,8 +7415,18 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
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();
Expand Down
Loading