MIR separates a value’s semantic type, physical storage, and lifetime policy. Those three facts often correlate, but they are not interchangeable.
Locals
Each local records:
- its resolved
TypeRef; - an optional source name;
- an
RcMode; - a
LocalStorageclassification.
A local is a compiler value identity, not necessarily one memory slot. Its selected carrier can reside in a WebAssembly local within one continuation, receive a frame slot because it crosses suspension, or denote an arena-relative handle to bytes elsewhere.
Storage
LocalStorage has four states:
| Storage | Contract |
|---|---|
Unset |
Layout inference has not stamped an authoritative choice |
Scalar |
Value has a fixed size/alignment and fits a scalar carrier |
Flex |
Local holds a four-byte arena-relative offset to allocated storage |
InlineAggregate |
Fixed aggregate bytes live in a reserved frame region |
Unset has conservative type-based fallbacks for early analysis, but it is not
valid where final code generation requires an explicit layout decision.
Inline aggregates avoid a separate heap allocation for bounded, non-escaping
values. Their primary local still uses the expected offset-shaped carrier so
field operations can remain uniform, while InlineAggregateData reserves the
actual frame bytes. Unsized or escaping data uses flex storage.
Frame layout
A suspending function’s frame begins with the runtime ABI header. Remaining slots are aligned and packed after it. Each slot records offset, size, alignment, type, and purpose.
Purposes include:
- parameters and locals live across suspension;
- state tags for confined continuation clusters;
- spawn-arm state and inline subframes;
- pointers to recursive child frames;
- reusable staging space for wide transient values;
- inline aggregate data;
- persistent storage for boxed aggregate call results;
- frame-resident cell contents.
The layout maps locals to primary slots and separately maps data regions such as inline aggregates and cells. These are not aliases by default. In particular, two inline aggregate data regions must not be merged merely because their pointer-shaped primary locals have non-overlapping apparent liveness.
The staging region follows a maximum, not sum, policy: sequential operations can reuse one region sized for the largest requirement. Persistent suspending results cannot use that shared region when two results may be live together.
Ownership
RcMode describes how an RC-relevant local participates in lifetime
management:
| Mode | Meaning |
|---|---|
Static |
Compile-time analysis inserts required clone/drop operations |
Confined |
Crosses a single-resume boundary while remaining statically managed |
Linear |
Ownership moves to at most one consumer; no clone/drop machinery |
PinElided |
A proven frame-local reference needs no runtime pin |
RawRef |
A short-lived absolute reference cannot cross an allocation barrier |
InheritedExtract |
Extracted value inherits the container’s ownership rather than retaining |
BorrowedExtract |
Extracted value is borrowed while the container remains owner |
The last two may have identical load shapes to an ordinary field read. Their explicit mode prevents code generation from guessing whether an extraction must retain.
Ownership becomes operations in MIR: clone, retain, drop, shallow drop, in-place drop, and reuse. These are semantic operations even if a later pass elides or combines them. Code generation must not invent missing reference counting from type shape.
Arena-relative and absolute addresses
Stable heap and frame references normally use offsets relative to the current arena base. An arena can grow or relocate its backing memory, so an absolute address derived before an allocation barrier may become stale.
RawRef is permitted only when analysis proves all uses remain in one
allocation-free span and die before the next barrier. The raw-pointer verifier
is the backstop:
- a derived absolute pointer cannot cross a memory-growth, host, allocation, or suspension barrier;
- it cannot escape through a return, frame, closure, container, or unknown call;
- consumers after a barrier must rematerialize from an arena-relative source.
Failure is diagnosed at the originating source range when available. Falling
back from RawRef to the normal relative reference is always safe; keeping an
unproven absolute address is not.
Runtime correspondence
MIR decides which values require frame or flex storage, but it does not implement the allocator. The runtime’s Memory section defines arena, flat, and TLSF allocation behavior. MIR’s obligation is to select the correct storage domain and emit explicit allocator and ownership primitives at the backend boundary.