@@ -4720,6 +4720,17 @@ impl Function {
47204720 self . push_insn ( block, Insn :: PatchPoint { invariant : Invariant :: MethodRedefined { klass : recv_class, method : method_id, cme } , state } ) ;
47214721 }
47224722
4723+ /// Side exit back to the state after a block-backed send.
4724+ /// Using the pre-send snapshot would re-execute the send in the interpreter.
4725+ fn gen_post_send_no_ep_escape_patch_point ( & mut self , block : BlockId , state : & FrameState , insn_idx : u32 ) {
4726+ let iseq = state. iseq ;
4727+ let mut reload_state = state. clone ( ) ;
4728+ reload_state. insn_idx = insn_idx as usize ;
4729+ reload_state. pc = unsafe { rb_iseq_pc_at_idx ( iseq, insn_idx) } ;
4730+ let reload_exit_id = self . push_insn ( block, Insn :: Snapshot { state : reload_state. without_locals ( ) } ) ;
4731+ self . push_insn ( block, Insn :: PatchPoint { invariant : Invariant :: NoEPEscape ( iseq) , state : reload_exit_id } ) ;
4732+ }
4733+
47234734 fn count_not_inlined_cfunc ( & mut self , block : BlockId , cme : * const rb_callable_method_entry_t ) {
47244735 let owner = unsafe { ( * cme) . owner } ;
47254736 let called_id = unsafe { ( * cme) . called_id } ;
@@ -8004,13 +8015,23 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
80048015 // Reload locals that may have been modified by the blockiseq.
80058016 // TODO: Avoid reloading locals that are not referenced by the blockiseq
80068017 // or not used after this. Max thinks we could eventually DCE them.
8007- let ep = fun. push_insn ( block, Insn :: GetEP { level : 0 } ) ;
8018+ if !ep_escaped && !state. locals . is_empty ( ) {
8019+ fun. gen_post_send_no_ep_escape_patch_point ( block, & state, insn_idx) ;
8020+ }
8021+ let mut base: Option < InsnId > = None ;
80088022 for local_idx in 0 ..state. locals . len ( ) {
80098023 let ep_offset = local_idx_to_ep_offset ( iseq, local_idx) ;
80108024 let ep_offset_u32 = u32:: try_from ( ep_offset)
80118025 . unwrap_or_else ( |_| panic ! ( "Could not convert ep_offset {ep_offset} to u32" ) ) ;
8012- // TODO: We could use `use_sp: true` with PatchPoint.
8013- let val = fun. get_local_from_ep ( block, ep, ep_offset_u32, 0 , types:: BasicObject ) ;
8026+ let recv = * base. get_or_insert_with ( || {
8027+ let base_insn = if !ep_escaped { Insn :: LoadSP } else { Insn :: GetEP { level : 0 } } ;
8028+ fun. push_insn ( block, base_insn)
8029+ } ) ;
8030+ let val = if !ep_escaped {
8031+ fun. get_local_from_sp ( block, recv, ep_offset_u32, types:: BasicObject )
8032+ } else {
8033+ fun. get_local_from_ep ( block, recv, ep_offset_u32, 0 , types:: BasicObject )
8034+ } ;
80148035 state. setlocal ( ep_offset_u32, val) ;
80158036 }
80168037 }
@@ -8040,13 +8061,23 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
80408061
80418062 if !blockiseq. is_null ( ) {
80428063 // Reload locals that may have been modified by the blockiseq.
8043- let ep = fun. push_insn ( block, Insn :: GetEP { level : 0 } ) ;
8064+ if !ep_escaped && !state. locals . is_empty ( ) {
8065+ fun. gen_post_send_no_ep_escape_patch_point ( block, & state, insn_idx) ;
8066+ }
8067+ let mut base: Option < InsnId > = None ;
80448068 for local_idx in 0 ..state. locals . len ( ) {
80458069 let ep_offset = local_idx_to_ep_offset ( iseq, local_idx) ;
80468070 let ep_offset_u32 = u32:: try_from ( ep_offset)
80478071 . unwrap_or_else ( |_| panic ! ( "Could not convert ep_offset {ep_offset} to u32" ) ) ;
8048- // TODO: We could use `use_sp: true` with PatchPoint.
8049- let val = fun. get_local_from_ep ( block, ep, ep_offset_u32, 0 , types:: BasicObject ) ;
8072+ let recv = * base. get_or_insert_with ( || {
8073+ let base_insn = if !ep_escaped { Insn :: LoadSP } else { Insn :: GetEP { level : 0 } } ;
8074+ fun. push_insn ( block, base_insn)
8075+ } ) ;
8076+ let val = if !ep_escaped {
8077+ fun. get_local_from_sp ( block, recv, ep_offset_u32, types:: BasicObject )
8078+ } else {
8079+ fun. get_local_from_ep ( block, recv, ep_offset_u32, 0 , types:: BasicObject )
8080+ } ;
80508081 state. setlocal ( ep_offset_u32, val) ;
80518082 }
80528083 }
@@ -8077,13 +8108,23 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
80778108 // Reload locals that may have been modified by the blockiseq.
80788109 // TODO: Avoid reloading locals that are not referenced by the blockiseq
80798110 // or not used after this. Max thinks we could eventually DCE them.
8080- let ep = fun. push_insn ( block, Insn :: GetEP { level : 0 } ) ;
8111+ if !ep_escaped && !state. locals . is_empty ( ) {
8112+ fun. gen_post_send_no_ep_escape_patch_point ( block, & state, insn_idx) ;
8113+ }
8114+ let mut base: Option < InsnId > = None ;
80818115 for local_idx in 0 ..state. locals . len ( ) {
80828116 let ep_offset = local_idx_to_ep_offset ( iseq, local_idx) ;
80838117 let ep_offset_u32 = u32:: try_from ( ep_offset)
80848118 . unwrap_or_else ( |_| panic ! ( "Could not convert ep_offset {ep_offset} to u32" ) ) ;
8085- // TODO: We could use `use_sp: true` with PatchPoint.
8086- let val = fun. get_local_from_ep ( block, ep, ep_offset_u32, 0 , types:: BasicObject ) ;
8119+ let recv = * base. get_or_insert_with ( || {
8120+ let base_insn = if !ep_escaped { Insn :: LoadSP } else { Insn :: GetEP { level : 0 } } ;
8121+ fun. push_insn ( block, base_insn)
8122+ } ) ;
8123+ let val = if !ep_escaped {
8124+ fun. get_local_from_sp ( block, recv, ep_offset_u32, types:: BasicObject )
8125+ } else {
8126+ fun. get_local_from_ep ( block, recv, ep_offset_u32, 0 , types:: BasicObject )
8127+ } ;
80878128 state. setlocal ( ep_offset_u32, val) ;
80888129 }
80898130 }
@@ -8114,13 +8155,23 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
81148155 // Reload locals that may have been modified by the blockiseq.
81158156 // TODO: Avoid reloading locals that are not referenced by the blockiseq
81168157 // or not used after this. Max thinks we could eventually DCE them.
8117- let ep = fun. push_insn ( block, Insn :: GetEP { level : 0 } ) ;
8158+ if !ep_escaped && !state. locals . is_empty ( ) {
8159+ fun. gen_post_send_no_ep_escape_patch_point ( block, & state, insn_idx) ;
8160+ }
8161+ let mut base: Option < InsnId > = None ;
81188162 for local_idx in 0 ..state. locals . len ( ) {
81198163 let ep_offset = local_idx_to_ep_offset ( iseq, local_idx) ;
81208164 let ep_offset_u32 = u32:: try_from ( ep_offset)
81218165 . unwrap_or_else ( |_| panic ! ( "Could not convert ep_offset {ep_offset} to u32" ) ) ;
8122- // TODO: We could use `use_sp: true` with PatchPoint.
8123- let val = fun. get_local_from_ep ( block, ep, ep_offset_u32, 0 , types:: BasicObject ) ;
8166+ let recv = * base. get_or_insert_with ( || {
8167+ let base_insn = if !ep_escaped { Insn :: LoadSP } else { Insn :: GetEP { level : 0 } } ;
8168+ fun. push_insn ( block, base_insn)
8169+ } ) ;
8170+ let val = if !ep_escaped {
8171+ fun. get_local_from_sp ( block, recv, ep_offset_u32, types:: BasicObject )
8172+ } else {
8173+ fun. get_local_from_ep ( block, recv, ep_offset_u32, 0 , types:: BasicObject )
8174+ } ;
81248175 state. setlocal ( ep_offset_u32, val) ;
81258176 }
81268177 }
0 commit comments