rustc-dev-guide/mir/passes.html

370 lines
22 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>MIR queries and passes: getting the MIR - Rust Compiler Development Guide</title>
<!-- Custom HTML head -->
<meta name="description" content="A guide to developing the Rust compiler (rustc)">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../favicon.svg">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../";
const default_light_theme = "light";
const default_dark_theme = "navy";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Rust Compiler Development Guide</h1>
<div class="right-buttons">
<a href="../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/rust-lang/rustc-dev-guide" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/rust-lang/rustc-dev-guide/edit/master/src/mir/passes.md" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="mir-queries-and-passes"><a class="header" href="#mir-queries-and-passes">MIR queries and passes</a></h1>
<p>If you would like to get the MIR:</p>
<ul>
<li>for a function - you can use the <code>optimized_mir</code> query (typically used by codegen) or the <code>mir_for_ctfe</code> query (typically used by compile time function evaluation, i.e., <em>CTFE</em>);</li>
<li>for a promoted - you can use the <code>promoted_mir</code> query.</li>
</ul>
<p>These will give you back the final, optimized MIR. For foreign def-ids, we simply read the MIR
from the other crate's metadata. But for local def-ids, the query will
construct the optimized MIR by requesting a pipeline of upstream queries<sup class="footnote-reference" id="fr-query-1"><a href="#footnote-query">1</a></sup>.
Each query will contain a series of passes.
This section describes how those queries and passes work and how you can extend them.</p>
<p>To produce the optimized MIR for a given def-id <code>D</code>, <code>optimized_mir(D)</code>
goes through several suites of passes, each grouped by a
query. Each suite consists of passes which perform linting, analysis, transformation or
optimization. Each query represent a useful intermediate point
where we can access the MIR dialect for type checking or other purposes:</p>
<ul>
<li><code>mir_built(D)</code> it gives the initial MIR just after it's built;</li>
<li><code>mir_const(D)</code> it applies some simple transformation passes to make MIR ready for
const qualification;</li>
<li><code>mir_promoted(D)</code> - it extracts promotable temps into separate MIR bodies, and also makes MIR
ready for borrow checking;</li>
<li><code>mir_drops_elaborated_and_const_checked(D)</code> - it performs borrow checking, runs major
transformation passes (such as drop elaboration) and makes MIR ready for optimization;</li>
<li><code>optimized_mir(D)</code> it performs all enabled optimizations and reaches the final state.</li>
</ul>
<h2 id="implementing-and-registering-a-pass"><a class="header" href="#implementing-and-registering-a-pass">Implementing and registering a pass</a></h2>
<p>A <code>MirPass</code> is some bit of code that processes the MIR, typically transforming it along the way
somehow. But it may also do other things like linting (e.g., <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/check_packed_ref/struct.CheckPackedRef.html"><code>CheckPackedRef</code></a>,
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/check_const_item_mutation/struct.CheckConstItemMutation.html"><code>CheckConstItemMutation</code></a>, <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/function_item_references/struct.FunctionItemReferences.html"><code>FunctionItemReferences</code></a>, which implement <code>MirLint</code>) or
optimization (e.g., <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/simplify/enum.SimplifyCfg.html"><code>SimplifyCfg</code></a>, <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/remove_unneeded_drops/struct.RemoveUnneededDrops.html"><code>RemoveUnneededDrops</code></a>). While most MIR passes
are defined in the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/"><code>rustc_mir_transform</code></a> crate, the <code>MirPass</code> trait itself is
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/pass_manager/trait.MirPass.html">found</a> in the <code>rustc_middle</code> crate, and it basically consists of one primary method,
<code>run_pass</code>, that simply gets an <code>&amp;mut Body</code> (along with the <code>tcx</code>).
The MIR is therefore modified in place (which helps to keep things efficient).</p>
<p>A basic example of a MIR pass is <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/remove_storage_markers/struct.RemoveStorageMarkers.html"><code>RemoveStorageMarkers</code></a>, which walks
the MIR and removes all storage marks if they won't be emitted during codegen. As you
can see from its source, a MIR pass is defined by first defining a
dummy type, a struct with no fields:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct RemoveStorageMarkers;
<span class="boring">}</span></code></pre></pre>
<p>for which we implement the <code>MirPass</code> trait. We can then insert
this pass into the appropriate list of passes found in a query like
<code>mir_built</code>, <code>optimized_mir</code>, etc. (If this is an optimization, it
should go into the <code>optimized_mir</code> list.)</p>
<p>Another example of a simple MIR pass is <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/cleanup_post_borrowck/struct.CleanupPostBorrowck.html"><code>CleanupPostBorrowck</code></a>, which walks
the MIR and removes all statements that are not relevant to code generation. As you can see from
its <a href="https://github.com/rust-lang/rust/blob/e2b52ff73edc8b0b7c74bc28760d618187731fe8/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs#L27">source</a>, it is defined by first defining a dummy type, a struct with no
fields:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct CleanupPostBorrowck;
<span class="boring">}</span></code></pre></pre>
<p>for which we implement the <code>MirPass</code> trait:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>impl&lt;'tcx&gt; MirPass&lt;'tcx&gt; for CleanupPostBorrowck {
fn run_pass(&amp;self, tcx: TyCtxt&lt;'tcx&gt;, body: &amp;mut Body&lt;'tcx&gt;) {
...
}
}
<span class="boring">}</span></code></pre></pre>
<p>We <a href="https://github.com/rust-lang/rust/blob/e2b52ff73edc8b0b7c74bc28760d618187731fe8/compiler/rustc_mir_transform/src/lib.rs#L413">register</a> this pass inside the <code>mir_drops_elaborated_and_const_checked</code> query.
(If this is an optimization, it should go into the <code>optimized_mir</code> list.)</p>
<p>If you are writing a pass, there's a good chance that you are going to
want to use a <a href="./visitor.html">MIR visitor</a>. MIR visitors are a handy way to walk all
the parts of the MIR, either to search for something or to make small
edits.</p>
<h2 id="stealing"><a class="header" href="#stealing">Stealing</a></h2>
<p>The intermediate queries <code>mir_const()</code> and <code>mir_promoted()</code> yield up
a <code>&amp;'tcx Steal&lt;Body&lt;'tcx&gt;&gt;</code>, allocated using <code>tcx.alloc_steal_mir()</code>.
This indicates that the result may be <strong>stolen</strong> by a subsequent query this is an
optimization to avoid cloning the MIR. Attempting to use a stolen
result will cause a panic in the compiler. Therefore, it is important
that you do not accidentally read from these intermediate queries without
the consideration of the dependency in the MIR processing pipeline.</p>
<p>Because of this stealing mechanism, some care must be taken to
ensure that, before the MIR at a particular phase in the processing
pipeline is stolen, anyone who may want to read from it has already
done so.</p>
<p>Concretely, this means that if you have a query <code>foo(D)</code>
that wants to access the result of <code>mir_promoted(D)</code>, you need to have <code>foo(D)</code>
calling the <code>mir_const(D)</code> query first. This will force it
to execute even though you don't directly require its result.</p>
<blockquote>
<p>This mechanism is a bit dodgy. There is a discussion of more elegant
alternatives in <a href="https://github.com/rust-lang/rust/issues/41710">rust-lang/rust#41710</a>.</p>
</blockquote>
<h3 id="overview"><a class="header" href="#overview">Overview</a></h3>
<p>Below is an overview of the stealing dependency in the MIR processing pipeline<sup class="footnote-reference" id="fr-part-1"><a href="#footnote-part">2</a></sup>:</p>
<pre class="mermaid">flowchart BT
mir_for_ctfe* --borrow--&gt; id40
id5 --steal--&gt; id40
mir_borrowck* --borrow--&gt; id3
id41 --steal part 1--&gt; id3
id40 --steal part 0--&gt; id3
mir_const_qualif* -- borrow --&gt; id2
id3 -- steal --&gt; id2
id2 -- steal --&gt; id1
id1([mir_built])
id2([mir_const])
id3([mir_promoted])
id40([mir_drops_elaborated_and_const_checked])
id41([promoted_mir])
id5([optimized_mir])
style id1 fill:#bbf
style id2 fill:#bbf
style id3 fill:#bbf
style id40 fill:#bbf
style id41 fill:#bbf
style id5 fill:#bbf
</pre>
<p>The stadium-shape queries (e.g., <code>mir_built</code>) with a deep color are the primary queries in the
pipeline, while the rectangle-shape queries (e.g., <code>mir_const_qualif*</code><sup class="footnote-reference" id="fr-star-1"><a href="#footnote-star">3</a></sup>) with a shallow color
are those subsequent queries that need to read the results from <code>&amp;'tcx Steal&lt;Body&lt;'tcx&gt;&gt;</code>. With the
stealing mechanism, the rectangle-shape queries must be performed before any stadium-shape queries,
that have an equal or larger height in the dependency tree, ever do.</p>
<h3 id="example"><a class="header" href="#example">Example</a></h3>
<p>As an example, consider MIR const qualification. It wants to read the result produced by the
<code>mir_const</code> query. However, that result will be <strong>stolen</strong> by the <code>mir_promoted</code> query at some
time in the pipeline. Before <code>mir_promoted</code> is ever queried, calling the <code>mir_const_qualif</code> query
will succeed since <code>mir_const</code> will produce (if queried the first time) or cache (if queried
multiple times) the <code>Steal</code> result and the result is <strong>not</strong> stolen yet. After <code>mir_promoted</code> is
queried, the result would be stolen and calling the <code>mir_const_qualif</code> query to read the result
would cause a panic.</p>
<p>Therefore, with this stealing mechanism, <code>mir_promoted</code> should guarantee any <code>mir_const_qualif*</code>
queries are called before it actually steals, thus ensuring that the reads have already happened
(remember that <a href="../query.html">queries are memoized</a>, so executing a query twice
simply loads from a cache the second time).</p>
<hr>
<ol class="footnote-definition"><li id="footnote-query">
<p>See the <a href="../query.html">Queries</a> chapter for the general concept of query. <a href="#fr-query-1"></a></p>
</li>
<li id="footnote-part">
<p>The <code>mir_promoted</code> query will yield up a tuple
<code>(&amp;'tcx Steal&lt;Body&lt;'tcx&gt;&gt;, &amp;'tcx Steal&lt;IndexVec&lt;Promoted, Body&lt;'tcx&gt;&gt;&gt;)</code>, <code>promoted_mir</code> will steal
part 1 (<code>&amp;'tcx Steal&lt;IndexVec&lt;Promoted, Body&lt;'tcx&gt;&gt;&gt;</code>) and <code>mir_drops_elaborated_and_const_checked</code>
will steal part 0 (<code>&amp;'tcx Steal&lt;Body&lt;'tcx&gt;&gt;</code>). And their stealing is irrelevant to each other,
i.e., can be performed separately. <a href="#fr-part-1"></a></p>
</li>
<li id="footnote-star">
<p>Note that the <code>*</code> suffix in the queries represent a set of queries with the same prefix.
For example, <code>mir_borrowck*</code> represents <code>mir_borrowck</code>, <code>mir_borrowck_const_arg</code> and
<code>mir_borrowck_opt_const_arg</code>. <a href="#fr-star-1"></a></p>
</li>
</ol>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../mir/visitor.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../asm.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../mir/visitor.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../asm.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
<script src="../mermaid.min.js"></script>
<script src="../mermaid-init.js"></script>
</div>
</body>
</html>