diff --git a/majit/majit-metainterp/src/trace_ctx.rs b/majit/majit-metainterp/src/trace_ctx.rs index f45ec3d2160..438281dd6ba 100644 --- a/majit/majit-metainterp/src/trace_ctx.rs +++ b/majit/majit-metainterp/src/trace_ctx.rs @@ -544,7 +544,6 @@ pub struct ReconstructRecipe { /// stable code identity rather than a live wrapper courier. pub code_ptr: *const (), pub jitcode_index: i32, - pub pc: usize, /// Guard-carried JitCode offset from the decoded resume frame; /// `majit_ir::resumedata::NO_JITCODE_PC` when the frame carried none. pub jitcode_pc: i32, @@ -569,9 +568,8 @@ pub struct BridgeInlineCarrier { /// pc; the root must instead resume at its own `frames[0].pc`, so this is /// threaded separately rather than derived from the trace start pc. pub root_pc: usize, - /// Guard-carried JitCode offset from `resume_data.frames[0]`; - /// `majit_ir::resumedata::NO_JITCODE_PC` when the frame carried none. - pub root_jitcode_pc: i32, + /// The JitCode body that owns `root_pc`. + pub root_jitcode_index: i32, /// `resume_data.frames[1..]`, OUTERMOST-FIRST. The portal (`frames[0]`) /// is NOT here — it is the caller-visible root `sym`. pub recipes: Vec, diff --git a/pyre/pyre-jit-trace/src/jitcode_dispatch.rs b/pyre/pyre-jit-trace/src/jitcode_dispatch.rs index 9addc28104a..493d0aa7c50 100644 --- a/pyre/pyre-jit-trace/src/jitcode_dispatch.rs +++ b/pyre/pyre-jit-trace/src/jitcode_dispatch.rs @@ -2806,7 +2806,6 @@ fn compute_bridge_root_parent_frame( root_sym: &crate::state::PyreSym, trace_ctx: &mut TraceCtx, root_pc: usize, - root_carried_jitcode_pc: i32, ) -> Option { if root_sym.jitcode.is_null() { return None; @@ -2815,7 +2814,7 @@ fn compute_bridge_root_parent_frame( // `root_pc` (`resume_data.frames[0].pc`) is already the post-call resume // point — the slot the inner frame's result lands in — so it is the // fallthrough `resume_py_pc` directly (no `semantic_fallthrough_pc`). - let resume_py_pc = root_pc as u32; + let resume_py_pc = crate::state::backxlat_py_pc(jitcode_index as i32, root_pc as i32) as u32; // Null the not-yet-produced call-result slot before collecting the active // boxes (the reconstructed callee supplies it on `SubReturn`), mirroring // `compute_inline_caller_frame`. Operate on a clone so `root_sym` stays a @@ -2834,14 +2833,17 @@ fn compute_bridge_root_parent_frame( .bridge_registers_r .clone() .unwrap_or_else(|| root_sym.registers_r.clone()); - if let Some(result_color) = crate::state::result_color_at_pc_at(jitcode_index as i32, root_pc) { + let root_py_pc = crate::state::backxlat_py_pc(jitcode_index as i32, root_pc as i32) as usize; + if let Some(result_color) = + crate::state::result_color_at_pc_at(jitcode_index as i32, root_py_pc) + { if result_color < regs_r.len() { regs_r[result_color] = trace_ctx.const_ref(pyre_object::PY_NULL as i64); } } - let root_word = (root_carried_jitcode_pc != majit_ir::resumedata::NO_JITCODE_PC - && root_carried_jitcode_pc >= 0) - .then_some(root_carried_jitcode_pc as usize); + let root_word = ((root_pc as i32) != majit_ir::resumedata::NO_JITCODE_PC + && (root_pc as i32) >= 0) + .then_some(root_pc); let root_liveness_word = match root_word.filter(|_| m73_outercap_carry_enabled()) { Some(w) => w as i32, None => majit_ir::resumedata::NO_JITCODE_PC, @@ -2879,7 +2881,7 @@ fn compute_bridge_root_parent_frame( /// callee frame of a multi-frame bridge as an INLINE SUB-WALK /// (`is_top_level = false`) rooted on the caller-visible portal `root_sym`. /// -/// The callee resumes at `entry` (its `resume_jitcode_pc_for(recipe.pc)`) with +/// The callee resumes at `entry` (its translated recipe Python pc) with /// its registers seeded by `argboxes_r` (portal reds + in-flight operand-stack /// temps from `setup_reconstructed_callee_frame`) and its locals carried in the /// already-emitted frame vable. Because the walk is a sub-walk, the callee's @@ -2902,7 +2904,6 @@ pub(crate) fn drive_bridge_carrier_subwalk( session: &std::cell::RefCell, root_sym: &crate::state::PyreSym, root_pc: usize, - root_jitcode_pc: i32, callee_pjc: &std::sync::Arc, callee_code_key: usize, callee_w_globals: usize, @@ -3001,7 +3002,7 @@ pub(crate) fn drive_bridge_carrier_subwalk( } // Paused root portal frame for the multi-frame guard snapshot. - let root_frame = compute_bridge_root_parent_frame(root_sym, ctx, root_pc, root_jitcode_pc)?; + let root_frame = compute_bridge_root_parent_frame(root_sym, ctx, root_pc)?; let outer_jitcode_index = root_frame.jitcode_index; let outer_active_boxes = root_frame.boxes.clone(); @@ -3067,7 +3068,8 @@ pub(crate) fn drive_bridge_carrier_subwalk( last_exc_value: None, last_exc_value_concrete: ConcreteValue::Null, // The outer Python frame is the root, paused at `root_pc`. - entry_py_pc: root_pc as u32, + entry_py_pc: crate::state::backxlat_py_pc(outer_jitcode_index as i32, root_pc as i32) + as u32, outer_resume_marker_jit_pc: root_frame.resume_marker_jit_pc, outer_jitcode_index, outer_active_boxes, @@ -3118,7 +3120,6 @@ pub(crate) fn drive_outer_frame_continuation( root_code_key: usize, root_w_globals: usize, root_pc: usize, - root_jitcode_pc: i32, entry: usize, frame_box: OpRef, frame_reg: usize, @@ -3194,7 +3195,7 @@ pub(crate) fn drive_outer_frame_continuation( regs_f[num_regs_f + i] = ctx.const_float(v); } - let root_frame = compute_bridge_root_parent_frame(root_sym, ctx, root_pc, root_jitcode_pc)?; + let root_frame = compute_bridge_root_parent_frame(root_sym, ctx, root_pc)?; let outer_jitcode_index = root_frame.jitcode_index; let outer_active_boxes = root_frame.boxes.clone(); @@ -3214,13 +3215,13 @@ pub(crate) fn drive_outer_frame_continuation( // walker banks. Only live colors are touched; dead slots stay // `OpRef::NONE` so a later guard snapshot cannot capture a stale operand. { - let root_word = (root_jitcode_pc != majit_ir::resumedata::NO_JITCODE_PC - && root_jitcode_pc >= 0) - .then_some(root_jitcode_pc as usize); + let root_word = ((root_pc as i32) != majit_ir::resumedata::NO_JITCODE_PC + && (root_pc as i32) >= 0) + .then_some(root_pc); // Mirror `compute_bridge_root_parent_frame` so scatter reads the banks in collection order. let banks = crate::state::frame_liveness_reg_indices_by_bank_at_with_jitcode_pc( outer_jitcode_index as i32, - root_pc as i32, + crate::state::backxlat_py_pc(outer_jitcode_index as i32, root_pc as i32), match root_word.filter(|_| m73_outercap_carry_enabled()) { Some(w) => w as i32, None => majit_ir::resumedata::NO_JITCODE_PC, @@ -3335,7 +3336,8 @@ pub(crate) fn drive_outer_frame_continuation( sub_jitcode_lookup: lookup_ref, last_exc_value: None, last_exc_value_concrete: ConcreteValue::Null, - entry_py_pc: root_pc as u32, + entry_py_pc: crate::state::backxlat_py_pc(outer_jitcode_index as i32, root_pc as i32) + as u32, outer_resume_marker_jit_pc: root_frame.resume_marker_jit_pc, outer_jitcode_index, outer_active_boxes, diff --git a/pyre/pyre-jit-trace/src/pyjitcode.rs b/pyre/pyre-jit-trace/src/pyjitcode.rs index 77b6a9751f0..8c3f63a6ea9 100644 --- a/pyre/pyre-jit-trace/src/pyjitcode.rs +++ b/pyre/pyre-jit-trace/src/pyjitcode.rs @@ -524,8 +524,8 @@ impl PyJitCode { /// PC directly in resume data (`miframe.pc`); pyre's resume data /// stores the Python bytecode PC and translates here. This /// translation is permanent: pyre interprets Python bytecode while - /// upstream interprets JitCode, so an *entry* py_pc (inline-callee - /// `recipe.pc`, root-portal / walk-entry `start_pc`) has no genuine + /// upstream interprets JitCode, so an *entry* py_pc (an inline-callee's + /// translated recipe pc, or root-portal / walk-entry `start_pc`) has no genuine /// JitCode coordinate in hand and must be resolved through the /// tables. The dense `pc_map` this once read has been deleted; the /// offset now derives from the two surviving exact tables plus the diff --git a/pyre/pyre-jit-trace/src/state.rs b/pyre/pyre-jit-trace/src/state.rs index fbcdd20b071..e1534192d87 100644 --- a/pyre/pyre-jit-trace/src/state.rs +++ b/pyre/pyre-jit-trace/src/state.rs @@ -787,6 +787,11 @@ pub fn skip_python_trivia_forward_public( } /// Translate a resume-frame pc word to a Python instruction coordinate. +/// +/// A negative word (sentinel / branch-orgpc tag) has no Python coordinate; per +/// the `decode_resume_pc` contract it passes through so the caller's `pc < 0` +/// screen rejects it (the internal metadata lookups below are bounds-checked and +/// never index with the word, so no wrap results). pub fn backxlat_py_pc(jitcode_index: i32, pc_word: i32) -> i32 { let fallback = majit_ir::resumedata::decode_resume_pc(pc_word).0; python_pc_for_jitcode_pc_public(jitcode_index, pc_word) @@ -5956,7 +5961,6 @@ fn reconstruct_inline_recipe( return Some(ReconstructRecipe { code_ptr: raw_code as *const (), jitcode_index: frame.jitcode_index, - pc: py_pc, jitcode_pc: frame.pc, nlocals, valuestackdepth, @@ -6138,7 +6142,6 @@ fn reconstruct_inline_recipe( Some(ReconstructRecipe { code_ptr: raw_code as *const (), jitcode_index: frame.jitcode_index, - pc: py_pc, jitcode_pc: frame.pc, nlocals, valuestackdepth, @@ -8982,13 +8985,13 @@ impl JitState for PyreJitState { ); } if resume_data.frames.len() > 1 { + let root_jitcode_index = resume_data.frames[0].jitcode_index; let root_pc_valid = resume_data.frames[0].pc >= 0; let root_pc = if root_pc_valid { resume_data.frames[0].pc as usize } else { 0 }; - let root_jitcode_pc = resume_data.frames[0].pc; let mut recipes: Vec = Vec::with_capacity(resume_data.frames.len() - 1); let mut ok = root_pc_valid; @@ -9027,7 +9030,7 @@ impl JitState for PyreJitState { } ctx.set_bridge_inline_carrier(BridgeInlineCarrier { root_pc, - root_jitcode_pc, + root_jitcode_index, recipes, }); } @@ -12791,8 +12794,11 @@ pub(crate) fn assemble_bridge_inline_pending( for k in nlocals..valuestackdepth { concrete_frame.push(recipe_slot_to_pyobj(recipe.concrete_r[k])); } - // last_instr = recipe.pc - 1 so next_instr() resumes AT recipe.pc. - concrete_frame.set_last_instr_from_next_instr(recipe.pc); + // last_instr is one before the recipe's Python pc so next_instr() resumes there. + concrete_frame + .set_last_instr_from_next_instr( + backxlat_py_pc(recipe.jitcode_index, recipe.jitcode_pc) as usize + ); // Symbolic side: mirror the FAST branch field-for-field. let mut sym = PyreSym::new_uninit(OpRef::NONE); diff --git a/pyre/pyre-jit-trace/src/trace.rs b/pyre/pyre-jit-trace/src/trace.rs index fda81ab05a1..79e839e078e 100644 --- a/pyre/pyre-jit-trace/src/trace.rs +++ b/pyre/pyre-jit-trace/src/trace.rs @@ -399,6 +399,11 @@ pub fn trace_bytecode( } else { start_pc }; + let lasti_pc = if let Some(ref c) = carrier { + crate::state::backxlat_py_pc(c.root_jitcode_index, c.root_pc as i32) as usize + } else { + start_pc + }; // RPython MetaInterp._interpret() parity: the walker (sole tracer) // executes as it records over a concrete `PyFrame` snapshot // (`snapshot_for_tracing`); the interpreter does not run during tracing. @@ -412,7 +417,7 @@ pub fn trace_bytecode( // record-only, and `flush_walk_end_state_to_frame` // (`raise_continue_running_normally` parity) advances the real frame so // the interpreter resumes AFTER the walked region, not from its start. - concrete_frame.set_last_instr_from_next_instr(start_pc); + concrete_frame.set_last_instr_from_next_instr(lasti_pc); let w_code = concrete_frame.pycode; // Issue #73 walker-as-tracer foundation probe (read-only). // `PYRE_DUMP_PERFN_JITCODE=1` dumps the per-CodeObject JitCode body @@ -420,7 +425,7 @@ pub fn trace_bytecode( // `miframe.pc == jitcode_pc` and `pc_map` can retire. See // `project_issue73_architecture_walker_as_tracer_2026_05_28`. if std::env::var_os("PYRE_DUMP_PERFN_JITCODE").is_some() { - dump_perfn_jitcode_for_trace(w_code, start_pc); + dump_perfn_jitcode_for_trace(w_code, lasti_pc); } let cf_addr = &*concrete_frame as *const pyre_interpreter::pyframe::PyFrame as usize; // The snapshot stands in for concrete stepping only; vable-statics @@ -439,6 +444,12 @@ pub fn trace_bytecode( // (trace_opcode.rs:3323-3424) and don't call init_symbolic; this path // handles the root frame push. sym.init_symbolic(ctx, cf_addr); + if let Some(ref carrier) = carrier { + debug_assert_eq!( + unsafe { (*sym.jitcode).index as i32 }, + carrier.root_jitcode_index + ); + } // Issue #215 item 2: drive the multiframe bridge-carrier resume via the // full-body walker (reconstruct the in-flight callee framestack + walk // innermost-first) instead of aborting to a no-JIT re-interpret below. @@ -855,7 +866,8 @@ fn inject_root_call_result(sym: &mut PyreSym, root_pc: usize, result: majit_ir:: return false; } let jitcode_index = unsafe { (*sym.jitcode).index as i32 }; - let Some(result_color) = crate::state::result_color_at_pc_at(jitcode_index, root_pc) else { + let root_py_pc = crate::state::backxlat_py_pc(jitcode_index, root_pc as i32) as usize; + let Some(result_color) = crate::state::result_color_at_pc_at(jitcode_index, root_py_pc) else { return false; }; let nlocals = sym.nlocals; @@ -886,7 +898,11 @@ fn drive_bridge_carrier_walk( let root_ec = sym.concrete_execution_context; if std::env::var_os("PYRE_P2_DIAG").is_some() { - let pcs: Vec = carrier.recipes.iter().map(|r| r.pc).collect(); + let pcs: Vec = carrier + .recipes + .iter() + .map(|r| crate::state::backxlat_py_pc(r.jitcode_index, r.jitcode_pc) as usize) + .collect(); eprintln!( "[p2-shape] root_pc={root_pc} n_recipes={} recipe_pcs={pcs:?}", carrier.recipes.len() @@ -918,9 +934,14 @@ fn drive_bridge_carrier_walk( let entry = select_recipe_entry( recipe.jitcode_index, callee_pjc.jitcode.index() as i32, - recipe.pc, + crate::state::backxlat_py_pc(recipe.jitcode_index, recipe.jitcode_pc) as usize, recipe.jitcode_pc, - || callee_pjc.resume_jitcode_pc_for(recipe.pc), + || { + callee_pjc.resume_jitcode_pc_for(crate::state::backxlat_py_pc( + recipe.jitcode_index, + recipe.jitcode_pc, + ) as usize) + }, ); let Some(entry) = entry else { ctx.cut_trace(pre_pos); @@ -934,7 +955,6 @@ fn drive_bridge_carrier_walk( // is known. let nlocals = recipe.nlocals.min(recipe.concrete_r.len()); let local_concretes = &recipe.concrete_r[..nlocals]; - // Increment 2b-i: drive the deepest callee as an inline SUB-WALK rooted on // the portal `sym` (is_top_level=false), so its `ref_return` surfaces // `SubReturn` instead of the top-level `Finish` pyre's own-portal model @@ -944,7 +964,6 @@ fn drive_bridge_carrier_walk( &session, sym, root_pc, - carrier.root_jitcode_pc, &callee_pjc, recipe.code_ptr as usize, callee_w_globals, @@ -968,7 +987,10 @@ fn drive_bridge_carrier_walk( if carrier.recipes.len() == 1 && std::env::var_os("PYRE_P2_COMPILE").is_some() { if inject_root_call_result(sym, root_pc, result) { crate::jitcode_dispatch::census_record("P2Drain::CompileRoot"); - return full_body_walk_trace(ctx, sym, w_code, root_pc, cf_addr); + let root_py_pc = + crate::state::backxlat_py_pc(carrier.root_jitcode_index, root_pc as i32) + as usize; + return full_body_walk_trace(ctx, sym, w_code, root_py_pc, cf_addr); } crate::jitcode_dispatch::census_record("P2Drain::ResultSlotUnresolved"); } @@ -977,15 +999,15 @@ fn drive_bridge_carrier_walk( match &walk { Some(Ok((outcome, end_pc))) => { eprintln!( - "[p2-drain] callee sub-walk OK recipe.pc={} entry={entry} end_pc={end_pc} outcome={outcome:?}", - recipe.pc + "[p2-drain] callee sub-walk OK recipe_py_pc={} entry={entry} end_pc={end_pc} outcome={outcome:?}", + crate::state::backxlat_py_pc(recipe.jitcode_index, recipe.jitcode_pc) ); crate::jitcode_dispatch::census_record("P2Drain::SubWalkOk"); } Some(Err(e)) => { eprintln!( - "[p2-drain] callee sub-walk STOP recipe.pc={} entry={entry} err={e:?}", - recipe.pc + "[p2-drain] callee sub-walk STOP recipe_py_pc={} entry={entry} err={e:?}", + crate::state::backxlat_py_pc(recipe.jitcode_index, recipe.jitcode_pc) ); crate::jitcode_dispatch::census_record("P2Drain::SubWalkStop"); } @@ -1048,7 +1070,11 @@ fn drive_bridge_framestack_walk( crate::jitcode_dispatch::fbw_store_journal_reset(); if std::env::var_os("PYRE_P2_DIAG").is_some() { - let pcs: Vec = carrier.recipes.iter().map(|r| r.pc).collect(); + let pcs: Vec = carrier + .recipes + .iter() + .map(|r| crate::state::backxlat_py_pc(r.jitcode_index, r.jitcode_pc) as usize) + .collect(); eprintln!( "[p2-framestack] root_pc={root_pc} n_recipes={} recipe_pcs={pcs:?}", carrier.recipes.len() @@ -1079,9 +1105,14 @@ fn drive_bridge_framestack_walk( let entry = select_recipe_entry( recipe.jitcode_index, callee_pjc.jitcode.index() as i32, - recipe.pc, + crate::state::backxlat_py_pc(recipe.jitcode_index, recipe.jitcode_pc) as usize, recipe.jitcode_pc, - || callee_pjc.resume_jitcode_pc_for(recipe.pc), + || { + callee_pjc.resume_jitcode_pc_for(crate::state::backxlat_py_pc( + recipe.jitcode_index, + recipe.jitcode_pc, + ) as usize) + }, ); let Some(entry) = entry else { ctx.cut_trace(pre_pos); @@ -1091,14 +1122,16 @@ fn drive_bridge_framestack_walk( let callee_w_globals = crate::state::recover_inline_callee_globals(recipe.code_ptr) as usize; let nlocals = recipe.nlocals.min(recipe.concrete_r.len()); let local_concretes = &recipe.concrete_r[..nlocals]; - let pos_after_setup = ctx.get_trace_position(); if std::env::var_os("PYRE_P2_DIAG").is_some() { - let root_entry = crate::state::pyjitcode_for_code(w_code) - .and_then(|pjc| pjc.resume_jitcode_pc_for(root_pc)); + let root_entry = crate::state::pyjitcode_for_code(w_code).and_then(|pjc| { + let root_py_pc = + crate::state::backxlat_py_pc(pjc.jitcode.index() as i32, root_pc as i32) as usize; + pjc.resume_jitcode_pc_for(root_py_pc) + }); eprintln!( - "[p2-fs] callee_entry(jit)={entry} callee.pc(py)={} root_pc(py)={root_pc} root_entry(jit)={root_entry:?} pos_pre={pre_pos:?} pos_after_setup={pos_after_setup:?}", - recipe.pc + "[p2-fs] callee_entry(jit)={entry} callee.pc(py)={} root_pc(jit)={root_pc} root_entry(jit)={root_entry:?} pos_pre={pre_pos:?} pos_after_setup={pos_after_setup:?}", + crate::state::backxlat_py_pc(recipe.jitcode_index, recipe.jitcode_pc) ); } @@ -1111,7 +1144,6 @@ fn drive_bridge_framestack_walk( &session, sym, root_pc, - carrier.root_jitcode_pc, &callee_pjc, recipe.code_ptr as usize, callee_w_globals, @@ -1164,15 +1196,7 @@ fn drive_bridge_framestack_walk( // `SafeAbortReconstruction` below (correct no-JIT re-interpret). if carrier.recipes.len() == 1 { if let Some(action) = drive_outer_continuation_and_map( - ctx, - &session, - sym, - w_code, - root_pc, - carrier.root_jitcode_pc, - cf_addr, - result, - pre_pos, + ctx, &session, sym, w_code, root_pc, cf_addr, result, pre_pos, ) { return action; } @@ -1200,18 +1224,19 @@ fn drive_outer_continuation_and_map( sym: &mut PyreSym, w_code: *const (), root_pc: usize, - root_jitcode_pc: i32, _cf_addr: usize, result: majit_ir::OpRef, pre_pos: majit_metainterp::recorder::TracePosition, ) -> Option { let root_pjc = crate::state::pyjitcode_for_code(w_code)?; + let root_py_pc = + crate::state::backxlat_py_pc(root_pjc.jitcode.index() as i32, root_pc as i32) as usize; let entry = select_recipe_entry( root_pjc.jitcode.index() as i32, root_pjc.jitcode.index() as i32, - root_pc, - root_jitcode_pc, - || root_pjc.resume_jitcode_pc_for(root_pc), + root_py_pc, + root_pc as i32, + || root_pjc.resume_jitcode_pc_for(root_py_pc), )?; // Decode the call-dst register: the op whose `next_pc == entry` is the // residual call the outer resumes after; its `>r` dst is the last operand @@ -1257,7 +1282,6 @@ fn drive_outer_continuation_and_map( w_code as usize, root_w_globals, root_pc, - root_jitcode_pc, entry, frame_box, frame_reg, @@ -1284,8 +1308,12 @@ fn drive_outer_continuation_and_map( Some(Ok((crate::jitcode_dispatch::DispatchOutcome::Terminate, _end_pc))) => { match crate::jitcode_dispatch::fbw_finish_payload_take() { Some((_, majit_ir::Type::Void)) => { - let key = crate::driver::make_green_key(w_code, root_pc); - ctx.set_green_key(key, (w_code as usize, root_pc)); + let green_pc = crate::state::backxlat_py_pc( + root_pjc.jitcode.index() as i32, + root_pc as i32, + ) as usize; + let key = crate::driver::make_green_key(w_code, green_pc); + ctx.set_green_key(key, (w_code as usize, green_pc)); Some(TraceAction::Finish { finish_args: vec![], finish_arg_types: vec![], @@ -1293,8 +1321,12 @@ fn drive_outer_continuation_and_map( }) } Some((finish_value, finish_type)) => { - let key = crate::driver::make_green_key(w_code, root_pc); - ctx.set_green_key(key, (w_code as usize, root_pc)); + let green_pc = crate::state::backxlat_py_pc( + root_pjc.jitcode.index() as i32, + root_pc as i32, + ) as usize; + let key = crate::driver::make_green_key(w_code, green_pc); + ctx.set_green_key(key, (w_code as usize, green_pc)); crate::jitcode_dispatch::census_record("P2Framestack::OuterFinish"); if std::env::var_os("PYRE_P2_DIAG").is_some() { eprintln!( diff --git a/pyre/pyre-jit/src/eval.rs b/pyre/pyre-jit/src/eval.rs index 2f171dcbeba..7c9caf5ab16 100644 --- a/pyre/pyre-jit/src/eval.rs +++ b/pyre/pyre-jit/src/eval.rs @@ -7671,8 +7671,11 @@ fn rebuild_typed_from_rd_numb( // The outer frame's decoded Python position is retained for resume-state // hygiene. The live resume selection uses the rebuilt frame chain. + // pc=-1 = no-snapshot sentinel; screen it out (as build_resumed_frames does) + // so the negative word never reaches the `as usize` cast. let rd_numb_pc = frames .first() + .filter(|f| f.pc >= 0) .map(|f| pyre_jit_trace::state::backxlat_py_pc(f.jitcode_index, f.pc) as usize); (typed, rd_numb_pc, virtuals_cache) }