From 09427f51a23724fa8ee04ecb94d306add917d0b0 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 31 Jan 2024 09:58:47 -0800 Subject: [PATCH] YJIT: Add codegen for Float arithmetics (#9774) * YJIT: Add codegen for Float arithmetics * Add Flonum and Fixnum tests --- bootstraptest/test_yjit.rb | 10 ++ yjit/bindgen/src/main.rs | 4 + yjit/src/codegen.rs | 181 +++++++++++++++++++++++++++++++++ yjit/src/cruby_bindings.inc.rs | 4 + yjit/src/stats.rs | 1 + 5 files changed, 200 insertions(+) diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index aef47ab0ad..58977b3e55 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -2484,6 +2484,16 @@ assert_equal '[true, false, true, false]', %q{ [is_odd(123), is_odd(456), is_odd(bignum), is_odd(bignum+1)] } +# Flonum and Flonum +assert_equal '[2.0, 0.0, 1.0, 4.0]', %q{ + [1.0 + 1.0, 1.0 - 1.0, 1.0 * 1.0, 8.0 / 2.0] +} + +# Flonum and Fixnum +assert_equal '[2.0, 0.0, 1.0, 4.0]', %q{ + [1.0 + 1, 1.0 - 1, 1.0 * 1, 8.0 / 2] +} + # Call to static and dynamic symbol assert_equal 'bar', %q{ def to_string(obj) diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index 2c264287bc..a09ab068cb 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -217,6 +217,10 @@ fn main() { // From internal/numeric.h .allowlist_function("rb_fix_aref") + .allowlist_function("rb_float_plus") + .allowlist_function("rb_float_minus") + .allowlist_function("rb_float_mul") + .allowlist_function("rb_float_div") // From internal/string.h .allowlist_function("rb_ec_str_resurrect") diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index b79054556e..0b71666c4f 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -4992,6 +4992,182 @@ fn jit_rb_int_aref( true } +fn jit_rb_float_plus( + jit: &mut JITState, + asm: &mut Assembler, + ocb: &mut OutlinedCb, + _ci: *const rb_callinfo, + _cme: *const rb_callable_method_entry_t, + _block: Option, + _argc: i32, + _known_recv_class: *const VALUE, +) -> bool { + // Guard obj is Fixnum or Flonum to avoid rb_funcall on rb_num_coerce_bin + let comptime_obj = jit.peek_at_stack(&asm.ctx, 0); + if comptime_obj.fixnum_p() || comptime_obj.flonum_p() { + let obj = asm.stack_opnd(0); + jit_guard_known_klass( + jit, + asm, + ocb, + comptime_obj.class_of(), + obj, + obj.into(), + comptime_obj, + SEND_MAX_DEPTH, + Counter::guard_send_not_fixnum_or_flonum, + ); + } else { + return false; + } + + // Save the PC and SP because the callee may allocate Float on heap + jit_prepare_routine_call(jit, asm); + + asm_comment!(asm, "Float#+"); + let obj = asm.stack_opnd(0); + let recv = asm.stack_opnd(1); + + let ret = asm.ccall(rb_float_plus as *const u8, vec![recv, obj]); + asm.stack_pop(2); // Keep recv during ccall for GC + + let ret_opnd = asm.stack_push(Type::Unknown); // Flonum or heap Float + asm.mov(ret_opnd, ret); + true +} + +fn jit_rb_float_minus( + jit: &mut JITState, + asm: &mut Assembler, + ocb: &mut OutlinedCb, + _ci: *const rb_callinfo, + _cme: *const rb_callable_method_entry_t, + _block: Option, + _argc: i32, + _known_recv_class: *const VALUE, +) -> bool { + // Guard obj is Fixnum or Flonum to avoid rb_funcall on rb_num_coerce_bin + let comptime_obj = jit.peek_at_stack(&asm.ctx, 0); + if comptime_obj.fixnum_p() || comptime_obj.flonum_p() { + let obj = asm.stack_opnd(0); + jit_guard_known_klass( + jit, + asm, + ocb, + comptime_obj.class_of(), + obj, + obj.into(), + comptime_obj, + SEND_MAX_DEPTH, + Counter::guard_send_not_fixnum_or_flonum, + ); + } else { + return false; + } + + // Save the PC and SP because the callee may allocate Float on heap + jit_prepare_routine_call(jit, asm); + + asm_comment!(asm, "Float#-"); + let obj = asm.stack_opnd(0); + let recv = asm.stack_opnd(1); + + let ret = asm.ccall(rb_float_minus as *const u8, vec![recv, obj]); + asm.stack_pop(2); // Keep recv during ccall for GC + + let ret_opnd = asm.stack_push(Type::Unknown); // Flonum or heap Float + asm.mov(ret_opnd, ret); + true +} + +fn jit_rb_float_mul( + jit: &mut JITState, + asm: &mut Assembler, + ocb: &mut OutlinedCb, + _ci: *const rb_callinfo, + _cme: *const rb_callable_method_entry_t, + _block: Option, + _argc: i32, + _known_recv_class: *const VALUE, +) -> bool { + // Guard obj is Fixnum or Flonum to avoid rb_funcall on rb_num_coerce_bin + let comptime_obj = jit.peek_at_stack(&asm.ctx, 0); + if comptime_obj.fixnum_p() || comptime_obj.flonum_p() { + let obj = asm.stack_opnd(0); + jit_guard_known_klass( + jit, + asm, + ocb, + comptime_obj.class_of(), + obj, + obj.into(), + comptime_obj, + SEND_MAX_DEPTH, + Counter::guard_send_not_fixnum_or_flonum, + ); + } else { + return false; + } + + // Save the PC and SP because the callee may allocate Float on heap + jit_prepare_routine_call(jit, asm); + + asm_comment!(asm, "Float#*"); + let obj = asm.stack_opnd(0); + let recv = asm.stack_opnd(1); + + let ret = asm.ccall(rb_float_mul as *const u8, vec![recv, obj]); + asm.stack_pop(2); // Keep recv during ccall for GC + + let ret_opnd = asm.stack_push(Type::Unknown); // Flonum or heap Float + asm.mov(ret_opnd, ret); + true +} + +fn jit_rb_float_div( + jit: &mut JITState, + asm: &mut Assembler, + ocb: &mut OutlinedCb, + _ci: *const rb_callinfo, + _cme: *const rb_callable_method_entry_t, + _block: Option, + _argc: i32, + _known_recv_class: *const VALUE, +) -> bool { + // Guard obj is Fixnum or Flonum to avoid rb_funcall on rb_num_coerce_bin + let comptime_obj = jit.peek_at_stack(&asm.ctx, 0); + if comptime_obj.fixnum_p() || comptime_obj.flonum_p() { + let obj = asm.stack_opnd(0); + jit_guard_known_klass( + jit, + asm, + ocb, + comptime_obj.class_of(), + obj, + obj.into(), + comptime_obj, + SEND_MAX_DEPTH, + Counter::guard_send_not_fixnum_or_flonum, + ); + } else { + return false; + } + + // Save the PC and SP because the callee may allocate Float on heap + jit_prepare_routine_call(jit, asm); + + asm_comment!(asm, "Float#/"); + let obj = asm.stack_opnd(0); + let recv = asm.stack_opnd(1); + + let ret = asm.ccall(rb_float_div as *const u8, vec![recv, obj]); + asm.stack_pop(2); // Keep recv during ccall for GC + + let ret_opnd = asm.stack_push(Type::Unknown); // Flonum or heap Float + asm.mov(ret_opnd, ret); + true +} + /// If string is frozen, duplicate it to get a non-frozen string. Otherwise, return it. fn jit_rb_str_uplus( jit: &mut JITState, @@ -9179,6 +9355,11 @@ pub fn yjit_reg_method_codegen_fns() { yjit_reg_method(rb_cInteger, "^", jit_rb_int_xor); yjit_reg_method(rb_cInteger, "[]", jit_rb_int_aref); + yjit_reg_method(rb_cFloat, "+", jit_rb_float_plus); + yjit_reg_method(rb_cFloat, "-", jit_rb_float_minus); + yjit_reg_method(rb_cFloat, "*", jit_rb_float_mul); + yjit_reg_method(rb_cFloat, "/", jit_rb_float_div); + yjit_reg_method(rb_cString, "empty?", jit_rb_str_empty_p); yjit_reg_method(rb_cString, "to_s", jit_rb_str_to_s); yjit_reg_method(rb_cString, "to_str", jit_rb_str_to_s); diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index db71c1b0db..4246468cea 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1030,6 +1030,10 @@ extern "C" { ) -> ::std::os::raw::c_int; pub fn rb_insn_len(insn: VALUE) -> ::std::os::raw::c_int; pub fn rb_vm_insn_decode(encoded: VALUE) -> ::std::os::raw::c_int; + pub fn rb_float_plus(x: VALUE, y: VALUE) -> VALUE; + 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_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/yjit/src/stats.rs b/yjit/src/stats.rs index 6a28a76c7e..19e452be16 100644 --- a/yjit/src/stats.rs +++ b/yjit/src/stats.rs @@ -437,6 +437,7 @@ make_counters! { guard_send_instance_of_class_mismatch, guard_send_interrupted, guard_send_not_fixnums, + guard_send_not_fixnum_or_flonum, guard_send_not_string, guard_send_respond_to_mid_mismatch,