Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
08ddb54
majit: emit guard_class for a read of the object header's class word
youknowone Aug 28, 2026
1d397fb
interpreter: promote w_class in the unary override probe
youknowone Sep 1, 2026
9e3ad35
jit-trace: descend pos, neg and invert whole through one table-driven…
youknowone Aug 28, 2026
43996ba
bench: re-record arith_int_bool jitstats for cranelift and wasm
youknowone Aug 28, 2026
1e26d0c
majit-translate: add helper_graphs seeds to the find_all_graphs BFS
youknowone Aug 29, 2026
0c1b566
majit-translate: join single-entry blocks and rtype the front's bool …
youknowone Aug 29, 2026
ff05191
interpreter: gate the binop override probes on promoted exactness
youknowone Aug 29, 2026
3f19897
jit-trace: descend binary_value_from_tag for exact int/bool operands
youknowone Aug 29, 2026
3d0f4df
jit-trace: seed the heap cache with the class word a NewWithVtable wr…
youknowone Aug 29, 2026
0caa37f
interpreter: split compare_slot's int arm from the looping layouts
youknowone Aug 29, 2026
6b02218
pyre-object: mark ensure_object_subclass_ranges_initialized not_in_trace
youknowone Aug 29, 2026
e6495dd
majit-metainterp: register guard_class/r>r in the production blackhol…
youknowone Aug 29, 2026
0904372
jit-trace: mark the class known in note_class_word_after_new
youknowone Aug 29, 2026
ebcb2d7
jit-trace: descend compare_value_from_tag for exact int/bool operands
youknowone Aug 29, 2026
cb105b3
bench: re-record fib_recursive cranelift jitstats
youknowone Aug 29, 2026
ee53a61
interpreter: retarget bigint_add/sub/mul to their jit_bigint_* seams
youknowone Aug 29, 2026
9610c1d
pyre-object: read a list's length without its lock
youknowone Aug 29, 2026
81f4210
bench: restore fib_recursive jitstats to the origin/main baseline
youknowone Aug 29, 2026
b208a8e
pyre-object: read the storage length fields directly in live_len
youknowone Aug 29, 2026
24275f9
jit-trace: restore the unary-negative int fold's wiring and helper
youknowone Aug 29, 2026
f0cfeac
interpreter: box the name for the uncached type lookup under the JIT
youknowone Aug 29, 2026
d14a91f
pyre-object: mark lookup_exc_class_for_kind elidable
youknowone Aug 29, 2026
b8dc765
jit-trace: narrow three over-approximations in the descent blocker scan
youknowone Aug 29, 2026
8154b86
bench: gate the generated unary descent paths
youknowone Aug 29, 2026
1556c6b
bench: size the foriter segmenting fixtures to this branch's raw op c…
youknowone Aug 30, 2026
a5def8c
bench: record unary_long_descent dynasm jitstats
youknowone Aug 30, 2026
e4ed118
majit: lower string reference equality for descents
youknowone Aug 30, 2026
e875443
jit-trace: retire the unary_negative_int fold
youknowone Aug 30, 2026
73f6d5c
interpreter: box the name for the uncached lookup_where pair under th…
youknowone Aug 30, 2026
cf37bae
majit-translate: type can_enter_jit's false result as Bool; re-record…
youknowone Aug 30, 2026
314fc4e
majit: fuse the zero_division constructor raise into a published mate…
youknowone Aug 30, 2026
27b6a24
jit-trace: retire the binary_op_int fold onto the generated descent
youknowone Aug 30, 2026
53af33a
jit-trace: retire the builtin_len fold onto the generated descent
youknowone Aug 30, 2026
1b902fc
docs: re-derive the 3.8 counts from symbol boundaries; update gate-tr…
youknowone Aug 30, 2026
3e44c07
interpreter: register the builtin len gateway fnaddr on wasm32
youknowone Aug 31, 2026
0337701
bench: branch the segmenting trace_limit on sys.platform; record four…
youknowone Aug 31, 2026
d21ead9
jit-trace: size Ref field descrs from the target word, not a 64-bit l…
youknowone Aug 31, 2026
da72c29
fmt
youknowone Aug 31, 2026
ea5aca2
bench: relabel load_name_builtin_cell_fold to the descent's fold label
youknowone Aug 31, 2026
3df7e30
bench: drop a superseded trace_limit sweep paragraph
youknowone Aug 31, 2026
c1c905c
bench: record arith_int_bool wasm guard_failures at 2611
youknowone Aug 31, 2026
e941d67
jit: lower the unary HLOps to canonical codewriter inline_call_r_r; r…
youknowone Sep 1, 2026
4a6bb54
jit-trace: count RECORDED_OPS only for residual calls that join the h…
youknowone Sep 1, 2026
0f1f1dd
majit-translate: count the synthetic entry into the start block in jo…
youknowone Sep 1, 2026
3d74f6f
jit-trace: widen switch_descr_targets to None on an unresolved key
youknowone Sep 1, 2026
0945f36
jit-trace: gate the descent blocker summary prints behind fbw_debug_a…
youknowone Sep 1, 2026
0d804af
interpreter: answer lookup_where_pair with one raw walk outside traces
youknowone Sep 1, 2026
bc42edf
majit-translate: name guard_class in op_name
youknowone Sep 1, 2026
65b99fd
comments: cite upstream by symbol instead of line number
youknowone Sep 1, 2026
86bc9ee
bench: record the segmenting band once, at the set_param call
youknowone Sep 1, 2026
5425e46
majit: fold a virtualizable field to the stored box, not to its position
youknowone Sep 1, 2026
434ab0d
majit: read a copied guard descr's rd_locs through to its donor
youknowone Sep 1, 2026
76d82b1
majit-translate: move guard_class off the byte raw_store_f now holds
youknowone Sep 1, 2026
225adfc
jit: emit the unary inline_call_r_r only for a fully bound callee body
youknowone Sep 1, 2026
de53019
bench: record calls_closures dynasm on the side of trace_limit it now…
youknowone Sep 1, 2026
99e0fd8
majit: carry the donor's rd_locs when a minted guard descr adopts its…
youknowone Sep 1, 2026
9a298d7
bench: record calls_closures cranelift on the same side of trace_limit
youknowone Sep 1, 2026
00573a2
jit-trace: memoize the descent blocker scan per entry argument-array …
youknowone Sep 2, 2026
83aac20
jit-trace: report the rewind clause in the subwalk-abort diagnostic
youknowone Sep 2, 2026
65de699
jit-trace: pass the load_super_attr descent the two parameters it lacked
youknowone Sep 2, 2026
67576ce
bench: return the calls_closures and fib_recursive baselines to origi…
youknowone Sep 2, 2026
25e2715
jit-trace: record the exact-int zero-divisor raise as generated ops
youknowone Sep 2, 2026
1a69ee1
jit-trace, bench: two guards generate_guard declines, and a merge poi…
youknowone Sep 2, 2026
bdab5cc
jit-trace: name the callee a NULL-Ref residual refusal fired on
youknowone Sep 2, 2026
4a0e410
bench: record the one guard failure the wasm nbody and short-circuit …
youknowone Sep 2, 2026
ef5ea37
jit-trace: read the short-form int_copy source as the inline byte
youknowone Sep 2, 2026
7c1122f
jit-trace: size the long payload and cell header Ref fields as WORD
youknowone Sep 2, 2026
c9f7925
jit-trace: spell the dead-arm scan test's condition in the byte that …
youknowone Sep 2, 2026
1e573c0
majit-translate, jit-trace: stamp the bool hop's result Bool and answ…
youknowone Sep 3, 2026
094502e
jit-trace: stop the proxy write-back scan from dereferencing a non-ob…
youknowone Sep 3, 2026
2be6db0
jit-trace: name the BigInt layout in the IS_OP fold's value-comparing…
youknowone Sep 3, 2026
ae56eb7
interpreter: stack-check the comparison override arm
youknowone Sep 3, 2026
94404eb
jit-trace: admit IntOrFloat lists to the generated len shortcut
youknowone Sep 3, 2026
63c3b2f
object: state what makes the lock-free list length sound, and make th…
youknowone Sep 3, 2026
eee6185
jit-trace, majit-translate: one concrete-operand read, a guard_class …
youknowone Sep 3, 2026
af801ae
docs: re-derive the §3.8 inventory and the fold-row count gate-triage…
youknowone Sep 3, 2026
4bcfb9f
jit-trace: say why a not_in_trace callee survives a rolled-back walk
youknowone Sep 3, 2026
211fbe4
object: rustfmt the bytes and ascii array bodies
youknowone Sep 3, 2026
e7f4326
bench: re-record the builtin_len_descent jitstats for the added mixed…
youknowone Sep 3, 2026
385bd04
interpreter, majit-translate: spell the int overflow promotion as one…
youknowone Sep 3, 2026
64d67d5
pyre-object: delete the jit_bigint_{add,sub,mul}_int_int seams
youknowone Sep 3, 2026
ee4e363
majit-translate: name bigint_and in the RBigInt-identity assertion
youknowone Sep 3, 2026
1c265ac
interpreter: stack-check the object.__ne__ gateway
youknowone Sep 3, 2026
77d8337
majit-trace: reach the elidable early return for a call-pure residual
youknowone Sep 4, 2026
f4d725f
interpreter, jit: enforce recursion limits on portal guard exits
youknowone Sep 4, 2026
2143904
ci: fit generated-descent gates to cross-host runs
youknowone Sep 4, 2026
e17f0e6
docs: poll builds and CI at five-minute intervals
youknowone Sep 4, 2026
6901452
jit: keep pending residual results out of async forcing snapshots
youknowone Sep 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,13 @@ comment at the site citing both sides.
- Confirm the worktree (`git rev-parse --show-toplevel`) before editing and
before staging — dozens of sibling worktrees share one `.git`.

## Build and CI polling

- Never check a running local build or CI job more often than once every five
minutes. While it runs, continue useful independent work when any is
available; otherwise wait for the five-minute boundary. Do not spend tokens
on repetitive status-only updates.

## Before committing

- `cargo test --all --no-default-features --features dynasm`. Both halves matter.
Expand Down
88 changes: 78 additions & 10 deletions majit/majit-backend-dynasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,38 @@ fn handle_fail_propagate_exception(frame_ptr: *mut jitframe::JitFrame) -> i64 {
value
}

/// Read a guard's logical fail arguments from its physical JITFRAME slots.
///
/// `llsupport/assembler.py::store_info_on_descr` writes `0xFFFF` for a
/// resume-data hole, and `ResumeDataDirectReader.decode_ref` (plus the other
/// typed decoders) asks the CPU for a physical slot only in its TAGBOX arm.
/// TAGCONST/TAGVIRTUAL values are reconstructed from resume metadata instead.
/// Majit's [`guard::decode_rd_loc_slot`] represents the sentinel as `None`;
/// use zero as the inert carrier, exactly as
/// `DynasmBackend::execute_token_ints_raw` does. Only a synthetic
/// descriptor with no `rd_locs` entry at all retains the historical
/// identity-slot fallback used by backend tests.
unsafe fn guard_fail_values(
descr: &dyn majit_ir::FailDescr,
frame_ptr: *mut jitframe::JitFrame,
) -> Vec<i64> {
let rd_locs_len = descr.rd_locs().len();
descr
.fail_arg_types()
.iter()
.enumerate()
.map(|(i, _)| {
if i < rd_locs_len {
guard::decode_rd_loc_slot(descr, i)
.map(|slot| unsafe { llmodel::get_int_value_direct(frame_ptr, slot) as i64 })
.unwrap_or(0)
} else {
unsafe { llmodel::get_int_value_direct(frame_ptr, i) as i64 }
}
})
.collect()
}

/// compile.py `AbstractResumeGuardDescr.handle_fail`.
///
/// Upstream:
Expand Down Expand Up @@ -682,16 +714,12 @@ fn handle_fail_resume_guard(
) -> i64 {
let trace_id = descr.trace_id();
let fail_index = descr.fail_index_per_trace();
let n_fail_args = descr.fail_arg_types().len();
let mut raw_values: Vec<i64> = Vec::with_capacity(n_fail_args);
for i in 0..n_fail_args {
// PyPy `llmodel.py _decode_pos` parity: read the slot
// from `descr.rd_locs[i]`. Synthetic descrs without `rd_locs`
// fall back to identity slot indexing — same shape as the
// pre-Slice-MM table-miss path.
let slot = guard::decode_rd_loc_slot(descr, i).unwrap_or(i);
raw_values.push(unsafe { llmodel::get_int_value_direct(frame_ptr, slot) as i64 });
}
// PyPy `llsupport/assembler.py::store_info_on_descr` plus
// `ResumeDataDirectReader.decode_ref` parity: a 0xFFFF entry is a
// resume-data hole, not permission to read the same-numbered physical
// slot. The old fallback turned an unrelated or uninitialised JITFRAME
// word into a Ref and registered it as a GC root during bridge recovery.
let mut raw_values = unsafe { guard_fail_values(descr, frame_ptr) };

let guard_value_operand = majit_backend::guard_value_counter_slot(descr)
.map(|slot| unsafe { llmodel::get_int_value_direct(frame_ptr, slot) as i64 });
Expand Down Expand Up @@ -918,6 +946,46 @@ mod tests {
ptr
}

#[test]
fn test_guard_fail_values_do_not_read_resume_holes() {
let descr = majit_backend::make_resume_guard_descr_typed(vec![Type::Ref, Type::Int]);
let fail_descr = descr.as_fail_descr().expect("resume guard descr");
fail_descr.set_rd_locs(vec![0xFFFF, 2]);

// Slot 0 deliberately contains a pointer-shaped poison. The first
// logical failarg is a resume-data hole, so recovery must not publish
// or root this physical word as a Ref. The mapped second argument is
// still read from its encoded physical slot.
let jf = unsafe { alloc_test_jitframe(0, &[0x1234_5678, 11, 77]) };
let values = unsafe { guard_fail_values(fail_descr, jf) };
assert_eq!(values, vec![0, 77]);

unsafe { libc::free(jf as *mut std::ffi::c_void) };
}

#[test]
fn test_guard_fail_values_all_holes_need_no_frame_slots() {
let descr = majit_backend::make_resume_guard_descr_typed(vec![Type::Ref; 3]);
let fail_descr = descr.as_fail_descr().expect("resume guard descr");
fail_descr.set_rd_locs(vec![0xFFFF; 3]);

// No physical slot exists for these logical positions. In particular,
// recovery must not perform even a speculative read of a hole.
let jf = unsafe { alloc_test_jitframe(0, &[]) };
assert_eq!(unsafe { guard_fail_values(fail_descr, jf) }, vec![0; 3]);
unsafe { libc::free(jf as *mut std::ffi::c_void) };
}

#[test]
fn test_guard_fail_values_synthetic_descr_keeps_identity_slots() {
let descr = majit_backend::make_resume_guard_descr_typed(vec![Type::Int; 2]);
let fail_descr = descr.as_fail_descr().expect("resume guard descr");
assert!(fail_descr.rd_locs().is_empty());
let jf = unsafe { alloc_test_jitframe(0, &[11, 77]) };
assert_eq!(unsafe { guard_fail_values(fail_descr, jf) }, vec![11, 77]);
unsafe { libc::free(jf as *mut std::ffi::c_void) };
}

// ── Bug 1 regression: unresolved target must not dereference result as pointer ──
// The old code let the helper return value flow into `mov rdx, rax; mov rcx, [rdx]`
// which dereferenced an integer as a pointer. This test verifies the trampoline
Expand Down
20 changes: 20 additions & 0 deletions majit/majit-metainterp/src/blackhole.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10470,6 +10470,11 @@ pub fn build_inline_call_only_bh_builder() -> BlackholeInterpBuilder {
"new_with_vtable/d>r",
majit_translate::insns::BC_NEW_WITH_VTABLE,
),
// The header read `jtransform.rs rewrite_op_getfield` replaces,
// in the ref bank (`OpKind::GuardClass`: one ref register in, the
// class out) — emitted once a descended helper reads `ob_type`
// through a graph the codewriter looks inside.
("guard_class/r>r", majit_translate::insns::BC_GUARD_CLASS_R),
] {
insns.insert(key.to_string(), byte);
}
Expand Down Expand Up @@ -10930,6 +10935,9 @@ pub fn wire_bhimpl_handlers(builder: &mut BlackholeInterpBuilder) {
// Canonical key is `guard_class/r>i`; the previous `/ri` shape was
// a pyre-invented bigram that omitted the `>i` return marker.
builder.wire_handler("guard_class/r>i", handler_guard_class);
// The same op with its result in the ref bank — the bank of the header
// read `jtransform.rs rewrite_op_getfield` replaced (`BC_GUARD_CLASS_R`).
builder.wire_handler("guard_class/r>r", handler_guard_class_r);
// RPython `rpython/jit/metainterp/blackhole.py:1537-1539`:
// @arguments("r", "d", "d")
// def bhimpl_record_quasiimmut_field(struct, fielddescr, mutatefielddescr):
Expand Down Expand Up @@ -11261,6 +11269,18 @@ fn handler_guard_class(
bh.registers_i[code[p + 1] as usize] = typeptr;
Ok(p + 2)
}
/// [`handler_guard_class`] with the class delivered to the ref bank —
/// `guard_class/r>r`, for a header read the graph typed as a GC ref.
fn handler_guard_class_r(
bh: &mut BlackholeInterpreter,
code: &[u8],
p: usize,
) -> Result<usize, DispatchError> {
let cpu = bh.cpu();
let typeptr = cpu.bh_classof(bh.registers_r[code[p] as usize]);
bh.registers_r[code[p + 1] as usize] = typeptr;
Ok(p + 2)
}
/// Safe fallback for the obsolete pyre-only named vtable lookup.
///
/// PyPy's `ClassRepr.getclsfield` emits an ordinary field read; it never tries
Expand Down
38 changes: 37 additions & 1 deletion majit/majit-metainterp/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4607,8 +4607,26 @@
fn set_adr_jump_offset(&self, offset: usize) {
unsafe { *self.adr_jump_offset.get() = offset };
}
/// `store_info_on_descr` writes `guardtok.faildescr.rd_locs`
/// (`llsupport/assembler.py:279`), so a backend that emits machine code for

Check warning on line 4611 in majit/majit-metainterp/src/compile.rs

View workflow job for this annotation

GitHub Actions / pre-commit

Cite upstream by symbol

`llsupport/assembler.py:279` names a line number. Drop the `:LINE` and name the symbol, or add `allow-line-citation` to record that the number was deliberate.
/// this guard leaves its own physical positions here. The frontend's
/// identity-with-holes layout is resume data instead, and
/// `_copy_resume_data_from` never runs `store_final_boxes_in_guard` on a
/// copied descr, so it is written only on the donor. Read through to the
/// donor when no backend has written one, the same way `fail_arg_types`,
/// `rd_numb` and `rd_consts` chase `get_resumestorage(): return prev`
/// (`compile.py:847-850`). Without it the hole mask is empty on every

Check warning on line 4618 in majit/majit-metainterp/src/compile.rs

View workflow job for this annotation

GitHub Actions / pre-commit

Cite upstream by symbol

`compile.py:847` names a line number. Drop the `:LINE` and name the symbol, or add `allow-line-citation` to record that the number was deliberate.
/// backend that keeps failargs in their logical slots, and both consumers
/// silently take their unmasked branch.
fn rd_locs(&self) -> &[u16] {
unsafe { &*self.rd_locs.get() }
let own = unsafe { &*self.rd_locs.get() };
if !own.is_empty() {
return own;
}
self.prev()
.as_fail_descr()
.map(|fd| fd.rd_locs())
.unwrap_or(&[])
}
fn set_rd_locs(&self, locs: Vec<u16>) {
unsafe { *self.rd_locs.get() = locs };
Expand Down Expand Up @@ -5157,6 +5175,24 @@
my_fd.set_rd_consts_arc(donor_fd.rd_consts_arc());
my_fd.set_rd_virtuals_arc(donor_fd.rd_virtuals_arc());
my_fd.set_rd_pendingfields_arc(donor_fd.rd_pendingfields_arc());
// `AbstractFailDescr.rd_locs` is a backend slot upstream, written
// only by `store_info_on_descr`, which every upstream backend runs —
// so `copy_all_attributes_from` has nothing to carry there. Pyre also
// seeds it in `store_final_boxes_in_guard` with the resume numbering's
// identity-with-holes layout, which cranelift and wasm keep because
// they leave failargs in their logical slots. That seed belongs to
// the payload copied just above: the known-class bitfield inside
// `rd_numb` holds one bit per non-hole Ref livebox, and
// `deserialize_optimizer_knowledge` can only find those bits by
// masking its fail args with the same holes.
//
// Every caller hands a descr the optimizer minted for a guard put in
// place of an already-emitted one — `replace_guard_op`, the
// GUARD_VALUE and GUARD_CLASS strengthening arms of
// `optimize_guard_value` / `replace_old_guard_with_guard_class`, and
// the vectorizer's `inhert_attributes` — and such a descr never
// reaches `store_final_boxes_in_guard`, so it has no layout of its own.
my_fd.set_rd_locs(donor_fd.rd_locs().to_vec());
// compile.py — chain.clone() preserves the donor's
// (already-flattened) accumulator chain on self, identity-stable.
let donor_chain = donor_fd.vector_info();
Expand Down
58 changes: 58 additions & 0 deletions majit/majit-metainterp/src/jitcode/assembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3542,6 +3542,64 @@ impl JitCodeBuilder {
self.inline_call_r_v(sub_jitcode_idx, &[], None);
}

/// Emit RPython's canonical `inline_call_{r,ir,irf}_{i,r,f,v}`
/// bytecode shape.
///
/// This is deliberately separate from [`Self::inline_call_r_r`] and its
/// siblings. Those methods predate the codewriter port and emit pyre's
/// opaque `inline_call_nested_ext/P` payload, including explicit callee
/// destination registers. `jtransform.py handle_regular_call` instead
/// emits a JitCode descr followed by kind-separated varlists; the callee's
/// `MIFrame.setup_call` places each list at the start of its matching
/// register bank. `pyre-jit`'s SSA assembler uses this method for that
/// orthodox stream.
pub fn canonical_inline_call(
&mut self,
key: &'static str,
sub_jitcode_idx: u16,
args_i: Option<&[u16]>,
args_r: Option<&[u16]>,
args_f: Option<&[u16]>,
result: Option<(JitArgKind, u16)>,
) {
self.write_insn(key);
self.push_u16(sub_jitcode_idx);
if let Some(args) = args_i {
self.push_canonical_inline_varlist(args, JitArgKind::Int);
}
if let Some(args) = args_r {
self.push_canonical_inline_varlist(args, JitArgKind::Ref);
}
if let Some(args) = args_f {
self.push_canonical_inline_varlist(args, JitArgKind::Float);
}
if let Some((kind, dst)) = result {
match kind {
JitArgKind::Int => self.touch_reg(dst),
JitArgKind::Ref => self.touch_ref_reg(dst),
JitArgKind::Float => self.touch_float_reg(dst),
}
self.push_reg_u8(dst, "canonical inline_call return");
}
}

fn push_canonical_inline_varlist(&mut self, args: &[u16], kind: JitArgKind) {
if args.len() > u8::MAX as usize {
self.encoding_overflow = true;
self.push_u8(0);
return;
}
self.push_u8(args.len() as u8);
for &src in args {
match kind {
JitArgKind::Int => self.touch_reg(src),
JitArgKind::Ref => self.touch_ref_reg(src),
JitArgKind::Float => self.touch_float_reg(src),
}
self.push_reg_u8(src, "canonical inline_call argument");
}
}

pub fn inline_call_r_i(
&mut self,
sub_jitcode_idx: u16,
Expand Down
16 changes: 15 additions & 1 deletion majit/majit-metainterp/src/jitcode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,11 @@ pub struct JitCode {
/// `JitCodeBuilder` populates this during runtime per-CodeObject
/// emission.
pub exec: JitCodeExecState,
/// Which descriptor namespace bytecode `d`/`j` operands address.
/// `from_canonical` wraps a source-translator JitCode whose operands use
/// the process-wide codewriter table; `JitCodeBuilder` creates a runtime
/// Python-code body whose operands use `exec.descrs`.
uses_global_descr_pool: bool,
/// Reachable symbolic residual targets, computed after the runtime wrapper
/// has received its final function-address bindings and descriptor pool.
///
Expand Down Expand Up @@ -633,6 +638,7 @@ impl JitCode {
Self {
core: majit_translate::jitcode::JitCode::new(name),
exec: JitCodeExecState::default(),
uses_global_descr_pool: false,
reachable_symbolic_residuals: std::sync::OnceLock::new(),
}
}
Expand Down Expand Up @@ -662,10 +668,15 @@ impl JitCode {
jit_merge_point_offset,
..JitCodeExecState::default()
},
uses_global_descr_pool: true,
reachable_symbolic_residuals: std::sync::OnceLock::new(),
}
}

pub fn uses_global_descr_pool(&self) -> bool {
self.uses_global_descr_pool
}

/// Borrow the canonical core (e.g. for serialization that
/// re-serializes only the canonical fields).
pub fn core(&self) -> &majit_translate::jitcode::JitCode {
Expand All @@ -683,7 +694,9 @@ impl JitCode {

impl Default for JitCode {
fn default() -> Self {
Self::from_canonical(majit_translate::jitcode::JitCode::default())
let mut jitcode = Self::from_canonical(majit_translate::jitcode::JitCode::default());
jitcode.uses_global_descr_pool = false;
jitcode
}
}

Expand All @@ -692,6 +705,7 @@ impl Clone for JitCode {
Self {
core: self.core.clone(),
exec: self.exec.clone(),
uses_global_descr_pool: self.uses_global_descr_pool,
reachable_symbolic_residuals: self.reachable_symbolic_residuals.clone(),
}
}
Expand Down
30 changes: 26 additions & 4 deletions majit/majit-metainterp/src/optimizeopt/virtualize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1173,10 +1173,21 @@ impl OptVirtualize {
field_descr.offset(),
);
}
let field_val = match &info {
// virtualize.py `optimize_GETFIELD_GC_I`:
// fieldop = opinfo.getfield(op.getdescr())
// self.make_equal_to(op, fieldop)
// `info.py AbstractStructPtrInfo.getfield` returns `self._fields[index]`
// — the stored Box itself, so `make_equal_to` forwards the read onto
// that box and a later `get_box_replacement` walks the box's own
// `_forwarded` chain. A virtualizable slot holds an `Operand`, so hand
// that operand over directly. Reading the slot back by POSITION
// instead re-keys it into whichever inputarg namespace the current
// pass owns: `VirtualizableTracker::init` seeds the slots from the
// seeding pass's inputargs, and an unrolled body owns a disjoint
// range, so the position resolves there to a fresh host that carries
// none of the facts the seeded box holds.
let field_box: Option<Operand> = match &info {
_ if !slot_resolvable => None,
PtrInfo::Virtual(vinfo) => get_field(&vinfo.fields, field_idx),
PtrInfo::VirtualStruct(vinfo) => get_field(&vinfo.fields, field_idx),
PtrInfo::Virtualizable(vstate) => vstate
.fields
.iter()
Expand All @@ -1190,7 +1201,18 @@ impl OptVirtualize {
op.result_type(),
)
})
.map(|(_, b)| b.to_opref()),
.map(|(_, b)| b.get_box_replacement(false)),
_ => None,
};
if let Some(b_val) = field_box {
let b_old = Operand::from_bound_op(op_rc);
ctx.make_equal_to(&b_old, &b_val);
return OptimizationResult::Remove;
}
let field_val = match &info {
_ if !slot_resolvable => None,
PtrInfo::Virtual(vinfo) => get_field(&vinfo.fields, field_idx),
PtrInfo::VirtualStruct(vinfo) => get_field(&vinfo.fields, field_idx),
_ => None,
};
if let Some(val_ref) = field_val {
Expand Down
Loading
Loading