<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Masahiro Morino</title>
    <description>The latest articles on DEV Community by Masahiro Morino (@masamori).</description>
    <link>https://dev.to/masamori</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1272991%2F920d4607-51ca-4b23-bbb2-f0fcfbad05a8.JPG</url>
      <title>DEV Community: Masahiro Morino</title>
      <link>https://dev.to/masamori</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/masamori"/>
    <language>en</language>
    <item>
      <title>Reading Rust's MIR: Following Control Flow and Values</title>
      <dc:creator>Masahiro Morino</dc:creator>
      <pubDate>Tue, 29 Sep 2026 12:43:23 +0000</pubDate>
      <link>https://dev.to/masamori/reading-rusts-mir-following-control-flow-and-values-36jm</link>
      <guid>https://dev.to/masamori/reading-rusts-mir-following-control-flow-and-values-36jm</guid>
      <description>&lt;p&gt;I started looking into linkers, but a linker's job isn't specific to Rust. Before getting there, I thought it would be more interesting to see how rustc takes our Rust code toward code generation.&lt;/p&gt;

&lt;p&gt;One of the representations we meet along the way is MIR. Apparently, it's used for both borrow checking and code generation. But what exactly is an “intermediate representation,” anyway?&lt;/p&gt;

&lt;p&gt;In this article, I'll use a few small examples to explore &lt;strong&gt;how to read Rust operations in the compiler's MIR&lt;/strong&gt;. We'll start with an &lt;code&gt;if&lt;/code&gt; expression to get a feel for control flow, look closely at a single assignment to see how values and places are represented, and then move on to a call to &lt;code&gt;Vec::push&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article is based on the Rust Compiler Development Guide as consulted on September 29, 2026. The examples were checked with Rust 1.96.0. MIR's structure and textual output can change with the compiler version and optimization settings.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is MIR for?
&lt;/h2&gt;

&lt;p&gt;MIR stands for &lt;strong&gt;Mid-level Intermediate Representation&lt;/strong&gt;. An intermediate representation is a way for a compiler to represent code while processing a program.&lt;/p&gt;

&lt;p&gt;The Rust we write has constructs such as &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;match&lt;/code&gt;, and nested expressions that make programs convenient to write and read. The compiler needs to answer questions like “Where is this value created, and where is it used?” or “After taking this branch, what happens next?” MIR organizes the program into a form that makes types, operations on values, and control flow explicit. That gives the compiler something it can analyze. &lt;a href="https://rustc-dev-guide.rust-lang.org/mir/index.html" rel="noopener noreferrer"&gt;The MIR — Rust Compiler Development Guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, the expression &lt;code&gt;if flag { x } else { 0 }&lt;/code&gt;, which we'll look at shortly, becomes a condition check, operations that set the return value, and a block where the paths meet. Complex expressions are broken down into smaller computations and temporary values. A more uniform representation lets the compiler follow values and control flow without having to handle every variation of the source syntax separately. &lt;a href="https://rustc-dev-guide.rust-lang.org/mir/construction.html" rel="noopener noreferrer"&gt;MIR construction — Rust Compiler Development Guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One major use of MIR is &lt;strong&gt;borrow checking&lt;/strong&gt;. Remember borrowing? The compiler examines how values are used and borrowed on MIR, and also uses MIR for subsequent optimization and code generation. That doesn't mean one unchanged MIR body survives all the way through compilation. It passes through analysis and transformation stages on its way to the form used for code generation. &lt;a href="https://rustc-dev-guide.rust-lang.org/overview.html#mir-lowering" rel="noopener noreferrer"&gt;Overview of the compiler&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's pause and put these representations side by side.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Representation&lt;/th&gt;
&lt;th&gt;Its role in the path we're looking at&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Rust source code&lt;/td&gt;
&lt;td&gt;Where we write the program using expressions and other language constructs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MIR&lt;/td&gt;
&lt;td&gt;Makes Rust types, operations on values, and branches explicit for borrow checking, optimization, and code generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLVM IR&lt;/td&gt;
&lt;td&gt;Carries the program from rustc into LLVM for further optimization and machine-code generation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There are other compiler representations before MIR, but I'm leaving those out here. I'd like to explore that part of the compiler sometime, too. For now, let's focus on &lt;strong&gt;MIR as a representation for analyzing Rust programs&lt;/strong&gt; and look inside it. &lt;a href="https://rustc-dev-guide.rust-lang.org/overview.html" rel="noopener noreferrer"&gt;Overview of the compiler&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  In MIR, control flow becomes blocks
&lt;/h2&gt;

&lt;p&gt;One characteristic of MIR is that control flow takes the form of a &lt;strong&gt;control-flow graph&lt;/strong&gt;, or CFG. Operations are grouped into basic blocks, connected by branches and jumps. Each block contains statements, such as assignments, and ends with a terminator, such as a branch, a function call, or a return. &lt;a href="https://rustc-dev-guide.rust-lang.org/mir/index.html" rel="noopener noreferrer"&gt;The MIR — Rust Compiler Development Guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's a small function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;choose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;flag&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returns &lt;code&gt;x&lt;/code&gt; when &lt;code&gt;flag&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, and &lt;code&gt;0&lt;/code&gt; when it's &lt;code&gt;false&lt;/code&gt;. Compiling this function to MIR with Rust 1.96.0 gives us four basic blocks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ups83w7ok35knjqd6ma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ups83w7ok35knjqd6ma.png" alt="bb0 checks flag. The true branch enters bb1 and sets the return value to x; the false branch enters bb2 and sets it to 0. Both paths join at bb3, which returns." width="800" height="686"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1: The block structure of the generated MIR. The diagram uses flag, x, and return value in place of the internal local names.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;bb1&lt;/code&gt;, setting the return value to &lt;code&gt;x&lt;/code&gt; is a statement, and jumping to &lt;code&gt;bb3&lt;/code&gt; is the terminator. &lt;code&gt;bb0&lt;/code&gt; has no statements at all, just the conditional-branch terminator. So a block doesn't have to do some computation before it branches.&lt;/p&gt;

&lt;p&gt;A basic block is a unit that &lt;strong&gt;executes its statements in order and ends with a terminator&lt;/strong&gt;. A terminator doesn't necessarily choose between multiple destinations. In this example, &lt;code&gt;goto&lt;/code&gt; has one successor, &lt;code&gt;switchInt&lt;/code&gt; selects a successor based on a value, and &lt;code&gt;return&lt;/code&gt; has no successor block within this function. Thinking of a terminator as the block's exit helps explain why these operations belong to the same category.&lt;/p&gt;

&lt;h2&gt;
  
  
  Matching the MIR output to the diagram
&lt;/h2&gt;

&lt;p&gt;In the output, arguments and the return value are represented by names such as &lt;code&gt;_1&lt;/code&gt;, &lt;code&gt;_2&lt;/code&gt;, and &lt;code&gt;_0&lt;/code&gt;. Here, &lt;code&gt;_1&lt;/code&gt; is &lt;code&gt;flag&lt;/code&gt;, &lt;code&gt;_2&lt;/code&gt; is &lt;code&gt;x&lt;/code&gt;, and &lt;code&gt;_0&lt;/code&gt; holds the return value. If we omit the declarations and keep just the blocks, the correspondence with the diagram becomes clear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bb0: {
    switchInt(copy _1) -&amp;gt; [0: bb2, otherwise: bb1];
}

bb1: {
    _0 = copy _2;
    goto -&amp;gt; bb3;
}

bb2: {
    _0 = const 0_u32;
    goto -&amp;gt; bb3;
}

bb3: {
    return;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What was one &lt;code&gt;if&lt;/code&gt; expression in the source has become a conditional branch, assignments, and a block where the paths meet. Making control flow explicit gives the compiler a way to analyze each path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading one line of MIR: Places and values
&lt;/h2&gt;

&lt;p&gt;Now that we know how the blocks connect, let's look more closely at the assignment in &lt;code&gt;bb1&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_0 = copy _2;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It simply puts &lt;code&gt;x&lt;/code&gt; into the return value, but the pieces have different roles in MIR. The key terms are &lt;strong&gt;Local, Place, Operand, and Rvalue&lt;/strong&gt;. We're starting to collect quite a few terms now.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Local is storage; a Place identifies a location
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Local&lt;/strong&gt; is a storage location for something like a function argument, a local variable, or a temporary value. MIR identifies locals by indices, written as &lt;code&gt;_1&lt;/code&gt;, for example. These are the locals in &lt;code&gt;choose&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Local&lt;/th&gt;
&lt;th&gt;Role in this function&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The special location for the return value&lt;/td&gt;
&lt;td&gt;&lt;code&gt;u32&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The argument &lt;code&gt;flag&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The argument &lt;code&gt;x&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;u32&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Storage here is a compiler-level concept. It doesn't mean that every Local necessarily ends up with its own stack slot in the final machine code. And the same-looking local names can have different roles in different functions, as we'll see below.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Place&lt;/strong&gt; is an expression identifying a location to read from or write to. It can refer to an entire Local, a field within it, or a location reached through a reference. For example, if &lt;code&gt;_1&lt;/code&gt; were a tuple, &lt;code&gt;_1&lt;/code&gt; would identify the whole tuple and &lt;code&gt;_1.0&lt;/code&gt; its first field. Yes, this gets a little confusing.&lt;/p&gt;

&lt;p&gt;A Local is a unit of storage; a Place specifies a particular location. In &lt;code&gt;_0 = copy _2;&lt;/code&gt;, both the destination &lt;code&gt;_0&lt;/code&gt; and the source &lt;code&gt;_2&lt;/code&gt; are Places referring to entire Locals. &lt;a href="https://rustc-dev-guide.rust-lang.org/mir/index.html#key-mir-vocabulary" rel="noopener noreferrer"&gt;Key MIR vocabulary&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  An Operand supplies a value; an Rvalue produces the value to assign
&lt;/h3&gt;

&lt;p&gt;Identifying a location doesn't yet say how we'll use the value stored there. That's where an &lt;strong&gt;Operand&lt;/strong&gt; comes in. Three forms we'll encounter are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operand&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;copy _2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy the value from the specified Place&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move _3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move the value from the specified Place&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;const 0_u32&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use a constant value&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An &lt;strong&gt;Rvalue&lt;/strong&gt;, meanwhile, is an expression that produces the value to assign. It might add values, create a reference to a Place, or simply use an Operand's value. The R comes from the &lt;strong&gt;right-hand side&lt;/strong&gt; of an assignment.&lt;/p&gt;

&lt;p&gt;Here's how the pieces fit together. This is an explanatory tree, not literal rustc output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Statement: assignment  _0 = copy _2;
├─ Destination Place: _0
└─ Rvalue: use an Operand's value directly
   └─ Operand: copy _2
      └─ Source Place: _2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The printed &lt;code&gt;copy _2&lt;/code&gt; can be a little puzzling: we're looking at an Rvalue when we consider the whole right-hand side, and an Operand when we focus on its input. &lt;strong&gt;The Rvalue in this example wraps one Operand and produces that Operand's value unchanged.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The assignment &lt;code&gt;_0 = const 0_u32;&lt;/code&gt; in &lt;code&gt;bb2&lt;/code&gt; has the same shape. The input changes from a value stored in a Local to a constant, but we're still assigning an Operand's value to the return-value Place. &lt;a href="https://rustc-dev-guide.rust-lang.org/mir/index.html#key-mir-vocabulary" rel="noopener noreferrer"&gt;Assignments, Rvalues, and Operands in MIR&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is Vec::push a terminator?
&lt;/h2&gt;

&lt;p&gt;Let's use those terms to read another example, also found in the official guide:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compiling it to MIR with Rust 1.96.0 produces this block for the first &lt;code&gt;push&lt;/code&gt;. I explicitly used &lt;code&gt;-C panic=unwind&lt;/code&gt; to inspect the unwinding path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bb1: {
    _3 = &amp;amp;mut _1;
    _2 = Vec::&amp;lt;i32&amp;gt;::push(move _3, const 1_i32) -&amp;gt; [return: bb2, unwind: bb5];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;_1&lt;/code&gt; is the Local for &lt;code&gt;vec&lt;/code&gt;. &lt;code&gt;_3&lt;/code&gt; is a temporary Local of type &lt;code&gt;&amp;amp;mut Vec&amp;lt;i32&amp;gt;&lt;/code&gt;, and &lt;code&gt;_2&lt;/code&gt; receives the &lt;code&gt;()&lt;/code&gt; returned by &lt;code&gt;push&lt;/code&gt;. Local indices belong to their individual functions, so these &lt;code&gt;_1&lt;/code&gt; and &lt;code&gt;_2&lt;/code&gt; are different from the ones in &lt;code&gt;choose&lt;/code&gt;. Watch out for that!&lt;/p&gt;

&lt;p&gt;The first line, &lt;code&gt;_3 = &amp;amp;mut _1;&lt;/code&gt;, is an assignment statement. On the left, &lt;code&gt;_3&lt;/code&gt; is the destination Place. On the right, &lt;code&gt;&amp;amp;mut _1&lt;/code&gt; is an Rvalue that creates a mutable reference to the Place &lt;code&gt;_1&lt;/code&gt;. Our earlier Rvalue simply used an Operand; this one creates a reference value from a Place.&lt;/p&gt;

&lt;p&gt;The next line passes the Operands &lt;code&gt;move _3&lt;/code&gt; and &lt;code&gt;const 1_i32&lt;/code&gt; to &lt;code&gt;push&lt;/code&gt;. What's being moved is the mutable reference stored in &lt;code&gt;_3&lt;/code&gt;, not the &lt;code&gt;Vec&lt;/code&gt; itself.&lt;/p&gt;

&lt;p&gt;And that whole call is a terminator. Despite its &lt;code&gt;_2 = ...&lt;/code&gt; appearance, it isn't an assignment statement. Look at the destinations at the end:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;th&gt;What happens in this example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;return: bb2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If the call returns normally, continue to the block containing the next &lt;code&gt;push&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unwind: bb5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If the call unwinds, enter the cleanup block that drops &lt;code&gt;vec&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;return: bb2&lt;/code&gt; label does not mean that &lt;code&gt;main&lt;/code&gt; finishes. It tells us &lt;strong&gt;where execution resumes in this function after the called function returns&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In Rust source, &lt;code&gt;vec.push(1);&lt;/code&gt; is one line. In MIR, it is split into preparing the reference argument and a call that explicitly includes the paths execution can take afterward. Treating a function call as a terminator lets us follow both normal execution and unwinding cleanup through the connections between blocks. &lt;a href="https://rustc-dev-guide.rust-lang.org/mir/index.html#key-mir-vocabulary" rel="noopener noreferrer"&gt;The guide's Vec example&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating MIR locally
&lt;/h2&gt;

&lt;p&gt;For the first example, I saved the function in &lt;code&gt;choose.rs&lt;/code&gt; and ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rustc choose.rs &lt;span class="nt"&gt;--crate-type&lt;/span&gt; lib &lt;span class="nt"&gt;--edition&lt;/span&gt; 2024 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-C&lt;/span&gt; opt-level&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;--emit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This writes the MIR to &lt;code&gt;choose.mir&lt;/code&gt;. The environment was &lt;code&gt;rustc 1.96.0 (ac68faa20 2026-05-25)&lt;/code&gt;, targeting &lt;code&gt;aarch64-apple-darwin&lt;/code&gt;, with LLVM 22.1.2. The output shown here comes from those conditions. Different optimization settings, for example, won't necessarily produce the same block structure.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;Vec&lt;/code&gt; example, I saved the code in &lt;code&gt;vec_push.rs&lt;/code&gt; and ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rustc vec_push.rs &lt;span class="nt"&gt;--edition&lt;/span&gt; 2024 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-C&lt;/span&gt; opt-level&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="nv"&gt;panic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;unwind &lt;span class="nt"&gt;--emit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even with &lt;code&gt;-C opt-level=0&lt;/code&gt;, the emitted MIR isn't necessarily the state before all transformations. The &lt;code&gt;StorageLive&lt;/code&gt; statements shown in the official guide don't appear in this &lt;code&gt;Vec&lt;/code&gt; output. When comparing dumps, we need to pay attention to both the Rust version and the stage of MIR we're looking at. Setting the MIR optimization level with &lt;code&gt;-Z mir-opt-level=0&lt;/code&gt; requires nightly; this article uses output from stable. &lt;a href="https://rustc-dev-guide.rust-lang.org/mir/index.html#key-mir-vocabulary" rel="noopener noreferrer"&gt;MIR output and optimization&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading control flow and values separately
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;choose&lt;/code&gt;, a single &lt;code&gt;if&lt;/code&gt; expression became a block that checks the condition, two blocks that set the return value, and a block that returns. Separating storage locations such as &lt;code&gt;_0&lt;/code&gt; from units of control flow such as &lt;code&gt;bb0&lt;/code&gt; makes the output easier to follow.&lt;/p&gt;

&lt;p&gt;Inside each block, Local and Place tell us which location we're dealing with; Operand and Rvalue tell us how values are used and produced. In the &lt;code&gt;Vec::push&lt;/code&gt; example, we also saw that a call is itself a terminator, with normal-return and unwinding paths.&lt;/p&gt;

&lt;p&gt;MIR represents Rust operations in a form the compiler can analyze. Follow control flow through the connections between blocks, then distinguish places from values within each line. With those two ideas in mind, it becomes easier to find the original Rust operations in what initially looks like a wall of symbols.&lt;/p&gt;

&lt;p&gt;So, when we move from MIR toward machine code, what happens to generic type arguments? And how is the code divided into units that can be compiled in parallel? In the follow-up, I plan to look at collecting the required code and dividing it into Codegen Units. That article is still in preparation.&lt;/p&gt;

&lt;p&gt;There's a lot to wrap my head around. But that's what makes it interesting.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>computerscience</category>
    </item>
  </channel>
</rss>
