llvm / llvm/eudsl

[eudsl-llvmpy] MIR: run codegen to a named pass and retain the MIR for inspection (e.g. post-RA)

Open
#653 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
79
Forks
14
Avg merge
11h 43m
Merged PRs (30d)
72

Description

Follow-up to #648. Today MirModule.emit_object(scheduler=, regalloc=) runs the entire back half of codegen (-start-after=finalize-isel through object emission) and returns the object bytes. There is no way to run codegen up to a chosen pass and get the intermediate MachineFunctions back for inspection — so you can't, from Python, observe the MIR after a specific stage (e.g. after register allocation / virtregrewriter, where the function is no longer in SSA form and has no virtual registers), which is exactly what you want when debugging a custom RegAllocBase/MachineSchedStrategy.

Proposal

Add a "run to a named pass and retain the MIR" entry point, e.g.:

mmi2 = mmi.run_to("virtregrewriter", regalloc="my-alloc")   # or stop_before=
print(mmi2.to_mir())        # inspect post-RA, non-SSA MIR

Semantics: run the codegen pipeline from finalize-isel up to (and including / before) the named pass, then retain the resulting MachineFunctions in the returned MirModule for inspection via the existing to_mir() / machine_function() surface — rather than continuing to emission and freeing them.

Mechanism (mirrors what already exists)

  • LLVM already provides process-global -stop-after=<pass> / -stop-before=<pass> cl::opts, analogous to the -start-after=finalize-isel that emit_object already sets and restores (see the Restore RAII in MirModule::emitObject, src/MIR/Machine.cpp). Set/restore stop-after/stop-before the same way (GIL-serialized; no lock).
  • Retaining MIR after stopping is the same trick create_machine_function uses for the ISel boundary: don't append the object-emission passes (addPassesToEmitFile would add FreeMachineFunctionPass and free the functions), keep the MachineModuleInfoWrapperPass alive in the returned wrapper's owned state (the BuildOwned/EmittedOwned variant in Machine.cpp), so the MachineFunctions stay queryable — exactly like the current build/inspect path.
  • The scheduler/regalloc selection (-misched, RegisterRegAlloc::setDefault) and the pendingCodegenError stash/re-raise machinery should compose unchanged, so you can run a custom allocator and stop right after it.

Scope

  • New MirModule.run_to(pass_name, *, stop_before=False, scheduler=None, regalloc=None) (name TBD) that runs finalize-isel -> <pass> and returns a MirModule whose MachineFunctions are retained.
  • Validate the pass name against the registered pass pipeline; an unknown name raises a catchable Python error (as emit_object does for unknown scheduler/regalloc names).
  • Reuse the existing state machine so a module can be inspected and then still emit_object'd (or make the retain-vs-emit interaction explicit and one-shot, matching the current BuildOwned/EmittedOwned handling).
  • Tests: run to virtregrewriter with a custom allocator, assert the returned MIR has no virtual registers / uses physregs (i.e. is post-RA, non-SSA); run to a pre-RA pass and assert vregs are still present; unknown-pass-name raises. Keep the 100% C++ coverage gate.

Notes

  • This pairs naturally with the existing to_mir() and the -start-after=finalize-isel plumbing; it's mostly option set/restore + retaining the MMI wrapper, not new pipeline construction.
  • Useful well beyond regalloc (inspecting scheduling results, prologue/epilogue insertion, etc.).

cc #600

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/MIR/Machine.cpp by tracing MirModule::emitObject, its Restore handling for codegen options, and the BuildOwned/EmittedOwned state used by create_machine_function. Compare how the existing to_mir() and machine_function() surfaces retain MachineFunction data. Done means a named-pass run preserves inspectable MIR, handles pre- and post-RA cases, rejects unknown passes, and keeps the existing coverage gate passing.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, python
Domain
backend-api-design, compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.