Open-Source Wikis

/

Rust

/

Compiler

/

MIR pipeline

rust-lang/rust

MIR pipeline

MIR (Mid-level Intermediate Representation) is rustc's main IR for everything after type checking. It's a control-flow-graph-based, three-address-code-style IR that's small enough to optimize quickly and structured enough to verify.

Why MIR exists

Before MIR (pre-Rust 1.12 era), rustc translated HIR straight to LLVM IR. That made many analyses awkward: the borrow checker had to reason about source-level constructs (for loops, ?, drop semantics), and codegen had to re-derive control flow. MIR fixed both: HIR is desugared into a small set of well-defined statements, and every later phase reads MIR.

Today MIR is the substrate for borrow checking, dataflow, optimization, const evaluation, and codegen.

Pipeline

graph TD
    HIR[(HIR + TypeckResults)]
    HIR --> ThirBuild[rustc_mir_build<br/>HIR → THIR]
    ThirBuild --> THIR[(THIR)]
    THIR --> Build[rustc_mir_build<br/>THIR → MIR]
    Build --> MIR0[(MIR: built)]
    MIR0 --> Borrowck[rustc_borrowck]
    Borrowck --> MIR1[(MIR: validated)]
    MIR1 --> Optimize[rustc_mir_transform]
    Optimize --> MIR2[(MIR: optimized)]
    MIR2 --> Eval[rustc_const_eval]
    MIR2 --> Mono[rustc_monomorphize]
    Eval -.const items.-> Mono
    Mono --> Codegen[codegen backend]

THIR

Before MIR comes THIR (Typed HIR) — a brief intermediate where every expression has a fully resolved type and every method call has a DefId. THIR is short-lived: rustc_mir_build builds it from HIR, then immediately lowers it to MIR. THIR exists because building MIR directly from HIR was complex; THIR is the "type-annotated tree" that simplifies the lowering. Defined in compiler/rustc_middle/src/thir.rs.

MIR shape

A MIR Body is:

  • A list of LocalDecls (_0 is the return place, _1.. are arguments, the rest are temporaries)
  • A list of BasicBlocks — each a sequence of Statements ending in a Terminator
  • Source info for diagnostics
  • Promoted constants (an array of sub-bodies for promoted & constants)

Statements are mostly assignments: _3 = _1 + _2, _4 = &_3, _5 = move _4. Terminators are Goto, SwitchInt, Return, Unreachable, Drop, Call, Assert, Yield, FalseEdge, FalseUnwind, InlineAsm. The full enum is in compiler/rustc_middle/src/mir/syntax.rs.

rustc_mir_build — building MIR

compiler/rustc_mir_build/ takes THIR and produces an unoptimized MIR Body. Its biggest sub-area is pattern lowering: match arms are lowered into a decision tree of SwitchInts and FalseEdges. The exhaustiveness check from rustc_pattern_analysis is invoked here.

rustc_borrowck — borrow checking

compiler/rustc_borrowck/ is the borrow checker. It runs on the freshly-built MIR and verifies:

  • No two mutable borrows alias
  • No mutable borrow aliases an immutable borrow
  • Lifetimes (regions) outlive their referents
  • Moves out of borrowed places aren't allowed
  • &mut cannot be reborrowed inappropriately

The implementation does region inference on the MIR, computing the smallest set of program points each lifetime variable must contain to satisfy the constraints. This is "Non-Lexical Lifetimes" (NLL) — although the term is now mostly historical because that's just how the borrow checker works.

The borrow checker is also the source of some of rustc's most-loved diagnostic improvements; the rustc_borrowck::diagnostics module is huge.

rustc_mir_dataflow — dataflow framework

compiler/rustc_mir_dataflow/ is a generic forward/backward dataflow analysis framework: define a state lattice and a transfer function, plug it into the engine, and get fixed-point analyses for free. Several analyses use it, the borrow checker among them.

rustc_mir_transform — optimization passes

compiler/rustc_mir_transform/ is a sequence of MIR-to-MIR passes:

  • SimplifyCfg — collapses goto chains
  • Inline — inlines small #[inline]d callees
  • ConstProp — constant propagation
  • DeadStoreElimination
  • RemoveStorageMarkers
  • Deaggregator_x = (a, b) → individual field stores
  • JumpThreading
  • … and many more

The main loop runs each enabled pass in order; passes log dumps to mir_dump/ when -Zdump-mir=all is set. See Tests for MIR for the snapshot test infrastructure.

rustc_const_eval — the MIR interpreter

compiler/rustc_const_eval/ interprets MIR. It powers:

  • const fn evaluation during type checking
  • const and static evaluation during codegen
  • Pattern matching on consts at compile time
  • Miri (src/tools/miri/), the standalone undefined-behavior detector — Miri is literally the same engine with extra alias tracking turned on (Stacked Borrows / Tree Borrows)

The interpreter operates on Allocations (byte buffers with provenance tracking) and provides a faithful runtime semantics for safe Rust plus the parts of unsafe Rust where the rules are agreed upon.

rustc_monomorphize — generic instantiation and partitioning

compiler/rustc_monomorphize/ does two things:

  1. Mono collection — find every concrete instantiation (MonoItem) actually reached from the crate's roots. This is essentially a reachability analysis on the call graph.
  2. CGU partitioning — group mono items into codegen units (CGUs) to balance parallel codegen. The resulting CGU plan is what each backend processes.

The tcx.collect_and_partition_mono_items(()) query is the entry point.

Key abstractions

Type Where Purpose
Body<'tcx> rustc_middle::mir A MIR body for one function/const/promoted
BasicBlock / BasicBlockData rustc_middle::mir A CFG node
Local / LocalDecl rustc_middle::mir A SSA-ish local variable
Place<'tcx> rustc_middle::mir An lvalue: local + projection list
Operand<'tcx> rustc_middle::mir An rvalue source (move, copy, constant)
Statement / Terminator rustc_middle::mir::syntax One MIR instruction
MoveData rustc_borrowck::move_data Move analysis output
BorrowSet rustc_borrowck::borrow_set All borrows in a body, indexed
Allocation rustc_const_eval::interpret Interpreter's heap / stack memory
MonoItem rustc_middle::mir::mono A concrete generic instantiation
Cgu rustc_middle::mir::mono A codegen unit

Entry points for modification

  • New MIR optimization → rustc_mir_transform/src/passes/ (note: opts must be sound for all targets, including const-eval)
  • Borrow checker fix → rustc_borrowck. Probably also a tests/ui/borrowck/ test
  • Const-eval bug → rustc_const_eval/src/interpret/. Probably also affects Miri
  • New MIR dump or visualization → rustc_middle::mir::pretty or mir_dump
  • Mono collection / CGU split — careful: changes affect compile time and binary layout

Where MIR-opt tests live

tests/mir-opt/ contains snapshot tests of MIR after specific passes. To run, edit, and regenerate:

./x test tests/mir-opt --bless

See also

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

MIR pipeline – Rust wiki | Factory