@@ -4951,8 +4951,12 @@ impl Function {
49514951 self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass: recv_class, method: method_id, cme }, state });
49524952 }
49534953
4954- /// Side exit back to the state after a block-backed send.
4955- /// Using the pre-send snapshot would re-execute the send in the interpreter.
4954+ /// Side exit back to the state after a send. Using the pre-send snapshot would re-execute the
4955+ /// send in the interpreter.
4956+ ///
4957+ /// Any send could cause the caller's environment to escape, and this patchpoint scopes our
4958+ /// analysis to the cases where the environment remains not escaped across the send. The
4959+ /// patchpoint also protects against local modifications through `Kernel#eval` and friends.
49564960 fn gen_post_send_no_ep_escape_patch_point(&mut self, block: BlockId, state: &FrameState, insn_idx: u32) {
49574961 let iseq = state.iseq;
49584962 let mut reload_state = state.clone();
@@ -8616,12 +8620,18 @@ fn add_iseq_to_hir(
86168620 let send = fun.push_insn(block, Insn::Send { recv, cd, block: None, args, state: exit_id, reason });
86178621 fun.push_insn(block, Insn::Jump(BranchEdge { target: join_block, args: vec![send] }));
86188622 state.stack_push(join_param);
8623+ if !state.locals.is_empty() {
8624+ fun.gen_post_send_no_ep_escape_patch_point(join_block, &state, insn_idx);
8625+ }
86198626 // Continue compilation from the join block at the next instruction.
86208627 block = join_block;
86218628 } else {
86228629 // Maybe monomorphic; handled in type_specialize
86238630 let send = fun.push_insn(block, Insn::Send { recv, cd, block: None, args, state: exit_id, reason: Uncategorized(opcode) });
86248631 state.stack_push(send);
8632+ if !state.locals.is_empty() {
8633+ fun.gen_post_send_no_ep_escape_patch_point(block, &state, insn_idx);
8634+ }
86258635 }
86268636 }
86278637 YARVINSN_send => {
@@ -8652,14 +8662,14 @@ fn add_iseq_to_hir(
86528662 };
86538663 let send = fun.push_insn(block, Insn::Send { recv, cd, block: block_handler, args, state: exit_id, reason: Uncategorized(opcode) });
86548664 state.stack_push(send);
8665+ if !state.locals.is_empty() {
8666+ fun.gen_post_send_no_ep_escape_patch_point(block, &state, insn_idx);
8667+ }
86558668
86568669 if let Some(BlockHandler::BlockIseq(_)) = block_handler {
86578670 // Reload locals that may have been modified by the blockiseq.
86588671 // TODO: Avoid reloading locals that are not referenced by the blockiseq
86598672 // or not used after this. Max thinks we could eventually DCE them.
8660- if !ep_escaped && !state.locals.is_empty() {
8661- fun.gen_post_send_no_ep_escape_patch_point(block, &state, insn_idx);
8662- }
86638673 let mut base: Option<InsnId> = None;
86648674 for local_idx in 0..state.locals.len() {
86658675 let ep_offset = local_idx_to_ep_offset(iseq, local_idx);
@@ -8700,12 +8710,12 @@ fn add_iseq_to_hir(
87008710 let recv = state.stack_pop()?;
87018711 let send_forward = fun.push_insn(block, Insn::SendForward { recv, cd, blockiseq, args, state: exit_id, reason: SendForwardNotSpecialized });
87028712 state.stack_push(send_forward);
8713+ if !state.locals.is_empty() {
8714+ fun.gen_post_send_no_ep_escape_patch_point(block, &state, insn_idx);
8715+ }
87038716
87048717 if !blockiseq.is_null() {
87058718 // Reload locals that may have been modified by the blockiseq.
8706- if !ep_escaped && !state.locals.is_empty() {
8707- fun.gen_post_send_no_ep_escape_patch_point(block, &state, insn_idx);
8708- }
87098719 let mut base: Option<InsnId> = None;
87108720 for local_idx in 0..state.locals.len() {
87118721 let ep_offset = local_idx_to_ep_offset(iseq, local_idx);
@@ -8743,14 +8753,14 @@ fn add_iseq_to_hir(
87438753 let blockiseq: IseqPtr = get_arg(pc, 1).as_ptr();
87448754 let result = fun.push_insn(block, Insn::InvokeSuper { recv, cd, blockiseq, args, state: exit_id, reason: Uncategorized(opcode) });
87458755 state.stack_push(result);
8756+ if !state.locals.is_empty() {
8757+ fun.gen_post_send_no_ep_escape_patch_point(block, &state, insn_idx);
8758+ }
87468759
87478760 if !blockiseq.is_null() {
87488761 // Reload locals that may have been modified by the blockiseq.
87498762 // TODO: Avoid reloading locals that are not referenced by the blockiseq
87508763 // or not used after this. Max thinks we could eventually DCE them.
8751- if !ep_escaped && !state.locals.is_empty() {
8752- fun.gen_post_send_no_ep_escape_patch_point(block, &state, insn_idx);
8753- }
87548764 let mut base: Option<InsnId> = None;
87558765 for local_idx in 0..state.locals.len() {
87568766 let ep_offset = local_idx_to_ep_offset(iseq, local_idx);
@@ -8790,14 +8800,14 @@ fn add_iseq_to_hir(
87908800 let recv = state.stack_pop()?;
87918801 let result = fun.push_insn(block, Insn::InvokeSuperForward { recv, cd, blockiseq, args, state: exit_id, reason: InvokeSuperForwardNotSpecialized });
87928802 state.stack_push(result);
8803+ if !state.locals.is_empty() {
8804+ fun.gen_post_send_no_ep_escape_patch_point(block, &state, insn_idx);
8805+ }
87938806
87948807 if !blockiseq.is_null() {
87958808 // Reload locals that may have been modified by the blockiseq.
87968809 // TODO: Avoid reloading locals that are not referenced by the blockiseq
87978810 // or not used after this. Max thinks we could eventually DCE them.
8798- if !ep_escaped && !state.locals.is_empty() {
8799- fun.gen_post_send_no_ep_escape_patch_point(block, &state, insn_idx);
8800- }
88018811 let mut base: Option<InsnId> = None;
88028812 for local_idx in 0..state.locals.len() {
88038813 let ep_offset = local_idx_to_ep_offset(iseq, local_idx);
0 commit comments