@@ -4957,6 +4957,93 @@ fn gen_opt_new(
49574957 }
49584958}
49594959
4960+ fn gen_opt_string_new (
4961+ jit : & mut JITState ,
4962+ asm : & mut Assembler ,
4963+ ) -> Option < CodegenStatus > {
4964+ let cd = jit. get_arg ( 0 ) . as_ptr ( ) ;
4965+ let jump_offset = jit. get_arg ( 1 ) . as_i32 ( ) ;
4966+ let capa_loc = jit. get_arg ( 2 ) . as_i32 ( ) ;
4967+
4968+ if !jit. at_compile_target ( ) {
4969+ return jit. defer_compilation ( asm) ;
4970+ }
4971+
4972+ let ci = unsafe { get_call_data_ci ( cd) } ; // info about the call site
4973+ let mid = unsafe { vm_ci_mid ( ci) } ;
4974+ let argc: i32 = unsafe { vm_ci_argc ( ci) } . try_into ( ) . unwrap ( ) ;
4975+
4976+ let recv_idx = argc;
4977+ let comptime_recv = jit. peek_at_stack ( & asm. ctx , recv_idx as isize ) ;
4978+
4979+ // The branch target is the generic new path, which calls new/initialize.
4980+ let jump_idx = jit. next_insn_idx ( ) as i32 + jump_offset;
4981+
4982+ // The fast path only specializes String itself. Anything else bails to the
4983+ // generic path.
4984+ if comptime_recv != unsafe { rb_cString } {
4985+ return end_block_with_jump ( jit, asm, jump_idx as u16 ) ;
4986+ }
4987+
4988+ let comptime_recv_klass = comptime_recv. class_of ( ) ;
4989+ let recv = asm. stack_opnd ( recv_idx) ;
4990+
4991+ perf_call ! ( "opt_string_new: " , jit_guard_known_klass(
4992+ jit,
4993+ asm,
4994+ recv,
4995+ recv. into( ) ,
4996+ comptime_recv,
4997+ SEND_MAX_DEPTH ,
4998+ Counter :: guard_send_klass_megamorphic,
4999+ ) ) ;
5000+
5001+ // The interpreter skips the initialize call entirely, so the fast path is
5002+ // only valid while "new" is the builtin and String#initialize is unredefined.
5003+ // Otherwise fall back to the generic path.
5004+ if jit. assume_expected_cfunc ( asm, comptime_recv_klass, mid, rb_class_new_instance_pass_kw as _ )
5005+ && assume_method_basic_definition ( jit, asm, comptime_recv_klass, ID ! ( initialize) ) {
5006+
5007+ extern "C" {
5008+ fn rb_str_buf_new ( capa : std:: os:: raw:: c_long ) -> VALUE ;
5009+ fn rb_str_new_capa_for_init ( capa : std:: os:: raw:: c_long ) -> VALUE ;
5010+ }
5011+
5012+ let str = if argc == 0 {
5013+ // bare String.new: an empty (embedded) string. rb_str_buf_new skips
5014+ // the encoding/coderange setup that rb_str_new would do.
5015+ jit_prepare_non_leaf_call ( jit, asm) ;
5016+ asm. ccall ( rb_str_buf_new as * const u8 , vec ! [ Opnd :: Imm ( 0 ) ] )
5017+ } else {
5018+ // String.new(capacity: n): only fixnum capacities take the fast path.
5019+ // A non-fixnum side-exits so the interpreter takes the generic path.
5020+ let capa = asm. stack_opnd ( capa_loc) ;
5021+ guard_object_is_fixnum ( jit, asm, capa, capa. into ( ) ) ;
5022+
5023+ jit_prepare_non_leaf_call ( jit, asm) ;
5024+
5025+ // FIX2LONG; rb_str_new_capa_for_init floors anything below the
5026+ // minimum buffer size (including negatives) so no clamping needed.
5027+ let capa = asm. load ( asm. stack_opnd ( capa_loc) ) ;
5028+ let capa = asm. rshift ( capa, Opnd :: UImm ( 1 ) ) ;
5029+ asm. ccall ( rb_str_new_capa_for_init as * const u8 , vec ! [ capa] )
5030+ } ;
5031+
5032+ // Write the allocated string into both the receiver slot and the
5033+ // bookkeeping slot (mirrors TOPN(argc) and TOPN(argc + 1)).
5034+ let result = asm. stack_opnd ( recv_idx + 1 ) ;
5035+ let recv = asm. stack_opnd ( recv_idx) ;
5036+ asm. ctx . set_opnd_mapping ( recv. into ( ) , TempMapping :: MapToStack ( Type :: CString ) ) ;
5037+ asm. mov ( recv, str) ;
5038+ asm. ctx . set_opnd_mapping ( result. into ( ) , TempMapping :: MapToStack ( Type :: CString ) ) ;
5039+ asm. mov ( result, str) ;
5040+
5041+ jump_to_next_insn ( jit, asm)
5042+ } else {
5043+ return end_block_with_jump ( jit, asm, jump_idx as u16 ) ;
5044+ }
5045+ }
5046+
49605047fn gen_jump (
49615048 jit : & mut JITState ,
49625049 asm : & mut Assembler ,
@@ -10825,6 +10912,7 @@ fn get_gen_fn(opcode: VALUE) -> Option<InsnGenFn> {
1082510912 YARVINSN_branchnil_without_ints => Some ( gen_branchnil) ,
1082610913 YARVINSN_jump_without_ints => Some ( gen_jump) ,
1082710914 YARVINSN_opt_new => Some ( gen_opt_new) ,
10915+ YARVINSN_opt_string_new => Some ( gen_opt_string_new) ,
1082810916
1082910917 YARVINSN_getblockparamproxy => Some ( gen_getblockparamproxy) ,
1083010918 YARVINSN_getblockparam => Some ( gen_getblockparam) ,
0 commit comments