Skip to content

Align rtyper and metainterp public symbol parity with RPython - #326

Merged
youknowone merged 30 commits into
mainfrom
rename
Jul 3, 2026
Merged

youknowone merged 30 commits into
mainfrom
rename

Conversation

@youknowone

Copy link
Copy Markdown
Owner

Summary

Self-review

  • I fully resolved all reasonable code review comments from Codex and CodeRabbit.
    • Auto-review section 1 is clear. This check is mandatory.
    • Auto-review section 2 is clear. If this is not checked, please add a comment explaining why.
  • I did not use AI to write the code of this patch.
    • If this is not checked, commits must include Assisted-by

@coderabbitai

coderabbitai Bot commented Jul 2, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

@youknowone, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 23 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0bf51e21-0b99-4a33-bca8-05ccc57f7973

📥 Commits

Reviewing files that changed from the base of the PR and between d110c4d and 495ec10.

📒 Files selected for processing (45)
  • majit/majit-backend-dynasm/src/aarch64/assembler.rs
  • majit/majit-backend/src/resume_value.rs
  • majit/majit-gc/src/rewrite.rs
  • majit/majit-macros/src/jit_interp/codegen_trace.rs
  • majit/majit-macros/src/jit_interp/mod.rs
  • majit/majit-metainterp/src/compile.rs
  • majit/majit-metainterp/src/jitprof.rs
  • majit/majit-metainterp/src/lib.rs
  • majit/majit-metainterp/src/optimizeopt/autogenintrules.rs
  • majit/majit-metainterp/src/optimizeopt/dependency.rs
  • majit/majit-metainterp/src/optimizeopt/intbounds.rs
  • majit/majit-metainterp/src/optimizeopt/vector.rs
  • majit/majit-metainterp/src/ruleopt/mod.rs
  • majit/majit-trace/src/counter.rs
  • majit/majit-translate/src/annotator/builtin.rs
  • majit/majit-translate/src/flowspace/argument.rs
  • majit/majit-translate/src/translator/driver.rs
  • majit/majit-translate/src/translator/rtyper/annlowlevel.rs
  • majit/majit-translate/src/translator/rtyper/exceptiondata.rs
  • majit/majit-translate/src/translator/rtyper/lltypesystem/ll_str.rs
  • majit/majit-translate/src/translator/rtyper/lltypesystem/llmemory.rs
  • majit/majit-translate/src/translator/rtyper/lltypesystem/module/ll_math.rs
  • majit/majit-translate/src/translator/rtyper/lltypesystem/rffi.rs
  • majit/majit-translate/src/translator/rtyper/lltypesystem/rgcref.rs
  • majit/majit-translate/src/translator/rtyper/lltypesystem/rordereddict.rs
  • majit/majit-translate/src/translator/rtyper/lltypesystem/rstr.rs
  • majit/majit-translate/src/translator/rtyper/normalizecalls.rs
  • majit/majit-translate/src/translator/rtyper/rbuiltin.rs
  • majit/majit-translate/src/translator/rtyper/rclass.rs
  • majit/majit-translate/src/translator/rtyper/rlist.rs
  • majit/majit-translate/src/translator/rtyper/rmodel.rs
  • majit/majit-translate/src/translator/rtyper/rpbc.rs
  • majit/majit-translate/src/translator/rtyper/rrange.rs
  • majit/majit-translate/src/translator/rtyper/rstr.rs
  • majit/majit-translate/src/translator/rtyper/rtuple.rs
  • majit/majit-translate/src/translator/rtyper/rtyper.rs
  • majit/majit-translate/src/translator/translator.rs
  • majit/majit-translate/src/translator/unsimplify.rs
  • pyre/pyre-interpreter/src/lib.rs
  • pyre/pyre-interpreter/src/module/signal/signalstate.rs
  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs
  • pyre/pyre-jit-trace/src/lib.rs
  • pyre/pyre-jit/src/lib.rs
  • pyre/pyre-object/src/dictmultiobject.rs
  • scripts/check-rpython-module-parity.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rename

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@youknowone youknowone changed the title Rename Align rtyper and metainterp public symbol parity with RPython Jul 2, 2026
@youknowone
youknowone marked this pull request as ready for review July 3, 2026 00:54
@github-actions

github-actions Bot commented Jul 3, 2026 •

Copy link
Copy Markdown

🤖 Codex parity review

Static analysis of this diff vs the local RPython/PyPy sources (commit e3d5a32).

1. Regressions to PyPy parity introduced by this patch

  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs:7479 ↔ rpython/jit/metainterp/blackhole.py:1711: Rust now stores one in-flight FOR_ITER item as RefCell<Option<(item, body_pc)>>, and pyre/pyre-jit-trace/src/jitcode_dispatch.rs:7632 says a new consume “overwrites any prior stash”. PyPy resumes by copying the whole MIFrame register state (_copy_data_from_miframe copies all int/ref/float registers at blackhole.py:1711-1730), so nested or multiple live iterator continuations are not collapsed into one global slot. This regresses the prior stack-shaped handling in upstream/main.

  • pyre/pyre-jit-trace/src/trace_opcode.rs:7192 ↔ pypy/module/__builtin__/functional.py:550: Rust calls range_iter_continues(concrete_iter) for every non-via_space_next iterator, which includes int range, long range, and fast sequence iterators, but then unconditionally reads W_IntRangeIterator.step/current at trace_opcode.rs:7193-7198. PyPy separates int ranges (W_IntRangeIterator, functional.py:550), long ranges (W_LongRangeIterator, functional.py:551-552), and sequence iterators (W_FastListIterObject.descr_next, pypy/objspace/std/iterobject.py:87-99). This is not layout-parity and can misread sequence/long-range iterators.

2. Other mismatches introduced by this patch

None.

3. Pre-existing mismatches (already present before this patch)

  • majit/majit-metainterp/src/optimizeopt/dependency.rs:980 ↔ rpython/jit/metainterp/optimizeopt/dependency.py:274: Rust has_dependency() only checks direct deps membership, despite its comment saying “direct or transitive”; PyPy Node.independent() walks forward and backward dependency paths (dependency.py:280-300). This can admit packs that PyPy rejects due to transitive dependence.

  • majit/majit-metainterp/src/optimizeopt/vector.rs:543 ↔ rpython/jit/metainterp/optimizeopt/vector.py:747: Rust blocks backward INT_SIGNEXT packing when packed.opcode == IntSignext; PyPy checks inquestion.getopnum() == rop.INT_SIGNEXT. The tested operation is different.

  • majit/majit-metainterp/src/optimizeopt/dependency.rs:421 ↔ rpython/jit/metainterp/optimizeopt/dependency.py:556: Rust DependencyGraph::build(ops) only builds nodes from the passed operation slice and leaves invariant_vars empty; PyPy constructs label, body nodes, jump, then calls update_invariant_vars(label) (dependency.py:558-572). Loop-invariant argument tracking is therefore not structurally complete.

4. Structural adaptations

  • majit/majit-metainterp/src/optimizeopt/dependency.rs:443 ↔ rpython/jit/metainterp/optimizeopt/dependency.py:577: Rust stores imaginary nodes inside DependencyGraph.nodes by index; PyPy stores object references and keeps imaginary nodes in self.inodes. This is an index-addressed Rust graph adaptation, not by itself a parity bug.

  • majit/majit-translate/src/translator/rtyper/rrange.rs:276 ↔ rpython/rtyper/rrange.py:140: Rust folds the Void type argument to ll_newrange into the helper builder and direct-calls (start, stop); PyPy passes c_rng as a Void constant to ll_newrange(RANGE, start, stop). Same low-level helper shape after specialization, but not 1:1 source shape.

  • pyre/pyre-interpreter/src/runtime_ops.rs:1257 ↔ pypy/objspace/std/iterobject.py:70: Rust’s fast FOR_ITER helper returns value-or-null for range/fast-sequence exhaustion; PyPy iterator descr_next raises StopIteration. This is a compiled-trace guard adaptation and is acceptable only when all callers preserve the same observable exhaustion behavior.

youknowone added 24 commits July 3, 2026 12:46
Implement rtype_builtin_range / rtype_builtin_xrange in rrange.rs and
register both in rbuiltin.rs install_default_typers.

- nb_args 1/2/3 arms select vstart/vstop/vstep; const step of zero raises
  TyperError.
- AbstractRangeRepr result: step != 0 -> gendirectcall ll_newrange,
  step == 0 -> ll_newrangest. Non-range result (real list) returns a
  deferred error citing the unported ll_newlist / ll_setitem_fast.
- build_ll_newrange_helper_graph: single block, malloc(RANGE.TO) flavor=gc
  + setfield start/stop.
- build_ll_newrangest_helper_graph: start block guards int_eq(step, 0)
  raising ValueError, build block mallocs RANGEST + setfield start/stop/step.
- emit_gc_malloc / emit_void_setfield graph-op helpers.

Assisted-by: Claude
Add RPythonTyper.cache_dummy_values and implement the lazy immortal
placeholder allocation for both dummy-value builders (rmodel.py:452-464,
rgcref.py:93-104):

- DummyValueBuilder::ll_dummy_value mallocs an immortal Struct/Array
  placeholder (n=1 for _is_varsize) keyed by TYPE in cache_dummy_values;
  the typer is threaded in at call time since the builder stores only
  rtyper_id for identity.
- DummyValueBuilderGCRef::ll_dummy_value casts the base-instance dummy
  (getinstancerepr(None) -> DummyValueBuilder over TYPE.TO) to GCREF and
  memoises it under GCREF; return type changed from Constant to
  LowLevelValue to match the base builder.
- The producer side (Repr.get_ll_dummyval_obj) stays deferred: its only
  consumers are the unported dict/ordereddict ENTRIES.dummy_obj fields.

Sharpen the GCRefRepr.ll_str deferral note to cite the concrete blocker:
ll_str is carried upstream by VoidRepr/TupleRepr (gen_str_function)/
string reprs, none of which pyre exposes as an ll_str surface, so the
hasattr guard is vacuously false.

Assisted-by: Claude
Port the profiler class surface from jitprof.py:16-50:

- BaseProfiler (jitprof.py:16-17) marker struct.
- EmptyProfiler (jitprof.py:19-50) no-op profiler with the full
  start/finish/tracing/backend/count/count_ops/get_counter/get_times
  surface; initialized = true.
- Profiler type alias exposing the upstream public name for JitProfiler.
- BrokenProfilerData error marker.

Classify JitProfiler / JitProfilerSnapshot / ProfilerEventGuard as
intentional Rust-side extras in check-rpython-module-parity.py.

Assisted-by: Claude
The bare ll_tupleiter(&LowLevelType, Hlvalue) / ll_tuplenext(Hlvalue)
functions returned rtuple_deferred and had no production callers — a
value-returning function cannot emit the malloc/setfield operations these
helpers require. rtuple.py:399-411 is faithfully ported by the
graph-builder forms build_ll_tupleiter_helper_graph (malloc ITERPTR.TO +
setfield 'tuple') and build_ll_tuplenext_helper_graph (getfield/ptr_nonzero
/StopIteration/null-store/return item0), which are the ones newiter /
rtype_next actually gendirectcall. Move the upstream citations onto those
builders and remove the dead stubs plus their assert-deferred test.

Assisted-by: Claude
Eliminate the separate-carrier deviation from dependency.py's Path/Node
model: Node.op becomes Option<Op> (None for imaginary nodes), and the
ImaginaryNode struct plus the PathNode enum are deleted. Path now stores
node indices uniformly (Vec<usize>), including imaginary nodes appended
to DependencyGraph.nodes via add_imaginary_node.

- Node::new_imaginary(label) builds an op=None node carrying a dotlabel
  and a fake index; is_imaginary() == op.is_none(); getoperation()
  returns Option<&Op>; op() unwraps for real-node call sites.
- Path::second/last/first/last_but_one return the actual node index at
  each position (real or imaginary), matching dependency.py:56-72.
- set_schedule_priority sets priority on every segment including
  imaginary ones (dependency.py:100-102), where the old split skipped
  them; is_always_pure skips imaginary segments (dependency.py:84-86).
- Cascade node.op field reads to op()/getoperation() across dependency.rs
  and vector.rs.

Assisted-by: Claude
Add per-rule fire counters to the autogen int-rule mixin, mirroring the
generated `_rule_names_<op>` / `_rule_fired_<op>` class attributes and
their `_all_rules_fired` registration (autogenintrules.py:18-22):

- `RULE_NAMES_*` / `RULE_FIRED_*` static tables plus `all_rules_fired()`
  registry, and a `fire(counts, index)` helper for the in-line bumps.
- Bump the matching counter at each of the 136 `optimize_INT_*` rewrite
  sites (`self._rule_fired_<op>[i] += 1`).
- Implement `print_rewrite_rule_statistics` (intbounds.py:862-870): dump
  the counts in a `jit-intbounds-stats` debug section.
- Registry-shape and debug-dump smoke tests.

Assisted-by: Claude
Port the dependency-graph path/edge primitives that
`analyse_index_calculations` needs (dependency.py):

- Node::provides()/depends() — forward/backward edge lists (246-253).
- Dependency::target_node()/origin_node()/is_failarg() (429-461).
- DependencyGraph::iterate_paths() — index-based port of the
  Node.iterate_paths generator (303-352): worklist path enumeration with
  optional destination, direction, path_max_len cap, and blacklist.
- Diamond-DAG unit test covering forward/backward/max-len enumeration.

Narrow the vector.rs analyse_index_calculations deferral note: of the
four cited blockers, imaginary_node (via add_imaginary_node),
iterate_paths, and is_always_pure are now ported; only edge_to /
remove_edge_to graph-mutation remain, plus #175 for mark_guard failargs.

Assisted-by: Claude
Port analyse_index_calculations (vector.py:529-583): build the loop
dependency graph, add an imaginary early-exit node, and reroute every
valid guard's edges through it so guards fail early, returning the
rewired graph as the reschedule basis.

Add the graph-mutation primitives it needs to dependency.rs:
- edge_to/remove_edge_to (index-based Node.edge_to/remove_edge_to),
  keeping the deps/users side-vectors the scheduler reads consistent.
- Dependency::new regains the failarg argument (dependency.py:415-421);
  add_edge was dropping it on newly created edges.
- schedule_operations excludes imaginary nodes from its output and
  indexes in_degree by node position rather than the Node.idx sentinel.

mark_guard's failargs body stays deferred to #175.

Assisted-by: Claude
mark_guard attaches a CompileLoopVersionDescr to GUARD_TRUE/GUARD_FALSE,
copying resume attributes from an existing descr, and sets the loop label
args as failargs via getarglist_operand.

- compile.rs: add make_compile_loop_version_descr / _typed / _with_payload.
- dependency.rs: set_edge_failarg propagates a failarg downgrade to both
  the forward and back edge (dependency.py:457-458 dep.backward).
rtype_builtin_range list-result path lowers to ll_range2list through a
helper graph using ll_newlist and setitem.

- rlist: expose build_ll_newlist_helper_graph and item_lowleveltype.
build_ll_dictiter_helper_graph, ll_dict_lookup, and ll_dictiter.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 495ec10e82

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

};
guard_op.setdescr(descr);
}
guard_op.setfailargs(loop_.label.getarglist_operand());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restamp fail-arg types when changing guard failargs

When an index guard is marked as an early-exit guard, this overwrites its failargs with the loop label args but leaves both op.fail_arg_types and the newly attached CompileLoopVersionDescr carrying the old donor guard's type vector (or an empty vector when there was no donor). For early-exit guards whose original failargs differ from the label args, the backend/deadframe paths decode exits using descr.fail_arg_types(), so guard failures can save or read the wrong number/types of values. After replacing the failargs, also recompute and set the matching fail-arg types on the op and fail descr.

Useful? React with 👍 / 👎.

@youknowone
youknowone merged commit 37f5008 into main Jul 3, 2026
29 checks passed
@youknowone
youknowone deleted the rename branch July 3, 2026 06:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant