diff --git a/yjit.c b/yjit.c index db0a5f5978..a1710f9c05 100644 --- a/yjit.c +++ b/yjit.c @@ -37,6 +37,12 @@ #include +// Field offsets for the RString struct +enum rstring_offsets { + RUBY_OFFSET_RSTRING_AS_HEAP_LEN = offsetof(struct RString, as.heap.len), + RUBY_OFFSET_RSTRING_EMBED_LEN = offsetof(struct RString, as.embed.len), +}; + // We need size_t to have a known size to simplify code generation and FFI. // TODO(alan): check this in configure.ac to fail fast on 32 bit platforms. STATIC_ASSERT(64b_size_t, SIZE_MAX == UINT64_MAX); diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index 23b32afbd0..98d9161866 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -81,6 +81,9 @@ fn main() { // From include/ruby/internal/core/rbasic.h .allowlist_type("RBasic") + .allowlist_type("rstring_offsets") + .allowlist_type("ruby_rstring_flags") + // From internal.h // This function prints info about a value and is useful for debugging .allowlist_function("rb_obj_info_dump") diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 00b39006b6..27141bb9bf 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -4123,6 +4123,39 @@ fn jit_rb_str_to_s( false } +// Codegen for rb_str_empty() +fn jit_rb_str_empty( + _jit: &mut JITState, + ctx: &mut Context, + 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 { + const _: () = assert!( + RUBY_OFFSET_RSTRING_AS_HEAP_LEN == RUBY_OFFSET_RSTRING_EMBED_LEN, + "same offset to len embedded or not so we can use one code path to read the length", + ); + + let recv_opnd = ctx.stack_pop(1); + let out_opnd = ctx.stack_push(Type::UnknownImm); + + let str_len_opnd = Opnd::mem( + (8 * size_of::()) as u8, + asm.load(recv_opnd), + RUBY_OFFSET_RSTRING_AS_HEAP_LEN as i32, + ); + + asm.cmp(str_len_opnd, Opnd::UImm(0)); + let string_empty = asm.csel_e(Qtrue.into(), Qfalse.into()); + asm.mov(out_opnd, string_empty); + + return true; +} + // Codegen for rb_str_concat() -- *not* String#concat // Frequently strings are concatenated using "out_str << next_str". // This is common in Erb and similar templating languages. @@ -7330,6 +7363,7 @@ impl CodegenGlobals { self.yjit_reg_method(rb_cInteger, "===", jit_rb_int_equal); // rb_str_to_s() methods in string.c + self.yjit_reg_method(rb_cString, "empty?", jit_rb_str_empty); self.yjit_reg_method(rb_cString, "to_s", jit_rb_str_to_s); self.yjit_reg_method(rb_cString, "to_str", jit_rb_str_to_s); self.yjit_reg_method(rb_cString, "bytesize", jit_rb_str_bytesize); diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index c28518ea38..aef65277b6 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -225,6 +225,9 @@ pub const RUBY_FL_USER19: ruby_fl_type = -2147483648; pub const RUBY_ELTS_SHARED: ruby_fl_type = 16384; pub const RUBY_FL_SINGLETON: ruby_fl_type = 4096; pub type ruby_fl_type = i32; +pub const RSTRING_NOEMBED: ruby_rstring_flags = 8192; +pub const RSTRING_FSTR: ruby_rstring_flags = 536870912; +pub type ruby_rstring_flags = u32; pub type st_data_t = ::std::os::raw::c_ulong; pub type st_index_t = st_data_t; pub const ST_CONTINUE: st_retval = 0; @@ -1052,6 +1055,9 @@ pub type ruby_vminsn_type = u32; pub type rb_iseq_callback = ::std::option::Option< unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void), >; +pub const RUBY_OFFSET_RSTRING_AS_HEAP_LEN: rstring_offsets = 16; +pub const RUBY_OFFSET_RSTRING_EMBED_LEN: rstring_offsets = 16; +pub type rstring_offsets = u32; pub type rb_seq_param_keyword_struct = rb_iseq_constant_body__bindgen_ty_1_rb_iseq_param_keyword; extern "C" { pub fn rb_singleton_class(obj: VALUE) -> VALUE;