feat: experimental Rust MIR frontend - #1365
Open
greenhat wants to merge 9 commits into
Open
Conversation
Local2Reg records (store, stored value) pairs during its initial walk and then promotes locals in ascending index order. When a promoted load feeds a store to a later local, that promotion rewrites the store operand, and the recorded value for the later local becomes a dangling reference to an erased op. The pass then emitted uses of undefined values, and the verifier did not catch them. Read the stored value from the store op at promotion time, so the chains of slot-to-slot copies that a frontend emits under the slot strategy promote correctly. Add a regression test that builds the two-local copy chain, applies the pass, and checks the printed IR: the chain must collapse to the entry value, and no store, load, or dangling reference may remain. The test fails without the fix, because the verifier alone does not reject the bad IR.
The Rust MIR frontend links against rustc in-process through rustc_public, which needs the compiler rlibs that the rustc-dev component provides.
Add midenc-frontend-rust-mir, a frontend that reads Rust MIR in-process through rustc_public (the stable-MIR API) and builds HIR from it directly, without the WebAssembly path. The crate is an experiment to validate that a MIR frontend can remove the losses of the Wasm path. The translator lowers every local non-generic function with the slot strategy: one local slot per MIR local, stores for definitions, loads for uses, and no phi construction; the existing local2reg pass recovers SSA. The v1 surface covers integer and boolean types, wrapping addition, whole-local moves and copies, integer constants, and the return terminator; every other MIR construct produces an error that names the construct. Golden tests snapshot the raw HIR and the post-local2reg HIR of a u32 add function, and a negative test checks the error for an unsupported f64 parameter. The package is private and stays out of the release surface.
…erpreter The golden files show the shape of the translated IR, but they do not show that the IR runs. Close that gap with the HIR interpreter: evaluate the translated add function with the arguments 1 and 2 and assert the result 3, both for the raw slot form and for the SSA form that the local2reg pass produces. This proves that the slot strategy and the SSA recovery each give executable IR.
The interpreter tests already cover the raw slot form and the promoted SSA form of the translated function, and they assert behavior instead of the exact printed IR. The golden snapshots added churn on every printer or numbering change without more coverage, so remove them together with the expect-test dependency.
Document the durable facts a contributor must know to work on this crate: the development-environment changes (the rustc-dev toolchain component and the rustc_private nightly feature) and the two-part rust-analyzer setup (the rustc_private package metadata plus the editor-side rustc.source = "discover" setting), with VS Code and Neovim examples. Point to the README from the metadata comment in Cargo.toml, because the metadata alone looks sufficient but is not.
greenhat
marked this pull request as ready for review
August 31, 2026 09:50
Record the stages beyond the current smoke test: function bodies, in-crate calls, Felt and the SDK, pipeline integration, an execution proof, whole-program translation, and an honest data layout. The whole-program stage carries the driver decision point: keep the standalone driver with an own reachability walk, or adopt a codegen-backend dylib for the compiler's mono-item collector. The translator is unaffected in either case, which is why the decision can wait until that stage.
The release lint rejects private packages that inherit the workspace version: they are never published, so a version that tracks a release domain is misleading. Pin the crate at 0.1.0 like the other private packages.
CI could compile midenc-frontend-rust-mir but could not link its test binary: rust-lld did not find libLLVM. rustc passes only <sysroot>/lib/rustlib/<host>/lib to the linker, and the LLVM shared library lands there through the llvm-tools component; the copy that the rustc component ships in <sysroot>/lib is not on the search path. Local builds worked because llvm-tools happened to be installed outside the toolchain file.
greenhat
force-pushed
the
rust-mir-frontend
branch
from
August 31, 2026 10:10
2bdbed3 to
47c4f1f
Compare
Contributor
Miden examples benchmarkCandidate
SVG flamegraphs and compiled packages are attached to the workflow run. |
Contributor
Author
pub enum TerminatorKind {
Goto {
target: BasicBlockIdx,
},
SwitchInt {
discr: Operand,
targets: SwitchTargets,
},
Resume,
Abort,
Return,
Unreachable,
Drop {
place: Place,
target: BasicBlockIdx,
unwind: UnwindAction,
},
Call {
func: Operand,
args: Vec<Operand>,
destination: Place,
target: Option<BasicBlockIdx>,
unwind: UnwindAction,
},
Assert {
cond: Operand,
expected: bool,
msg: AssertMessage,
target: BasicBlockIdx,
unwind: UnwindAction,
},
InlineAsm {
template: String,
operands: Vec<InlineAsmOperand>,
options: String,
line_spans: String,
destination: Option<BasicBlockIdx>,
unwind: UnwindAction,
},
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The scope of this PR is deliberately small so it is easier to review: it translates a single
addfunction end to end and nothing more. It lays the bedrock — the crate layout, the driver, the translator skeleton, and the test approach — on which the next stages build (seefrontend/rust-mir/roadmap.md).What this adds
A new experimental crate
midenc-frontend-rust-mirthat reads Rust MIR and builds HIR from it directly, without the WebAssembly path. The frontend translates every local, non-generic function; every unsupported MIR construct is a hard error that names the construct. Tests evaluate the translated function with the HIR interpreter (add(1, 2) == 3), both in the raw form and after SSA recovery.The branch also fixes a real bug in the shared
Local2Regpass, found by this frontend's output pattern: promoting chained local-to-local copies left references to erased values, and the verifier did not catch it. The fix comes with a regression test that fails without it.Trade-offs
How the MIR is obtained. The frontend links against rustc and runs the compiler in-process through
rustc_public, the stable-MIR API, behind a standalone driver. rustc stops after metadata (--emit=metadata), so the MIR stays in memory and no MIR text is parsed. The alternative — a rustc codegen-backend dylib, which would give us the compiler's own mono-item collector for whole-program discovery — was deliberately not taken: it inverts the control flow (cargo would drive midenc instead of midenc calling a frontend), couples us to the unstableCodegenBackendtrait, and adds a toolchain-parity dylib to distribute. The translator consumes onlyrustc_publictypes, so this decision is revisitable at the whole-program stage without rewriting the translator; the roadmap records the decision point.No SSA construction in the frontend. Each MIR local becomes a local slot (definitions store, uses load, no phi nodes), and the existing
local2regpass recovers SSA. This keeps the translator trivial at the cost of depending on that pass — which is what surfaced theLocal2Regbug above.