Files
revive/docs/print.html
T
LJ 6e0dfdcbb1 Update newyork optimizer in compiler book (#549)
## Description

Updates the compiler book to reflect the state of the newyork optimizer
as of resolc `v1.3.0`.
2026-06-18 16:39:15 +02:00

4934 lines
306 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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>revive compiler book</title>
<meta name="robots" content="noindex">
<!-- Custom HTML head -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="favicon-de23e50b.svg">
<link rel="shortcut icon" href="favicon-8114d1fc.png">
<link rel="stylesheet" href="css/variables-8adf115d.css">
<link rel="stylesheet" href="css/general-2459343d.css">
<link rel="stylesheet" href="css/chrome-ae938929.css">
<link rel="stylesheet" href="css/print-9e4910d8.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="fonts/fonts-9644e21d.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="mdbook-highlight-css" href="highlight-493f70e1.css">
<link rel="stylesheet" id="mdbook-tomorrow-night-css" href="tomorrow-night-4c0ae647.css">
<link rel="stylesheet" id="mdbook-ayu-highlight-css" href="ayu-highlight-3fdfc3ac.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";
window.path_to_searchindex_js = "searchindex-339ac392.js";
</script>
<!-- Start loading toc.js asap -->
<script src="toc-1290cc30.js"></script>
</head>
<body>
<div id="mdbook-help-container">
<div id="mdbook-help-popup">
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
<div>
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
<p>Press <kbd>?</kbd> to show this help</p>
<p>Press <kbd>Esc</kbd> to hide this help</p>
</div>
</div>
</div>
<div id="mdbook-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="mdbook-sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("mdbook-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 = false;
}
if (sidebar === 'visible') {
sidebar_toggle.checked = true;
} else {
html.classList.remove('sidebar-visible');
}
</script>
<nav id="mdbook-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="mdbook-sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="mdbook-page-wrapper" class="page-wrapper">
<div class="page">
<div id="mdbook-menu-bar-hover-placeholder"></div>
<div id="mdbook-menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="mdbook-sidebar-toggle" class="icon-button" for="mdbook-sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="mdbook-sidebar">
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></span>
</label>
<button id="mdbook-theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="mdbook-theme-list">
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M371.3 367.1c27.3-3.9 51.9-19.4 67.2-42.9L600.2 74.1c12.6-19.5 9.4-45.3-7.6-61.2S549.7-4.4 531.1 9.6L294.4 187.2c-24 18-38.2 46.1-38.4 76.1L371.3 367.1zm-19.6 25.4l-116-104.4C175.9 290.3 128 339.6 128 400c0 3.9 .2 7.8 .6 11.6c1.8 17.5-10.2 36.4-27.8 36.4H96c-17.7 0-32 14.3-32 32s14.3 32 32 32H240c61.9 0 112-50.1 112-112c0-2.5-.1-5-.2-7.5z"/></svg></span>
</button>
<ul id="mdbook-theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-ayu">Ayu</button></li>
</ul>
<button id="mdbook-search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="mdbook-searchbar">
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352c79.5 0 144-64.5 144-144s-64.5-144-144-144S64 128.5 64 208s64.5 144 144 144z"/></svg></span>
</button>
</div>
<h1 class="menu-title">revive compiler book</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<span class=fa-svg id="print-button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M128 0C92.7 0 64 28.7 64 64v96h64V64H354.7L384 93.3V160h64V93.3c0-17-6.7-33.3-18.7-45.3L400 18.7C388 6.7 371.7 0 354.7 0H128zM384 352v32 64H128V384 368 352H384zm64 32h32c17.7 0 32-14.3 32-32V256c0-35.3-28.7-64-64-64H64c-35.3 0-64 28.7-64 64v96c0 17.7 14.3 32 32 32H64v64c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V384zm-16-88c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24z"/></svg></span>
</a>
</div>
</div>
<div id="mdbook-search-wrapper" class="hidden">
<form id="mdbook-searchbar-outer" class="searchbar-outer">
<div class="search-wrapper">
<input type="search" id="mdbook-searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="mdbook-searchresults-outer" aria-describedby="searchresults-header">
<div class="spinner-wrapper">
<span class=fa-svg id="fa-spin"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M304 48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zm0 416c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM48 304c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm464-48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM142.9 437c18.7-18.7 18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zm0-294.2c18.7-18.7 18.7-49.1 0-67.9S93.7 56.2 75 75s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zM369.1 437c18.7 18.7 49.1 18.7 67.9 0s18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9z"/></svg></span>
</div>
</div>
</form>
<div id="mdbook-searchresults-outer" class="searchresults-outer hidden">
<div id="mdbook-searchresults-header" class="searchresults-header"></div>
<ul id="mdbook-searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('mdbook-sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('mdbook-sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#mdbook-sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="mdbook-content" class="content">
<main>
<h1 id="welcome"><a class="header" href="#welcome">Welcome</a></h1>
<p>Hello and a warm welcome to the <code>revive</code> Solidity compiler book!</p>
<blockquote class="blockquote-tag blockquote-tag-warning">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>Warning</p>
<p>Solidity on PVM is running on the <code>pallet-revive</code> runtime. This introduces <strong>observable semantic differences</strong> in comparison with the EVM.</p>
<p>Study the <a href="https://paritytech.github.io/revive/user_guide/differences.html">differences</a> section carefully. <strong>Ignoring these differences may lead to defunct contracts.</strong></p>
<p>Notable examples:</p>
<ul>
<li>The 63/64 gas rule isn’t implemented in the pallet (introduces potential DoS vector when calling other contracts)</li>
<li>Contract instantiation works differently (by hash instead of by code)</li>
<li>The gas model implemented by <code>pallet-revive</code> differs from Ethereum</li>
<li>The heap size is fixed instead of gas-metered and there’s a fixed amount of stack size (contracts working fine on EVM may trap on PVM)</li>
</ul>
</blockquote>
<h2 id="target-audience"><a class="header" href="#target-audience">Target audience</a></h2>
<ul>
<li><strong>Solidity dApp developers</strong> should read the <a href="#resolc-user-guide">user guide</a>. Solidity on PolkaVM introduces important differences to EVM which should be well understood.</li>
<li><strong>Contributors</strong> will find the <a href="#developer-guide">developer guide</a> helpful for getting up to speed.</li>
</ul>
<h2 id="other-polkadot-contracts-resources"><a class="header" href="#other-polkadot-contracts-resources">Other Polkadot contracts resources</a></h2>
<p>Head to <a href="https://docs.polkadot.com/develop/smart-contracts/">contracts.polkadot.io</a> for more general information about contracts on Polkadot.</p>
<h2 id="about"><a class="header" href="#about">About</a></h2>
<p>This <a href="https://github.com/rust-lang/mdBook">mdBook</a> documents the revive Solidity compiler project. The content is found under <code>book/</code>. Run <code>make book</code> to observe changes.</p>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="resolc-user-guide"><a class="header" href="#resolc-user-guide"><code>resolc</code> user guide</a></h1>
<p><code>resolc</code> is a Solidity <code>v0.8</code> compiler for <a href="https://docs.polkadot.com/develop/smart-contracts/overview/#native-smart-contracts">Polkadot <code>native</code> smart contracts</a>. Solidity compiled with <code>resolc</code> targets PolaVM (PVM). Thanks to additional compiler optimizations and the PVM JIT, contract code can execute much faster than the EVM equivalent. <code>resolc</code> supports almost all Solidity <code>v0.8</code> features including inline assembly, offering a high level of comptability with the Ethereum Solidity reference implementation.</p>
<h2 id="revive-vs-resolc-nomenclature"><a class="header" href="#revive-vs-resolc-nomenclature"><code>revive</code> vs. <code>resolc</code> nomenclature</a></h2>
<p><code>revive</code> is the name of the overarching “Solidity to PolkaVM” compiler project, which contains multiple components (for example the Yul parser, the code generation library, the <code>resolc</code> executable itself, and many more things).</p>
<p><code>resolc</code> is the name of the compiler driver executable, combining many <code>revive</code> components in a single and easy to use binary application.</p>
<p>In other words, <code>revive</code> is the whole compiler infrastructure (more like <code>LLVM</code>) and <code>resolc</code> is a user-facing single-entrypoint compiler frontend (more like <code>clang</code>).</p>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="installation"><a class="header" href="#installation">Installation</a></h1>
<p>Building Solidity contracts for PolkaVM requires installing the following two compilers:</p>
<ul>
<li><code>solc</code>: The <a href="https://github.com/argotorg/solidity">Ethereum Solidity reference compiler</a> implementation.</li>
<li><code>resolc</code>: The revive Solidity compiler YUL frontend and PolkaVM code generator.</li>
</ul>
<h2 id="resolc-binary-releases"><a class="header" href="#resolc-binary-releases"><code>resolc</code> binary releases</a></h2>
<p><code>resolc</code> is supported an all major operating systems and installation is straightforward.
Please find our <a href="https://paritytech.github.io/resolc-bin/">binary releases</a> for the following platforms:</p>
<ul>
<li>Linux (MUSL)</li>
<li>MacOS (universal)</li>
<li>Windows</li>
<li>Wasm via emscripten</li>
</ul>
<h2 id="installing-the-solc-dependency"><a class="header" href="#installing-the-solc-dependency">Installing the <code>solc</code> dependency</a></h2>
<p><code>resolc</code> uses <code>solc</code> during the compilation process, please refer to the <a href="https://docs.soliditylang.org/en/latest/installing-solidity.html">Ethereum Solidity documentation</a> for installation instructions.</p>
<h2 id="revive-npm-package"><a class="header" href="#revive-npm-package"><code>revive</code> NPM package</a></h2>
<p>We distribute the revive compiler as <a href="https://github.com/paritytech/revive/tree/main/js/resolc">node.js module</a>.</p>
<h2 id="buidling-resolc-from-source"><a class="header" href="#buidling-resolc-from-source">Buidling <code>resolc</code> from source</a></h2>
<p>Please follow the build <a href="https://github.com/paritytech/revive?tab=readme-ov-file#building-from-source">instructions in the revive <code>README.md</code></a>.</p>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="cli-usage"><a class="header" href="#cli-usage">CLI usage</a></h1>
<p>We aim to keep the <code>resolc</code> CLI usage close to <code>solc</code>. There are a few things and options worthwhile to know about in <code>resolc</code> which do not exist in the Ethereum world. This chapter explains those in more detail than the CLI help message.</p>
<blockquote class="blockquote-tag blockquote-tag-tip">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"></path></svg>Tip</p>
<p>For the complete help about CLI options, please see <code>resolc --help</code>.</p>
</blockquote>
<h3 id="llvm-optimization-levels"><a class="header" href="#llvm-optimization-levels">LLVM optimization levels</a></h3>
<pre><code class="language-bash">-O, --optimization &lt;OPTIMIZATION&gt;
</code></pre>
<p><code>resolc</code> exposes the optimization level setting for the LLVM backend. The performance and size of compiled contracts varies widely between different optimization levels. (This is independent of <code>--newyork</code> which selects the IR lowering pipeline.)</p>
<p>Valid levels are the following:</p>
<ul>
<li><code>0</code>: No optimizations are applied.</li>
<li><code>1</code>: Basic optimizations for execution time.</li>
<li><code>2</code>: Advanced optimizations for execution time.</li>
<li><code>3</code>: Aggressive optimizations for execution time.</li>
<li><code>s</code>: Optimize for code size.</li>
<li><code>z</code>: Aggressively optimize for code size.</li>
</ul>
<p>By default, <code>-Oz</code> is applied.</p>
<h3 id="newyork-ir-pipeline"><a class="header" href="#newyork-ir-pipeline">newyork IR pipeline</a></h3>
<pre><code class="language-bash">--newyork
</code></pre>
<p>Enables the <a href="#the-newyork-optimizer">newyork optimizer</a> to reduced compiled contract code size, by routing Yul lowering through the <strong>experimental</strong> newyork IR pipeline instead of the standard Yul-to-LLVM path. Composes with <code>--yul</code>, <code>--combined-json</code>, and the default Solidity mode. In standard JSON mode this flag is rejected; enable the pipeline via the <a href="#settingspolkavmnewyork"><code>settings.polkavm.newyork</code></a> input field instead. Off by default.</p>
<h3 id="stack-size"><a class="header" href="#stack-size">Stack size</a></h3>
<pre><code class="language-bash">--stack-size &lt;STACK_SIZE&gt;
</code></pre>
<p>PVM is a register machine with a traditional stack memory space for local variables. This controls the total amount of stack space the contract can use.</p>
<p>You are incentivized to keep this value as small as possible:</p>
<ol>
<li>Increasing the stack size will increase gas costs due to increased startup costs.</li>
<li>The stack size contributes to the total memory size a contract can use, which includes the contract’s code size.</li>
</ol>
<p>Default value: 131072</p>
<blockquote class="blockquote-tag blockquote-tag-warning">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>Warning</p>
<p>If the contract uses more stack memory than configured, it will compile fine but eventually revert execution at runtime!</p>
</blockquote>
<h3 id="heap-size"><a class="header" href="#heap-size">Heap size</a></h3>
<pre><code class="language-bash">--heap-size &lt;HEAP_SIZE&gt;
</code></pre>
<p>Unlike the EVM, due to the lack of dynamic memory metering, PVM contracts emulate the EVM heap memory with a static buffer. Consequentially, instead of infinite memory with exponentially growing gas costs, PVM contracts have a finite amount of memory with constant gas costs available.</p>
<p>You are incentivized to keep this value as small as possible:
1.Increasing the heap size will increase startup costs.
2.The heap size contributes to the total memory size a contract can use, which includes the contract’s code size</p>
<p>Default value: 131072</p>
<blockquote class="blockquote-tag blockquote-tag-warning">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>Warning</p>
<p>If the contract uses more heap memory than configured, it will compile fine but eventually revert execution at runtime!</p>
</blockquote>
<h3 id="solc"><a class="header" href="#solc">solc</a></h3>
<pre><code class="language-bash">--solc &lt;SOLC&gt;
</code></pre>
<p>Specify the path to the <code>solc</code> executable. By default, the one in <code>${PATH}</code> is used.</p>
<h3 id="debug-artifacts"><a class="header" href="#debug-artifacts">Debug artifacts</a></h3>
<pre><code class="language-bash">--debug-output-dir &lt;DEBUG_OUTPUT_DIRECTORY&gt;
</code></pre>
<p>Dump all intermediary compiler artifacts to files in the specified directory. This includes the Yul IR, optimized and unoptimized LLVM IR, the ELF object and the PVM assembly. When the newyork pipeline is active, the <a href="#debug-output">newyork IR is additionally dumped</a> (the final IR, a pre-late-pass snapshot, and heap and memory optimization data). Useful for debugging and development purposes.</p>
<h3 id="debug-info"><a class="header" href="#debug-info">Debug info</a></h3>
<pre><code class="language-bash">-g
</code></pre>
<p>Generate source based debug information in the output code file. Useful for debugging and development purposes and disabled by default.</p>
<h3 id="deploy-time-linking"><a class="header" href="#deploy-time-linking">Deploy time linking</a></h3>
<pre><code class="language-bash">--link [--libraries &lt;LIBRARIES&gt;] &lt;INPUT_FILES&gt;
</code></pre>
<p>In Solidity, 3 things can happen with libraries:</p>
<ol>
<li>They are not <code>extern</code>ally callable and thus can be inlined.
<ol>
<li>The solc Solidity optimizer inlines those (usually the case). Note: <code>resolc</code> always activates the solc Solidity optimizer.</li>
<li>If the solc Solidity optimizer is disabled or for some reason fails to inline them (both rare), they are not inlined and require linking.</li>
</ol>
</li>
<li>They are <code>extern</code>ally callable but still linked at compile time. This is the case if at compile time the library address is known (i.e. <code>--libraries</code> supplied in CLI or the corresponding setting in STD JSON input).</li>
<li>They are linked at deploy time. This happens when the compiler does not know the library address (i.e. <code>--libraries</code> flag is missing or the provided libraries are incomplete, same for STD JSON input). This case is rare because it’s discourage and should never be used by production dApps.</li>
</ol>
<p>In cases <code>1.2</code> and <code>3</code>:</p>
<ul>
<li>Some of the produced code blobs will be in the “unlinked” raw <code>ELF</code> object format and not yet deployable.</li>
<li>To make them deployable, they need to be “linked” (done using the <code>resolc --link</code> linker mode explained below).</li>
<li>The compiler emitted <code>DELEGATECALL</code> instructions to call non-inlined (unlinked) libraries. The contract deployer must make sure to deploy any libraries prior to contract deployment.</li>
</ul>
<blockquote class="blockquote-tag blockquote-tag-warning">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>Warning</p>
<p>Using deploy time linking is officially <strong>discouraged</strong>. Mainly due to bytecode hashes changing after the fact. We decided to support it in <code>resolc</code> regardless, due to popular request.</p>
</blockquote>
<p>Similar to how it works in <code>solc</code>, <code>--libraries</code> may be used to provide libraries during linking mode.</p>
<p>Unlike with <code>solc</code>, where linking implies a simple string substitution mechanism, <code>resolc</code> needs to resolve actual missing <code>ELF</code> symbols. This is due to how factory dependencies work in PVM. As a consequence, it isn’t sufficient to just provide the unlinked blobs to the linker. Instead, they must be provided in the exact same directory structure the Solidity source code was found during compile time.</p>
<p>Example:</p>
<ul>
<li>The contract <code>src/foo/bar.sol:Bar</code> is involved in deploy time linking. It may be a factory dependency.</li>
<li>The contract blob needs to be provided inside a relative <code>src/foo/</code> directory to <code>--link</code>. Otherwise symbol resolution may fail.</li>
</ul>
<blockquote class="blockquote-tag blockquote-tag-note">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
<p>Tooling is supposed to take care of this. In the future, we may append explicit linkage data to simplify the deploy time linking feature.</p>
</blockquote>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="js-npm-package"><a class="header" href="#js-npm-package">JS NPM package</a></h1>
<p>The <code>resolc</code> compiler driver is published as an NPM package under <a href="https://www.npmjs.com/package/@parity/resolc">@parity/resolc</a>.</p>
<p>It’s usable from <code>Node.js</code> code or directly from the command line:</p>
<pre><code class="language-shell">npx @parity/resolc@latest --bin crates/integration/contracts/flipper.sol -o /tmp/out
</code></pre>
<blockquote class="blockquote-tag blockquote-tag-note">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
<p>While the npm package makes a nice portable option, it doesn’t expose all options.</p>
</blockquote>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="tooling-integration"><a class="header" href="#tooling-integration">Tooling integration</a></h1>
<p><code>resolc</code> achieved successful integration with a variety of third party developer tools.</p>
<h2 id="solidity-toolkits"><a class="header" href="#solidity-toolkits">Solidity toolkits</a></h2>
<p>Support for <code>resolc</code> is available in forks of the <a href="https://hardhat.org">hardhat</a> and <a href="https://getfoundry.sh">foundry</a> Solidity toolkits:</p>
<ul>
<li><a href="https://github.com/paritytech/hardhat-polkadot">The Parity Hardhat fork</a></li>
<li><a href="https://github.com/paritytech/foundry-polkadot?tab=readme-ov-file#2-resolc-compiler-integration">The Parity Foundry fork</a></li>
</ul>
<h2 id="compiler-explorer"><a class="header" href="#compiler-explorer">Compiler explorer</a></h2>
<p><code>resolc</code> is available on <a href="https://godbolt.org/z/6GM6n4Ka3">godbolt.org</a> for the Solidity and Yul input languages. See also the announcement post on the <a href="https://forum.polkadot.network/t/resolc-is-live-on-compiler-explorer">forum</a>.</p>
<h2 id="remix-ide"><a class="header" href="#remix-ide">Remix IDE</a></h2>
<p>There is remix IDE fork with <code>resolc</code> support at <a href="https://remix.polkadot.io">remix.polkadot.io</a>. Unfortunately this is no longer actively maintained (there might be bugs and outdated <code>resolc</code> versions).</p>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="standard-json-interface"><a class="header" href="#standard-json-interface">Standard JSON interface</a></h1>
<p>The <code>revive</code> compiler is mostly compatible with the <code>solc</code> standard JSON interface. There are a few differences and additional (PVM related) <strong>input</strong> configurations:</p>
<h2 id="the-settingspolkavm-object"><a class="header" href="#the-settingspolkavm-object">The <code>settings.polkavm</code> object</a></h2>
<p>Used to configure PVM specific compiler settings.</p>
<h3 id="settingspolkavmdebuginformation"><a class="header" href="#settingspolkavmdebuginformation"><code>settings.polkavm.debugInformation</code></a></h3>
<p>A boolean value allowing to enable debug information. Corresponds to <code>resolc -g</code>.</p>
<h3 id="settingspolkavmnewyork"><a class="header" href="#settingspolkavmnewyork"><code>settings.polkavm.newyork</code></a></h3>
<p>A boolean value allowing to enable the experimental <a href="#the-newyork-optimizer">newyork IR pipeline</a> for Yul lowering. Corresponds to <code>resolc --newyork</code>. Off by default.</p>
<p>The output JSON includes which pipeline actually ran, via the top-level <code>resolc_pipeline</code> field (<code>"newyork"</code> or <code>"yul"</code> for the standard Yul-to-LLVM pipeline).</p>
<h3 id="the-settingspolkavmmemoryconfig-object"><a class="header" href="#the-settingspolkavmmemoryconfig-object">The <code>settings.polkavm.memoryConfig</code> object</a></h3>
<p>Used to apply PVM specific memory configuration settings.</p>
<h4 id="settingspolkavmmemoryconfigheapsize"><a class="header" href="#settingspolkavmmemoryconfigheapsize"><code>settings.polkavm.memoryConfig.heapSize</code></a></h4>
<p>A numerical value allowing to configure the contract heap size. Corresponds to <code>resolc --heap-size</code>.</p>
<h4 id="settingspolkavmmemoryconfigstacksize"><a class="header" href="#settingspolkavmmemoryconfigstacksize"><code>settings.polkavm.memoryConfig.stackSize</code></a></h4>
<p>A numerical value allowing to configure the contract stack size. Corresponds to <code>resolc --stack-size</code>.</p>
<h2 id="the-settingsoptimizer-object"><a class="header" href="#the-settingsoptimizer-object">The <code>settings.optimizer</code> object</a></h2>
<p>The <code>settings.optimizer</code> object is augmented with support for PVM specific optimization settings.</p>
<h3 id="settingsoptimizermode"><a class="header" href="#settingsoptimizermode"><code>settings.optimizer.mode</code></a></h3>
<p>A single char value to configure the LLVM optimizer settings. Corresponds to <code>resolc -O</code>.</p>
<h2 id="settingsllvmarguments"><a class="header" href="#settingsllvmarguments"><code>settings.llvmArguments</code></a></h2>
<p>Allows to specify arbitrary command line arguments to LLVM initialization. Used mainly for development and debugging purposes.</p>
<h2 id="the-settingsoutputselection-object"><a class="header" href="#the-settingsoutputselection-object">The <code>settings.outputSelection</code> object</a></h2>
<p>Used to select desired outputs.</p>
<h3 id="the-all--wildcard"><a class="header" href="#the-all--wildcard">The “all” (<code>*</code>) wildcard</a></h3>
<p>Resolc supports the “all” (<code>*</code>) wildcard for the file-level (first-level) and contract-level (second-level) keys. A file-level key can be either the wildcard or a specific file name, whereas the contract-level key can only be the wildcard for robustness reasons.</p>
<p>Thus, output can be requested in 2 ways:</p>
<pre><code class="language-json">// All files and all contracts:
{
"settings": {
"outputSelection": {
"*": {
"*": [/* specific contract-level output fields */],
"": [/* specific file-level output fields */]
}
}
}
}
// Specific files and all their contracts:
{
"settings": {
"outputSelection": {
"path/to/my/file.sol": {
"*": [/* specific contract-level output fields */],
"": [/* specific file-level output fields */]
},
// Rest of files...
}
}
}
</code></pre>
<h3 id="the-contract-level-evm-output-selection"><a class="header" href="#the-contract-level-evm-output-selection">The contract-level <code>evm</code> output selection</a></h3>
<blockquote class="blockquote-tag blockquote-tag-note">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
<p>Currently, resolc supports requesting either the full <code>evm</code> output, or one more level of specificity, such as <code>evm.bytecode</code>.</p>
</blockquote>
<p>When requesting code generation, such as <code>evm.bytecode</code> or <code>evm.assembly</code>, the resolc compilation process additionally needs <code>ast</code>, <code>metadata</code>, <code>irOptimized</code>, and <code>evm.methodIdentifiers</code> selectors. These selectors will be automatically added if code generation is needed, but will only be included in the output if explicitly requested.</p>
<pre><code class="language-json">{
"settings": {
"outputSelection": {
"path/to/my/file1.sol": {
// Contracts in this file will generate bytecode.
// Only these fields of the JSON output selection will be in the `contracts` output.
"*": ["abi", "evm.methodIdentifiers", "metadata", "evm.bytecode"],
// Only this field of the JSON output selection will be in the `sources` output.
"": ["ast"]
},
"path/to/my/file2.sol": {
// No contracts in this file will generate bytecode.
"*": ["abi", "evm.methodIdentifiers", "metadata"],
// No `ast` will be in the `sources` output (only the automatically added `id`,
// similar to solc as this is not a configurable output selection).
"": []
},
}
}
}
</code></pre>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="differences-to-evm"><a class="header" href="#differences-to-evm">Differences to EVM</a></h1>
<p>This section highlights some potentially observable differences in the <a href="https://docs.soliditylang.org/en/latest/yul.html#evm-dialect">YUL EVM dialect</a> translation compared to Ethereum Solidity.</p>
<p>Solidity developers deploying dApps to <a href="https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/revive"><code>pallet-revive</code></a> ought to read and understand this section well.</p>
<h2 id="deploy-code-vs-runtime-code"><a class="header" href="#deploy-code-vs-runtime-code">Deploy code vs. runtime code</a></h2>
<p>Our contract runtime does not differentiate between runtime code and deploy (constructor) code.
Instead, both are emitted into a single PVM contract code blob and live on-chain.
Therefore, in EVM terminology, the deploy code equals the runtime code.</p>
<blockquote class="blockquote-tag blockquote-tag-tip">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"></path></svg>Tip</p>
<p>In constructor code, the <code>codesize</code> instruction will return the call data size instead of the actual code blob size.</p>
</blockquote>
<h2 id="solidity"><a class="header" href="#solidity">Solidity</a></h2>
<p>We are aware of the following differences in the translation of Solidity code.</p>
<h3 id="the-6364-gas-rule"><a class="header" href="#the-6364-gas-rule">The <code>63/64</code> gas rule</a></h3>
<p><code>pallet-revive</code> <strong>doesn’t apply the <code>63/64</code> gas rule</strong>. We strongly advice to change any code calling untrusted contracts to supply a limited amount of gas only!</p>
<h3 id="addresscreationcode"><a class="header" href="#addresscreationcode"><code>address.creationCode</code></a></h3>
<p>This returns the bytecode keccak256 hash instead.</p>
<h2 id="yul-functions"><a class="header" href="#yul-functions">YUL functions</a></h2>
<p>The below list contains noteworthy differences in the translation of YUL functions.</p>
<blockquote class="blockquote-tag blockquote-tag-note">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
<p>Many functions receive memory buffer offset pointer or size arguments. The PVM pointer size is 32 bit, supplying memory offset or buffer size values above <code>2^32-1</code> may lead to <code>OutOfGas</code> errors trap contract execution.</p>
</blockquote>
<p>The <code>solc</code> compiler ought to always emit valid memory references, so Solidity dApp authors don’t need to worry about this unless they deal with low level <code>assembly</code> code.</p>
<h3 id="mload-mstore-msize-mcopy-memory-related-functions"><a class="header" href="#mload-mstore-msize-mcopy-memory-related-functions"><code>mload</code>, <code>mstore</code>, <code>msize</code>, <code>mcopy</code> (memory related functions)</a></h3>
<p>In general, revive preserves the memory layout, meaning low level memory operations are supported. However, a few caveats apply:</p>
<ul>
<li>The EVM linear heap memory is emulated using a fixed byte buffer of 128kb. This implies that the maximum memory a contract can use is limited to 128kb (on Ethereum, contract memory is capped by gas and therefore varies).</li>
<li>Thus, accessing memory offsets larger than the fixed buffer size will trap the contract at runtime with an <code>OutOfBound</code> error.</li>
<li>The compiler might detect and optimize unused memory reads and writes, leading to a different <code>msize</code> compared to what the EVM would see.</li>
</ul>
<h3 id="calldataload-calldatacopy"><a class="header" href="#calldataload-calldatacopy"><code>calldataload</code>, <code>calldatacopy</code></a></h3>
<p>In the constructor code, the offset is ignored and this always returns <code>0</code>.</p>
<blockquote class="blockquote-tag blockquote-tag-warning">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>Warning</p>
<p><code>pallet-revive</code> restricts the calldata size (to 128kb at the time of writing).</p>
</blockquote>
<h3 id="codecopy"><a class="header" href="#codecopy"><code>codecopy</code></a></h3>
<p>Only supported in constructor code.</p>
<h3 id="create-create2"><a class="header" href="#create-create2"><code>create</code>, <code>create2</code></a></h3>
<p>Deployments on revive work different than on EVM. In a nutshell: Instead of supplying the deploy code concatenated with the constructor arguments (the EVM deploy model), the <a href="https://docs.rs/pallet-revive/latest/pallet_revive/trait.SyscallDoc.html#tymethod.instantiate">revive runtime expects two pointers</a>:</p>
<ol>
<li>A buffer containing the code hash to deploy.</li>
<li>The constructor arguments buffer.</li>
</ol>
<p>To make contract instantiation using the <code>new</code> keyword in Solidity work seamlessly,
<code>revive</code> translates the <code>dataoffset</code> and <code>datasize</code> instructions so that they assume the contract hash instead of the contract code.
The hash is always of constant size.
Thus, <code>revive</code> is able to supply the expected code hash and constructor arguments pointer to the runtime.</p>
<blockquote class="blockquote-tag blockquote-tag-warning">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>Warning</p>
<p>This might fall apart in code creating contracts inside <code>assembly</code> blocks. <strong>We strongly discourage using the <code>create</code> family opcodes to manually craft deployments in <code>assembly</code> blocks!</strong> Usually, the reason for using <code>assembly</code> blocks is to save gas, which is likely futile on revive due to the underlying differences in the VM architectures, gas models and transaction costs.</p>
</blockquote>
<h3 id="dataoffset"><a class="header" href="#dataoffset"><code>dataoffset</code></a></h3>
<p>Returns the contract hash.</p>
<h3 id="datasize"><a class="header" href="#datasize"><code>datasize</code></a></h3>
<p>Returns the contract hash size (constant value of <code>32</code>).</p>
<h3 id="revert-return"><a class="header" href="#revert-return"><code>revert</code>, <code>return</code></a></h3>
<p><code>pallet-revive</code> restricts the returndata size (to 128kb at the time of writing).</p>
<h3 id="prevrandao-difficulty"><a class="header" href="#prevrandao-difficulty"><code>prevrandao</code>, <code>difficulty</code></a></h3>
<p>Translates to a constant value of <code>2500000000000000</code>.</p>
<h3 id="pc-extcodecopy"><a class="header" href="#pc-extcodecopy"><code>pc</code>, <code>extcodecopy</code></a></h3>
<p>Only valid to use in EVM (they also have no use case in PVM) and produce a compile time error.</p>
<h3 id="blobhash-blobbasefee"><a class="header" href="#blobhash-blobbasefee"><code>blobhash</code>, <code>blobbasefee</code></a></h3>
<p>Related to the Ethereum rollup model and produce a compile time error. Polkadot offers a superior rollup model, removing the use case for blob data related opcodes.</p>
<h2 id="difference-regarding-the-solc-via-ir-mode"><a class="header" href="#difference-regarding-the-solc-via-ir-mode">Difference regarding the <code>solc</code> <code>via-ir</code> mode</a></h2>
<p>There are two different compilation pipelines available in <code>solc</code> and <a href="https://docs.soliditylang.org/en/latest/ir-breaking-changes.html">there are small differences between them</a>.</p>
<p>Since <code>resolc</code> processes the YUL IR, always assume the <code>solc</code> IR based codegen behavior for contracts compiled with the <code>revive</code> compiler.</p>
<h3 id="example-state-variable-initialization-order-in-inheritance"><a class="header" href="#example-state-variable-initialization-order-in-inheritance">Example: State variable initialization order in inheritance</a></h3>
<p>With <code>via-ir</code>, base constructors run before derived state variables are initialized:</p>
<pre><code class="language-solidity">contract InnerContract {
uint public innerConstructedStartTokenId;
constructor() {
innerConstructedStartTokenId = _startTokenId();
}
function _startTokenId() internal view virtual returns (uint) {
return 0;
}
}
contract Test is InnerContract {
uint public START_TOKEN_ID = 1;
constructor() InnerContract() {
}
function _startTokenId() internal view virtual override returns (uint) {
return START_TOKEN_ID;
}
}
</code></pre>
<p>Here, <code>innerConstructedStartTokenId</code> in <code>Test</code> returns <code>0</code> (with legacy EVM codegen it’d return <code>1</code>).</p>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="rust-contract-libraries"><a class="header" href="#rust-contract-libraries">Rust contract libraries</a></h1>
<blockquote class="blockquote-tag blockquote-tag-note">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
<p>This is not yet implemented but something for consideration on the roadmap.</p>
</blockquote>
<p>Solidity - tightly coupled to the EVM - introduces some inherent inefficiencies that are by design and either needs to be followed or can’t be easily worked around, even with efforts like better optimized compiler and VM implementations. This represents a technical dead end. So far the EVM sees no adoption beyond the blockchain industry. Chances are that <a href="https://ethereum-magicians.org/t/long-term-l1-execution-layer-proposal-replace-the-evm-with-risc-v">the EVM end up deprecated</a> for technical reasons (or maybe not and the RISC-V idea gets abandoned, who knows).</p>
<p>PVM, however, is a general purpose VM. It supports LLVM based mainstream programming languages like Rust. It’s a common software engineering practice to compose applications from pieces written in multiple languages, using each to their own strength. For example, AI solutions traditionally use the python scripting language for convenient developer experience, while the underlying AI models get implemented in a lower level language such as C++.</p>
<p>The same pattern can of course be applied to dApps, where we’d expect application specific languages like Solidity mixed with libraries implementing computationally complex algorithms in a lower level language. Business logic and user interfaces are naturally implemented as regular Solidity dApps which can include (link against) Rust libraries. Rust is a fast, safe low level language and the Polkadot SDK is written in Rust itself, making it an excellent choice.</p>
<p>For example, <a href="https://en.wikipedia.org/wiki/Zero-knowledge_proof">ZK proof verifiers</a> or expensive <a href="https://en.wikipedia.org/wiki/Decentralized_finance">DeFi</a> primitives would benefit greatly from Rust implementations.</p>
<p><code>revive</code> provides tooling support and a small Rust contracts SDK for seamless integration with Rust libraries.</p>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="revive-runner-sandbox"><a class="header" href="#revive-runner-sandbox"><code>revive-runner</code> sandbox</a></h1>
<p>Running contract code usually requires a blockchain node. While local dev nodes can be used, sometimes it’s just not desirable to do so. Instead, it can be much more convenient to run and debug contract code with a stripped down environment.</p>
<p>This is where the <code>revive-runner</code> comes in handy. In a nutshell, it is a single-binary no-blockchain <code>pallet-revive</code> runtime.</p>
<h2 id="installation-and-usage"><a class="header" href="#installation-and-usage">Installation and usage</a></h2>
<p>Inside the root <code>revive</code> repository directory, install it from source (requires Rust installed):</p>
<pre><code class="language-bash">make install-revive-runner
</code></pre>
<p>After installing, see <code>revive-runner --help</code> for usage help.</p>
<h2 id="trace-logs"><a class="header" href="#trace-logs">Trace logs</a></h2>
<p>The standard <code>RUST_LOG</code> environment variable controls the log output from the contract execution. This includes <code>revive</code> runtime logs and PVM execution trace logs. Sometimes it’s convenient to have more fine granular insight. Some useful filters:</p>
<ul>
<li><code>RUST_LOG=runtime=trace</code>: The <code>pallet-revive</code> runtime trace logs.</li>
<li><code>RUST_LOG=polkavm=trace</code>: Low level PolkaVM instruction tracing.</li>
</ul>
<h2 id="automatic-contract-instantiation"><a class="header" href="#automatic-contract-instantiation">Automatic contract instantiation</a></h2>
<p>To avoid running the constract in an unitialized state, <code>revive-runner</code> automatically instantiates the contract before calling it (constructor arguments can be provided).</p>
<h2 id="example"><a class="header" href="#example">Example</a></h2>
<p>Suppose we want to trace the syscalls of the execution of a compiled contract file <code>Flipper.pvm</code>:</p>
<pre><code class="language-bash">RUST_LOG=runtime=trace revive-runner -f Flipper.pvm
[DEBUG runtime::revive] Contract memory usage: purgable=6144/3145728 KB baseline=103063/1572864
[TRACE runtime::revive::strace] call_data_size() = Ok(0) gas_consumed: Weight { ref_time: 985209, proof_size: 0 }
[TRACE runtime::revive::strace] value_transferred(out_ptr: 4294836096) = Ok(()) gas_consumed: Weight { ref_time: 2937634, proof_size: 0 }
[TRACE runtime::revive::strace] call_data_copy(out_ptr: 131216, out_len: 0, offset: 0) = Ok(()) gas_consumed: Weight { ref_time: 4084483, proof_size: 0 }
[TRACE runtime::revive::strace] seal_return(flags: 0, data_ptr: 131216, data_len: 0) = Err(TrapReason::Return(ReturnData { flags: 0, data: [] })) gas_consumed: Weight { ref_time: 5510615, proof_size: 0 }
[TRACE runtime::revive] frame finished with: Ok(ExecReturnValue { flags: (empty), data: [] })
[TRACE runtime::revive::strace] call_data_size() = Ok(0) gas_consumed: Weight { ref_time: 985209, proof_size: 0 }
[TRACE runtime::revive::strace] seal_return(flags: 1, data_ptr: 131088, data_len: 0) = Err(TrapReason::Return(ReturnData { flags: 1, data: [] })) gas_consumed: Weight { ref_time: 2456669, proof_size: 0 }
[TRACE runtime::revive] frame finished with: Ok(ExecReturnValue { flags: REVERT, data: [] })
</code></pre>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="developer-guide"><a class="header" href="#developer-guide">Developer guide</a></h1>
<p>This chapter covers internal aspects of the compiler and helps contributors getting started with the <code>revive</code> codebase.</p>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="contributor-guide"><a class="header" href="#contributor-guide">Contributor guide</a></h1>
<p>The <code>revive</code> compiler is an open source software project and we gladly accept quality contributions from anyone!</p>
<h2 id="getting-started"><a class="header" href="#getting-started">Getting started</a></h2>
<p>A quick reference on how to build the Solidity compiler is maintained in the project’s <a href="https://github.com/paritytech/revive?tab=readme-ov-file#building-from-source">README.md</a>.</p>
<h3 id="using-the-makefile"><a class="header" href="#using-the-makefile">Using the <code>Makefile</code></a></h3>
<p>The <a href="https://github.com/paritytech/revive/blob/main/Makefile">Makefile</a> comprehensively encapsulates all development aspects of this codebase. It is kept concise and readable. Please read and use it! You’ll learn for example:</p>
<ul>
<li>How to build and install a <code>resolc</code> development version.</li>
<li>How to run tests and benchmarks.</li>
<li>How to cross-compile <code>resolc</code>.</li>
</ul>
<p>As a general rule-of-thumb: If <code>make test</code> runs fine locally, chances for green CI pipelines are good.</p>
<h3 id="codebase-organization"><a class="header" href="#codebase-organization">Codebase organization</a></h3>
<p>For the most parts, <code>revive</code> is a rather standard Rust workspace codebase. There are some non-Rust dependencies, which sometimes complicates things a little bit.</p>
<h4 id="the-crates-dir"><a class="header" href="#the-crates-dir">The <code>crates/</code> dir</a></h4>
<p>All Rust crates live under the <code>crates/</code> directory. The workspace automatically considers any crate found therein. If you need to add a new create, please implement it there.</p>
<p>Compiler library crates should be named with the <code>revive-</code> prefix. The crate location doesn’t need the prefix.</p>
<h4 id="dependencies"><a class="header" href="#dependencies">Dependencies</a></h4>
<p>Dependencies should be added as workspace dependencies. Try to avoid pinning dependencies whenever possible. If possible, add dev dependencies as <code>dev-dependencies</code> only.</p>
<p>Please do always include the <code>Cargo.lock</code> dependency lock file with your PR. Please don’t run <code>cargo update</code> together with other changes (it is preferred to update the lock file in a dedicated dependency update PR).</p>
<h2 id="contribution-rules"><a class="header" href="#contribution-rules">Contribution rules</a></h2>
<ol>
<li>Changes must be submitted via a pull request (PR) to the github upstream repository.</li>
<li>Ensure that your branch passes <code>make test</code> locally when submitting a pull request.</li>
<li>A PR must not be merged until CI fully passes. Exceptions can be made (for example to fix CI issues itself).</li>
<li>No force pushes to the <code>main</code> branch and open PR branches.</li>
<li>Maintainers can request changes or deny contributions at their own discretion.</li>
</ol>
<h2 id="style-guide"><a class="header" href="#style-guide">Style guide</a></h2>
<p>We require the official Rust formatter and clippy linter. In addition to that, please also consider the following best-effort aspects:</p>
<ul>
<li>Avoid <a href="https://en.wikipedia.org/wiki/Magic_number_(programming)">magic numbers</a> and strings. Instead, add them as module constants.</li>
<li>Avoid abbreviated variable and function names. Always provide meaningful and readable symbols.</li>
<li>Don’t write macros and don’t use third party macros for things that can easily be expressed in few lines of code or outlined into functions.</li>
<li>Avoid import aliasing. Please use the parent or fully qualified path for conflicting symbols.</li>
<li>Any inline comments must provide additional semantic meaning, explain counter-intuitive behavior or highlight non-obvious design decisions. In other words, try to make the code expressive enough to a degree it doesn’t need comments expressing the same thing again in the English language. Delete such comments if your AI assistant generated them.</li>
<li>Public items must have a meaningful doc comment.</li>
<li>Provide meaningful panic messages to <code>.expect()</code> or just use <code>.unwrap()</code>.</li>
</ul>
<h2 id="ai-policy"><a class="header" href="#ai-policy">AI policy</a></h2>
<p>Contributors may use whatever AI assistance tools they wish to whatever degree they wish in the process of creating their contribution, <strong>given they acknowledge the following</strong>:</p>
<p><em>Project maintainers may reject any contribution (or portions of it) if the contribution shows signs of problematic involvement of generative AI</em>.</p>
<p>Judgement of “problematic involvement” lies at the sole discretion of project maintainers. No proof (whether a contribution was in fact AI generated or not) is required. Rationale:</p>
<ul>
<li>No one enjoys reading soulless and uncanny LLM slop. Please review and fix any AI slop yourself prior to submitting a PR.</li>
<li>A Solidity compiler is security sensitive software. Even miniscule mistakes can ultimately lead to loss of funds. AI models are inherently stochastic. They regurarly fail to capture important nuances or produce straight hallucinations. Code that was “blindly” generated has no home here.</li>
<li><code>revive</code> is a large codebase. Generative AI assistants may not have enough “context window” to sufficiently capture correctness, consistency and style aspects of the codebase. We’d like to keep this codebase maintainable <em>by humans</em> for the forseeable future.</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="compiler-architecture-and-internals"><a class="header" href="#compiler-architecture-and-internals">Compiler architecture and internals</a></h1>
<p><code>revive</code> relies on <code>solc</code>, the <a href="https://github.com/argotorg/solidity">Ethereum Solidity compiler</a>, as the Solidity frontend to process smart contracts written in Solidity. <a href="https://github.com/llvm/llvm-project">LLVM</a>, a popular and powerful compiler framework, is used as the compiler backend and does the heavy lifting in terms of optimizitations and RISC-V code generation.</p>
<p><code>revive</code> mainly takes care of lowering the Yul intermediate representation (IR) produced by <code>solc</code> to LLVM IR. This approach provides a good balance between maintaining a high level of Ethereum compatibility, good contract performance and feasible engineering efforts.</p>
<h2 id="resolc"><a class="header" href="#resolc"><code>resolc</code></a></h2>
<p><code>resolc</code> is the overarching compiler driver library and binary.</p>
<p>When compiling a Solidity source file with <code>resolc</code>, the following steps happen under the hood:</p>
<ol>
<li><code>solc</code> is used to lower the Solidity source code into <a href="https://docs.soliditylang.org/en/latest/yul.html">YUL intermediate representation</a>.</li>
<li><code>revive</code> lowers the YUL IR into LLVM IR.</li>
<li>LLVM optimizes the code and emits a RISC-V ELF shared object (through LLD).</li>
<li>The <a href="https://github.com/paritytech/polkavm">PolkaVM</a> linker finally links the ELF shared object into a PolkaVM blob.</li>
</ol>
<p>This compilation process can be visualized as follows:</p>
<p><img src="developer_guide/images/resolc.svg" alt="Architecture Overview"></p>
<h2 id="reproducible-contract-builds"><a class="header" href="#reproducible-contract-builds">Reproducible contract builds</a></h2>
<p>Because on-chain contract code is identified via its code blob hash, it is crucial to maintain reproducible contract builds. A given compiler version must reproduce the contract build <em>exactly</em> on every target platform <code>resolc</code> supports via the official binary releases.</p>
<p>To ensure this, we employ the following measures:</p>
<ul>
<li>The code generation must be fully deterministic. For example iterating over standard <code>HashMap</code> invalidates this due to its internal state, making it an invalid operation in <code>revive</code>. To circumvent that, a <code>BTreeMap</code> can be used instead.</li>
<li>We release fully statically linked <code>resolc</code> binaries. This prevents dynamic linking of potentially differentiating libraries.</li>
<li>The only non-bundled dependency is the <code>solc</code> compiler. This is considered fine because the same properties apply to <code>solc</code>.</li>
</ul>
<h2 id="the-revive-compiler-libraries"><a class="header" href="#the-revive-compiler-libraries">The <code>revive</code> compiler libraries</a></h2>
<p>The main compiler logic is implemented in the <code>revive-yul</code> and <code>revive-llvm-context</code> crates.</p>
<p>The Yul library implements a lexer and parser and lowers the resulting tree into LLVM IR. It does so by emitting LL using the LLVM builder and our own <code>revive-llvm-context</code> compiler context crate. The revive LLVM context crate encapsulates code generation logic (decoupled from the parser).</p>
<p>The Yul library also implements a simple <a href="https://en.wikipedia.org/wiki/Visitor_pattern">visitor</a> interface (see <a href="https://github.com/paritytech/revive/blob/main/crates/yul/src/visitor.rs">visitor.rs</a>). If you want to work with the AST, it is strongly recommended to implement visitors. The LLVM code generation is implemented using a dedicated trait for historical reasons only.</p>
<h2 id="evm-heap-memory"><a class="header" href="#evm-heap-memory">EVM heap memory</a></h2>
<p>PVM doesn’t offer a similar API. Hence the emitted contract code emulates the linear EVM heap memory using a static byte buffer. Data inside this byte buffer is kept big endian for EVM compatibility reasons (unaligned access is allowed and makes optimizing this non-trivial).</p>
<p>Unlike with the EVM, where heap memory usage is gas metered, our heap size is static (the size is user controllable via a setting flag). The compiler emits bound checks to prevent overflows.</p>
<h2 id="the-llvm-dependency"><a class="header" href="#the-llvm-dependency">The LLVM dependency</a></h2>
<p>LLVM is a special non Rust dependency. We interface its builder interface via the <a href="https://crates.io/crates/inkwell">inkwell</a> wrapper crate.</p>
<p>We use upstream LLVM, but release and use our custom builds. We require the compiler builtins specifically built for the PVM <code>rv64emacb</code> target and always leave assertions on. Furthermore, we need cross builds because <code>resolc</code> itself targets emscripten and musl. The <a href="https://crates.io/crates/revive-llvm-builder">revive-llvm-builer</a> functions as a cross-platform build script and is used to build and release the LLVM dependency.</p>
<p>We also maintain the <a href="https://crates.io/crates/lld-sys">lld-sys crate</a> for interfacing with <code>LLD</code>. The LLVM linker is used during the compilation process, but we don’t want to distribute another binary.</p>
<h2 id="custom-optimizations"><a class="header" href="#custom-optimizations">Custom optimizations</a></h2>
<p>An experimental <a href="#the-newyork-optimizer"><code>newyork</code> optimizer</a> introduces a custom IR layer between Yul and LLVM IR to capture optimization opportunities that neither <code>solc</code> nor LLVM can realize on their own. <code>solc</code> optimizes for EVM gas on a 256-bit big-endian stack machine, while LLVM lacks the domain knowledge to understand EVM memory semantics or Solidity patterns. The newyork IR bridges this gap with passes for type narrowing, memory optimization, function deduplication, and more.</p>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="the-newyork-optimizer"><a class="header" href="#the-newyork-optimizer">The newyork optimizer</a></h1>
<p>The <code>newyork</code> crate (<code>crates/newyork/</code>) introduces an additional intermediate representation (IR) layer between Yul and LLVM IR. It enables domain-specific optimizations that neither <code>solc</code> nor LLVM can perform on their own, because they lack semantic knowledge about the cross-domain compilation from EVM to PolkaVM.</p>
<blockquote class="blockquote-tag blockquote-tag-note">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
<p>The newyork optimizer is experimental. It is gated behind the <code>--newyork</code> CLI flag or the <code>settings.polkavm.newyork</code> field in standard JSON input, and not yet enabled by default.</p>
</blockquote>
<h2 id="motivation"><a class="header" href="#motivation">Motivation</a></h2>
<p>The EVM and PolkaVM are fundamentally different machines:</p>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Property</th><th>EVM</th><th>PolkaVM (RISC-V)</th></tr>
</thead>
<tbody>
<tr><td>Word size</td><td>256-bit</td><td>64-bit</td></tr>
<tr><td>Endianness</td><td>Big-endian</td><td>Little-endian</td></tr>
<tr><td>Architecture</td><td>Stack machine</td><td>Register-based</td></tr>
<tr><td>Memory model</td><td>Linear with free pointer convention</td><td>Flat address space</td></tr>
</tbody>
</table>
</div>
<p><code>solc</code> optimizes Yul IR for EVM gas costs on a 256-bit big-endian stack machine. LLVM, on the other hand, operates at too low a level to understand EVM memory semantics or Solidity patterns. By the time Yul reaches LLVM IR, the high-level intent is lost.</p>
<p>The newyork IR sits between these two worlds and recovers enough semantic information to make optimization decisions that neither compiler can make alone.</p>
<h2 id="pipeline-overview"><a class="header" href="#pipeline-overview">Pipeline overview</a></h2>
<pre><code class="language-text"> ┌──────────────────────────────────────────────────┐
Yul AST ──────────► │ newyork IR │ ──► LLVM IR ──► RISC-V
from_yul│ │ to_llvm
│ 1. inline │
│ 2. simplify (pass 1) │
│ 3. dedup (exact + fuzzy) │
│ 4. mem_opt + fmp_prop + keccak_fold │
│ 5. simplify (pass 2) │
│ 6. mapping_access_outlining + guard_narrow │
│ 7. simplify (pass 3) │
│ 8. dedup (exact + fuzzy, pass 2) │
│ ── recursive on subobjects ── │
│ 9. type_inference (iterative narrowing) │
│ 10. late inline loop: inline, simplify, outline, │
│ guard-narrow, simplify, dedup, narrow │
│ 11. heap_opt (analysis) │
│ 12. validate │
└──────────────────────────────────────────────────┘
</code></pre>
<p>The optimizer runs the following passes in order:</p>
<ol>
<li><strong>Inlining</strong> – custom heuristics tuned for PolkaVM call overhead, with Tarjan SCC-based recursion detection and quadratic leave-overhead modeling.</li>
<li><strong>Simplify</strong> (pass 1) – constant folding, algebraic identities, strength reduction (<code>mul</code> by power-of-2 to <code>shl</code>), copy propagation, dead code elimination, environment read CSE (<code>callvalue</code>, <code>caller</code>, <code>origin</code>, etc.), and revert pattern outlining (panic selectors, custom error selectors).</li>
<li><strong>Function deduplication</strong> – exact structural match, then fuzzy dedup (functions differing only in literal constants are parameterized and merged, up to 4 differing positions).</li>
<li><strong>Memory optimization</strong> – load-after-store elimination, keccak256 fusion (<code>mstore</code> + <code>keccak256</code> sequences into <code>Keccak256Single</code>/<code>Keccak256Pair</code> nodes), free memory pointer propagation (replaces <code>mload(0x40)</code> with a known constant), and constant keccak256 folding (precomputes hashes of compile-time-constant inputs).</li>
<li><strong>Simplify</strong> (pass 2) – cleans up dead code and new constant expressions exposed by memory optimization and keccak folding.</li>
<li><strong>Compound outlining</strong> – detects <code>keccak256_pair</code> + <code>sload</code>/<code>sstore</code> sequences and fuses them into <code>MappingSLoad</code>/<code>MappingSStore</code> IR nodes, eliminating intermediate hash values. <strong>Guard narrowing</strong> – detects <code>if gt(val, MASK) { revert }</code> and <code>iszero(eq(val, and(val, MASK)))</code> patterns and inserts AND-mask narrowing, giving type inference proof that values fit in fewer bits.</li>
<li><strong>Simplify</strong> (pass 3) – propagates opportunities created by compound outlining and guard narrowing.</li>
<li><strong>Function deduplication</strong> (pass 2) – catches new duplicates exposed by guard narrowing and compound outlining canonicalization.</li>
<li><strong>Type inference</strong> – narrows 256-bit values to smaller widths (<code>I1</code>, <code>I8</code>, <code>I32</code>, <code>I64</code>, <code>I128</code>, <code>I160</code>) where provable. Runs iteratively for up to 4 cascading refinement rounds, combining forward min-width propagation, backward use-context demands, transparent-operation demand propagation, and interprocedural parameter/return narrowing.</li>
<li><strong>Late inline loop</strong> – now that narrowing and simplification have shrunk wrapper functions below the inline thresholds, re-runs inlining, simplification, mapping access outlining, guard narrowing, deduplication, and type inference to collect the residual opportunities.</li>
<li><strong>Heap analysis</strong> – analyzes memory access patterns (alignment, static offsets, taintedness, escaping regions) to determine which accesses can use native little-endian layout, skipping byte-swap operations. Uses GCD-based alignment propagation and per-region taint tracking.</li>
<li><strong>Validation</strong> – checks SSA well-formedness (use-before-def, multiple definitions), yield count consistency, and function reference correctness.</li>
</ol>
<p>Steps 1-8 run recursively on subobjects (deployed contract code), where optimization impact is greatest. Steps 9-12 run on the full object tree.</p>
<h2 id="ir-design"><a class="header" href="#ir-design">IR design</a></h2>
<p>The newyork IR is an SSA form with structured control flow, inspired by <a href="https://mlir.llvm.org/docs/Dialects/SCFDialect/">MLIR’s SCF dialect</a>. Key design choices:</p>
<ul>
<li><strong>Explicit types with address spaces</strong>: Every value carries a bit-width (<code>I1</code>, <code>I8</code>, <code>I32</code>, <code>I64</code>, <code>I128</code>, <code>I160</code>, <code>I256</code>) and pointers carry address space information (<code>Heap</code>, <code>Stack</code>, <code>Storage</code>, <code>Code</code>). All values start as <code>I256</code> and are narrowed by type inference.</li>
<li><strong>Pure expressions vs. effectful statements</strong>: Expressions compute values without side effects; statements perform memory, storage, or control flow effects. This separation simplifies analysis and rewriting.</li>
<li><strong>Semantic annotations</strong>: Memory operations are tagged with region information (<code>Scratch</code>, <code>FreePointerSlot</code>, <code>Dynamic</code>). Storage operations carry static slot values when known at compile time.</li>
<li><strong>Structured control flow</strong>: <code>If</code>, <code>Switch</code>, and <code>For</code> nodes preserve the high-level structure from Yul, with explicit region arguments and yields for value flow across control edges.</li>
</ul>
<p>For per-operation detail — printed syntax, operand and result types, and more — see the <a href="#newyork-ir-reference">newyork IR reference</a>.</p>
<h2 id="key-optimizations-explained"><a class="header" href="#key-optimizations-explained">Key optimizations explained</a></h2>
<h3 id="type-narrowing"><a class="header" href="#type-narrowing">Type narrowing</a></h3>
<p>EVM operates on 256-bit words, but most values in practice fit in 32 or 64 bits. The type inference pass performs bidirectional analysis:</p>
<ul>
<li><strong>Forward</strong>: computes minimum width from literal values and operation semantics (e.g., <code>add(I64, I8)</code> produces <code>I65</code>, rounded up to <code>I128</code>).</li>
<li><strong>Backward use tracking</strong>: classifies each value’s uses into 9 context categories (<code>MemoryOffset</code>, <code>MemoryValue</code>, <code>StorageAccess</code>, <code>Comparison</code>, <code>Arithmetic</code>, <code>FunctionArg</code>, <code>FunctionReturn</code>, <code>ExternalCall</code>, <code>General</code>). All categories conservatively demand the full <code>I256</code> width by default; the categorization is what enables the interprocedural phase to selectively relax the demand for narrowed function arguments. Earlier versions narrowed directly from the use category, but that was unsound for memory offsets — <code>mload(2^128)</code> aliased to <code>mload(0)</code> because the bounds check ran on an already-truncated value (commit <code>ccca38df</code>).</li>
<li><strong>Transparent demand propagation</strong>: for modular-arithmetic operations (<code>Add</code>, <code>Sub</code>, <code>Mul</code>, <code>And</code>, <code>Or</code>, <code>Xor</code>), propagates narrow demands backward through operands, exploiting the property that <code>trunc(op(a,b), N) == op(trunc(a,N), trunc(b,N))</code>.</li>
<li><strong>Interprocedural</strong>: iteratively narrows function parameter and return types in up to four rounds, combining four narrowing strategies — body-driven parameter narrowing, caller-driven parameter narrowing, forward-based return narrowing, and demand-based return narrowing — and re-running full inference between rounds. Parameters are clamped to at least <code>I32</code> (XLEN on PolkaVM).</li>
</ul>
<p>This allows LLVM to emit native 32/64-bit instructions instead of software-emulated 256-bit arithmetic, and eliminates expensive multi-instruction comparison sequences (16-20 RISC-V instructions for i256 comparisons reduced to 1-2 for i64).</p>
<h3 id="guard-narrowing"><a class="header" href="#guard-narrowing">Guard narrowing</a></h3>
<p>Solidity emits runtime guards that prove values fit in narrow ranges (e.g., address validation via <code>if gt(val, 2^160-1) { revert }</code>). The guard narrowing pass detects these patterns and inserts explicit AND-mask narrowing after the guard. This gives downstream type inference proof that the value fits in fewer bits, enabling cascading narrowing of comparisons, arithmetic, and memory operations that use the guarded value.</p>
<p>Two pattern families are recognized:</p>
<ul>
<li><strong>GT-based guards</strong>: <code>if gt(val, MASK) { &lt;terminates&gt; }</code> where MASK is a boundary value like <code>2^N - 1</code></li>
<li><strong>EQ-based guards</strong>: <code>iszero(eq(val, and(val, MASK)))</code> patterns common in Solidity’s address validation</li>
</ul>
<h3 id="heap-optimization"><a class="header" href="#heap-optimization">Heap optimization</a></h3>
<p>PVM doesn’t provide EVM-compatible linear memory, so the compiler emulates it using a byte buffer with byte-swap operations for big-endian compatibility. The heap analysis pass determines which memory accesses can use native little-endian layout by analyzing access patterns:</p>
<ul>
<li>Tracks alignment and static offset information for all memory accesses using GCD-based propagation</li>
<li>Propagates taintedness when addresses escape to external calls, are written by external sources (<code>codecopy</code>, <code>calldatacopy</code>), or use unaligned access patterns</li>
<li>Tracks variable-accessed offsets to prevent mode mismatches between native and byte-swap accesses to the same location</li>
<li>Handles loop-carried variables conservatively (marked as non-literal to prevent false constant propagation)</li>
</ul>
<p>The codegen backend supports four memory access modes: <code>AllNative</code> (all accesses skip byte-swap), <code>InlineNative</code> (constant-offset accesses use native layout), <code>InlineByteSwap</code> (constant-offset accesses use inline byte-swap), and <code>ByteSwap</code> (standard byte-swap through helper functions).</p>
<h3 id="free-memory-pointer-range-proof"><a class="header" href="#free-memory-pointer-range-proof">Free memory pointer range proof</a></h3>
<p>The Solidity free memory pointer (<code>mload(0x40)</code>) always fits in 32 bits — sbrk enforces <code>FMP &lt; heap_size</code> on every store, regardless of which memory mode the contract uses. After every literal <code>mload(0x40)</code>, codegen emits a <code>trunc N → zext 256</code> chain (where N is <code>bits(heap_size - 1)</code>, e.g. 17 for the 131,072-byte default heap). The trunc-extend round-trip is a no-op semantically, but exposes the bound to LLVM’s IPSCCP range analysis, which then propagates it through every <code>add(fmp, K)</code> and eliminates the trailing <code>safe_truncate_int_to_xlen</code> overflow checks at every FMP-derived offset use. Despite only affecting a single codegen site, this is the single largest contributor to the optimizer’s code-size reduction.</p>
<p>A subtle gating issue: the byte-order mode (<code>InlineNative</code> / <code>ByteSwap</code>) and the value bound on FMP are <em>independent</em> invariants. <code>fmp_native_safe()</code> and <code>can_use_native(0x40)</code> protect against mixing little-endian writers with big-endian readers on the FMP slot, which would corrupt the stored offset; the value bound is unrelated and holds in every mode. Earlier versions of the codegen gated the load-side range proof on the byte-order checks, which suppressed the optimization for any contract with dynamic memory accesses. Decoupling the two reasonings — keeping the byte-order gate on the <em>store</em> side, dropping it from the load-side range proof — is what makes the multiplicative IPSCCP effect available to OZ-class contracts.</p>
<h3 id="soundness-traps-for-fmp-optimizations"><a class="header" href="#soundness-traps-for-fmp-optimizations">Soundness traps for FMP optimizations</a></h3>
<p>The FMP slot is small but easy to mis-optimize. The codebase carries several
regression tests for previously-found soundness bugs; new FMP-related changes
should be verified against them:</p>
<ul>
<li><strong><code>mload_at_fmp_slot</code></strong> (<code>crates/integration/src/tests.rs</code>, fixed in
<code>1fd6063c</code>): tests <code>mload(0x40)</code> and offsets near it (<code>0x21</code>, <code>0x3f</code>, <code>0x42</code>)
on a contract that also performs dynamic mloads. Catches byte-order mismatches
when one access goes native (LE) and another goes byte-swap (BE). The fix
blocks native mode for FMP whenever <code>has_dynamic_accesses</code> is true.</li>
<li><strong><code>mload_huge_offset_traps</code></strong> (fixed in <code>ccca38df</code>): tests that
<code>mload(2^128)</code> and <code>mload(2^255)</code> correctly trap via the gas-exhaustion
path. Catches <code>UseContext::MemoryOffset</code> narrowing bypassing the
<code>safe_truncate_int_to_xlen</code> overflow check at the use site —
<code>mload(2^128)</code> aliasing to <code>mload(0)</code> and returning the zero-initialized
scratch slot. The fix classifies <code>MemoryOffset</code> as <code>I256</code> so it doesn’t
drive narrowing; the bounds check at the use site catches out-of-range.</li>
<li><strong>FMP i32 shortcut removal</strong> (<code>dbcfc921</code>): an earlier optimization stored
only 4 bytes at offset 0x40 instead of the full 32-byte EVM word, breaking
any inline assembly using <code>mstore(0x40, ...)</code> for non-FMP purposes.
Caused a cascade of 249/251 retester failures via allocator corruption.
No dedicated regression test was added — the retester corpus was sufficient
coverage — but the lesson generalizes: writes to 0x40 must store the full
word, even when the high bits are provably zero, because the slot is part
of the same 32-byte memory region read by other code.</li>
</ul>
<p>When adding an optimization that touches FMP, distinguish carefully between:
the <strong>byte-order encoding</strong> at the slot (must be consistent between writers
and readers), the <strong>value bound</strong> (FMP &lt; heap_size, always true), and the
<strong>stored width</strong> (must be 32 bytes for <code>mstore(0x40, ...)</code>, even though only
the low N bits are non-zero).</p>
<h4 id="known-limitation-dynamic-full-word-stores-to-the-fmp-word"><a class="header" href="#known-limitation-dynamic-full-word-stores-to-the-fmp-word">Known limitation: dynamic full-word stores to the FMP word</a></h4>
<p>The <code>fmp_could_be_unbounded</code> analysis flags a static <code>mstore(0x40, untrusted)</code> and any
dynamic-offset <code>mstore8</code>, but <strong>not</strong> a dynamic-offset full-word <code>mstore</code>. Such a store whose
i256 offset wraps (mod 2²⁵⁶) to the FMP word <code>[0x40, 0x5f]</code> overwrites the free pointer with an
arbitrary value, which the load-side range proof would then truncate — a miscompile.</p>
<p>This is a deliberate, documented gap rather than a bug fix because there is no cheap sound
discriminator. A store hits the FMP word iff its offset lands in <code>[0x40, 0x5f]</code>, which is
in-bounds — <code>safe_truncate_int_to_xlen</code> only traps offsets <code>≥ heap_size</code> — and 256-bit wrap lets
any computed <code>add(base, k)</code> reach <code>0x40</code> with an adversarial operand, so the offset cannot be
proven to miss the slot from width/range information. The only sound recognizer (treat
<code>add(fmp, small_const)</code> as <code>≥ 0x80</code> by induction on FMP-boundedness) needs new FMP-derivation
dataflow, is fragile, and still misses dynamic-index array stores. Conservatively flagging <em>every</em>
dynamic full-word store (as the rare dynamic <code>mstore8</code> does, where it is free) disables the FMP
range proof for essentially every contract — measured at roughly <strong>+9% / +30 KB</strong> on the
OpenZeppelin corpus.</p>
<p>The gap is unreachable from solc output: solc’s dynamic memory stores are all free-pointer-relative
(<code>≥ 0x80</code>) and never target <code>0x40</code>. Only hand-written Yul (<code>resolc --yul</code>) with an offset
engineered to equal <code>0x40</code> reaches it.</p>
<h3 id="keccak256-fusion-and-folding"><a class="header" href="#keccak256-fusion-and-folding">Keccak256 fusion and folding</a></h3>
<p>Two complementary optimizations target the common Solidity pattern of hashing values for storage slot computation:</p>
<ul>
<li><strong>Fusion</strong>: Recognizes <code>mstore</code> + <code>keccak256</code> sequences and fuses them into dedicated IR nodes (<code>Keccak256Single</code>, <code>Keccak256Pair</code>), eliminating intermediate memory traffic.</li>
<li><strong>Constant folding</strong>: When all keccak256 inputs are compile-time constants, the hash is computed at compile time and replaced with a literal.</li>
</ul>
<h4 id="known-limitation-constant-folding-drops-the-fused-keccak-scratch-write-back"><a class="header" href="#known-limitation-constant-folding-drops-the-fused-keccak-scratch-write-back">Known limitation: constant-folding drops the fused-keccak scratch write-back</a></h4>
<p>The fused <code>Keccak256Pair</code>/<code>Keccak256Single</code> helpers write their inputs back to scratch memory
(<code>[0, 0x40)</code> / <code>[0, 0x20)</code>), and fusion <em>dead-eliminates the original <code>mstore</code>s</em> because that
write-back reproduces them. Constant-folding the fused node to a literal removes the helper, so the
scratch is left unwritten — a later <code>mload</code> from <code>[0, 0x40)</code> that the optimizer cannot forward
(across a region/call boundary) would read stale memory.</p>
<p>This gap is deliberately left open: it is solc-unreachable (solc treats scratch as volatile and never
re-reads it as data after a keccak), and every sound fix is a code-size regression because the dropped
write-back means the current output is already short the stores (disabling the fold falls back to the
runtime keccak helper, +0.78% on the OZ corpus, with no later mem_opt pass to clean re-emitted
writes). Only hand-written Yul that reads scratch after a constant-operand keccak across a boundary
can observe it.</p>
<h3 id="compound-outlining-mapping-access"><a class="header" href="#compound-outlining-mapping-access">Compound outlining (mapping access)</a></h3>
<p>Solidity mapping accesses follow a predictable pattern: hash a key with a storage slot, then load/store the result. The compound outlining pass detects <code>keccak256_pair(key, slot)</code> followed by <code>sload</code>/<code>sstore</code> and fuses them into <code>MappingSLoad</code>/<code>MappingSStore</code> IR nodes. These are lowered to outlined helper functions (<code>__revive_mapping_sload</code>, <code>__revive_mapping_sstore</code>) that combine the hash computation with the storage operation, eliminating intermediate values and redundant byte-swaps.</p>
<h3 id="fuzzy-function-deduplication"><a class="header" href="#fuzzy-function-deduplication">Fuzzy function deduplication</a></h3>
<p>Solidity generates many near-identical functions that differ only in literal constants (e.g., error selectors, storage slot offsets). Fuzzy deduplication identifies such groups, parameterizes the differing literals (up to 4 positions), and replaces all copies with calls to a single shared implementation.</p>
<h3 id="revert-pattern-outlining"><a class="header" href="#revert-pattern-outlining">Revert pattern outlining</a></h3>
<p>The simplify pass detects common revert patterns and replaces them with compact IR nodes:</p>
<ul>
<li><strong>Panic reverts</strong>: Solidity <code>Panic(uint256)</code> sequences (selector <code>0x4e487b71</code> + encoded panic code) are collapsed into <code>PanicRevert { code }</code> nodes, which are lowered to shared helper functions.</li>
<li><strong>Custom error reverts</strong>: ABI-encoded custom error reverts with known selectors are collapsed into <code>CustomErrorRevert { selector, args }</code> nodes.</li>
</ul>
<p>These patterns appear dozens of times in typical contracts, and outlining them into shared blocks eliminates significant code duplication.</p>
<h3 id="outlined-helper-functions"><a class="header" href="#outlined-helper-functions">Outlined helper functions</a></h3>
<p>The LLVM codegen backend generates approximately 15 types of outlined helper functions for common operations:</p>
<ul>
<li><strong>Storage</strong>: <code>__revive_sload_word</code>, <code>__revive_sstore_word</code> (handle byte-swap internally)</li>
<li><strong>Mapping</strong>: <code>__revive_mapping_sload</code>, <code>__revive_mapping_sstore</code> (keccak256 + storage in one call)</li>
<li><strong>Callvalue</strong>: <code>__revive_callvalue</code>, <code>__revive_callvalue_nonzero</code> (boolean optimization for non-payable checks)</li>
<li><strong>Calldataload</strong>: <code>__revive_calldataload</code> (outlined when &gt;= 20 call sites)</li>
<li><strong>Memory</strong>: <code>__revive_store_bswap</code>, <code>__revive_exit_checked</code>, <code>__revive_return_word</code></li>
<li><strong>Errors</strong>: <code>__revive_error_string_revert_N</code>, <code>__revive_custom_error_N</code> (per data-word count)</li>
<li><strong>Keccak wrappers</strong>: <code>__keccak256_slot_N</code> (one <code>noinline</code> wrapper per constant slot, internally dispatching to <code>__revive_keccak256_two_words</code>)</li>
</ul>
<p>Additionally, common exit patterns (revert with constant length, zero-value returns) are deduplicated into shared LLVM basic blocks, saving hundreds of instruction copies in large contracts.</p>
<h2 id="codesize-results"><a class="header" href="#codesize-results">Codesize results</a></h2>
<h3 id="integration-test-contracts"><a class="header" href="#integration-test-contracts">Integration test contracts</a></h3>
<p>Reproducible with <code>cargo test --package revive-integration -- codesize</code> for the <em>Via Yul IR</em> column (<code>crates/integration/codesize.json</code>) and <code>cargo test --package revive-integration --features newyork -- codesize</code> for the <em>Via newyork IR</em> column (<code>crates/integration/codesize_newyork.json</code>).</p>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Contract</th><th>Via Yul IR (bytes)</th><th>Via newyork IR (bytes)</th><th>Reduction</th></tr>
</thead>
<tbody>
<tr><td>Baseline</td><td>838</td><td>493</td><td>−41.2%</td></tr>
<tr><td>Computation</td><td>2,368</td><td>1,217</td><td>−48.6%</td></tr>
<tr><td>DivisionArithmetics</td><td>11,444</td><td>7,370</td><td>−35.6%</td></tr>
<tr><td>ERC20</td><td>18,057</td><td>8,726</td><td>−51.7%</td></tr>
<tr><td>Events</td><td>1,614</td><td>909</td><td>−43.7%</td></tr>
<tr><td>FibonacciIterative</td><td>1,373</td><td>969</td><td>−29.4%</td></tr>
<tr><td>Flipper</td><td>2,205</td><td>1,058</td><td>−52.0%</td></tr>
<tr><td>SHA1</td><td>7,830</td><td>6,264</td><td>−20.0%</td></tr>
</tbody>
</table>
</div>
<h3 id="openzeppelin-contracts"><a class="header" href="#openzeppelin-contracts">OpenZeppelin contracts</a></h3>
<p>Measured against real-world contracts generated with the OpenZeppelin Wizard. The numbers below are a development snapshot.</p>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Contract</th><th>Via newyork IR (bytes)</th></tr>
</thead>
<tbody>
<tr><td>oz_gov</td><td>81,840</td></tr>
<tr><td>erc721</td><td>52,634</td></tr>
<tr><td>erc20</td><td>45,703</td></tr>
<tr><td>oz_stable</td><td>45,052</td></tr>
<tr><td>oz_rwa</td><td>41,581</td></tr>
<tr><td>erc1155</td><td>33,087</td></tr>
<tr><td>oz_simple_erc20</td><td>17,024</td></tr>
<tr><td>proxy</td><td>3,748</td></tr>
<tr><td><strong>Total</strong></td><td><strong>320,669</strong></td></tr>
</tbody>
</table>
</div>
<p>For comparison, building the same contracts without the newyork optimizer at the equivalent snapshot produced <strong>563,526</strong> bytes total — a reduction of about <strong>−43%</strong> across the corpus.</p>
<p>Per-contract reductions in the integration suite range from roughly <strong>−20%</strong> (SHA1, where the bulk of the work is the SHA-1 inner loop and offers little to optimize) to about <strong>−52%</strong> (Flipper, where the optimizer strips away most of Solidity’s dispatch and storage-access scaffolding).</p>
<h2 id="development-history-and-challenges"><a class="header" href="#development-history-and-challenges">Development history and challenges</a></h2>
<p>The first version of the newyork optimizer was authored collaboratively and reviewed extensively by the <code>revive</code> maintainers, as well as Claude Opus, Claude Fable, Qwen, Minimax and Deepseek LLMs, over a span of many months — from early February 2026 through mid-June 2026.</p>
<p>The development progressed through several distinct phases:</p>
<p><strong>Phase 1 – Initial scaffolding</strong>: The first draft established the core IR data structures, Yul-to-newyork-IR translation, and LLVM codegen. Early commits focused on getting a correct round-trip through the new pipeline.</p>
<p><strong>Phase 2 – Optimization passes</strong>: Once the baseline was stable, optimization passes were added iteratively: inlining, simplification, memory optimization, function deduplication, keccak256 fusion, and type inference. Each pass was validated against differential tests comparing EVM and PVM execution.</p>
<p><strong>Phase 3 – Soundness hardening</strong>: Several type inference and narrowing approaches turned out to be unsound and had to be reworked:</p>
<ul>
<li>An early type inference approach caused namespace collisions across subobjects and was scoped per-object.</li>
<li>Caller-based parameter narrowing was polluted by overly aggressive inference and replaced with body-based structural analysis.</li>
<li>Backward demand-driven narrowing required multiple iterations to become provably safe.</li>
</ul>
<p><strong>Phase 4 – Measuring and tuning</strong>: Systematic measurement of OpenZeppelin contracts revealed which optimizations had the most impact and which approaches regressed performance.</p>
<p>Throughout development the optimizer was validated against the existing integration and differential test suites (containing over 30,000 test cases), which run each contract on both EVM and PVM and assert identical state changes.</p>
<p>The newyork compiler pipeline introduced no new regressions over these test suites. This was achieved by careful manual reviews and many LLM bughunt loops. Additionally, a final security review by Anthropic’s Fable 5 LLM found no remaining soundness issues. As with any new compiler feature, it should still be treated as experimental as of now.</p>
<h3 id="approaches-that-did-not-work"><a class="header" href="#approaches-that-did-not-work">Approaches that did not work</a></h3>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Approach</th><th>Outcome</th></tr>
</thead>
<tbody>
<tr><td>Storage bswap decomposition (4x bswap.i64)</td><td>Regressed: LLVM handles bswap.i256 better natively</td></tr>
<tr><td>NoInline on <code>__revive_int_truncate</code></td><td>+62% regression: PolkaVM call overhead exceeds inline cost</td></tr>
<tr><td>Native FMP memory (inline sbrk)</td><td>Mixed: small contracts improved, large ones regressed from sbrk bloat</td></tr>
<tr><td>Shared overflow trap block</td><td>Mixed: prevented LLVM from eliminating individual dead overflow checks</td></tr>
<tr><td>Aggressive IR-level single-call inlining</td><td>Regressed large contracts (ERC20 +6.1%): merged bodies become monolithic functions LLVM can’t optimize, so large functions are deferred to LLVM’s inliner instead</td></tr>
<tr><td>Type-inference narrowing of <code>mload(0x40)</code> to I32</td><td>Regressed small contracts (+252 bytes): conflicts with the codegen FMP range proof; the bound is exposed via a trunc→zext pair instead</td></tr>
<tr><td>Full simplifier re-run after <code>mem_opt</code></td><td>Mixed: helped small ERC20 (−293 bytes) but regressed the OZ stablecoin (+72 bytes); replaced by a targeted keccak-only fold</td></tr>
</tbody>
</table>
</div>
<p>These results highlight a recurring theme: interacting well with LLVM’s own optimization passes is critical. Optimizations at the IR level can inadvertently inhibit LLVM’s downstream passes, sometimes causing surprising regressions.</p>
<h2 id="known-limitations-and-future-work"><a class="header" href="#known-limitations-and-future-work">Known limitations and future work</a></h2>
<p>The following opportunities have been identified but are not yet implemented:</p>
<ul>
<li><strong>Memory optimization across loop boundaries</strong>: Tracked memory state is cleared around <code>for</code> loop condition, body, and post blocks, so load-after-store eliminations do not carry across loop iterations. Preserving loop-invariant state would recover more eliminations.</li>
<li><strong>Adaptive inlining thresholds</strong>: Current thresholds are static constants. Profile-guided or contract-size-aware heuristics could improve decisions for diverse contract sizes.</li>
<li><strong>Extended fuzzy deduplication</strong>: The current pass only parameterizes literals in <code>Let</code> bindings and <code>SStore</code> slots. Extending it to consider literals inside <code>MStore</code>, <code>Return</code>, <code>Revert</code>, and <code>Log</code> statements would find more deduplication opportunities.</li>
<li><strong>Type checking in validation</strong>: The validator checks SSA well-formedness and structure, but not operation type consistency. Type discipline is maintained by construction (type inference and codegen), with LLVM’s IR verifier as the backstop.</li>
<li><strong>Loop variable narrowing</strong>: Loop-carried variables are conservatively widened to <code>I256</code>. Reaching a fixed-point across loop iterations could allow narrower types for simple counters.</li>
<li><strong>Functions with <code>leave</code> inside a <code>for</code> loop are not inlined</strong>: the IR-level inliner defers such functions to LLVM’s inliner, so they miss the interprocedural constant propagation and width narrowing the IR-level pass provides.</li>
</ul>
<h2 id="debug-output"><a class="header" href="#debug-output">Debug output</a></h2>
<p>Passing <code>--debug-output-dir &lt;path&gt;</code> makes the newyork pipeline write IR and analysis artifacts for each compiled contract into that directory. The dumps are produced automatically whenever the directory is set.</p>
<div class="table-wrapper">
<table>
<thead>
<tr><th>File</th><th>Content</th></tr>
</thead>
<tbody>
<tr><td><code>&lt;file-stem&gt;.newyork</code></td><td>Final optimized IR, annotated with the inferred type widths</td></tr>
<tr><td><code>&lt;file-stem&gt;.snapshot.newyork</code></td><td>IR snapshot taken before the late passes (only when captured during translation)</td></tr>
<tr><td><code>&lt;file-stem&gt;.heap.newyork</code></td><td>Heap analysis summary (native regions/offsets, taintedness, escapes, dynamic accesses)</td></tr>
<tr><td><code>&lt;file-stem&gt;.mem.newyork</code></td><td>Memory optimization counters (loads/stores eliminated, keccak fusions, FMP loads eliminated)</td></tr>
</tbody>
</table>
</div>
<h2 id="module-reference"><a class="header" href="#module-reference">Module reference</a></h2>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Module</th><th>Purpose</th></tr>
</thead>
<tbody>
<tr><td><code>lib.rs</code></td><td>Pipeline orchestration and pass sequencing</td></tr>
<tr><td><code>ir.rs</code></td><td>Core IR data structures (types, expressions, statements, functions, objects)</td></tr>
<tr><td><code>from_yul.rs</code></td><td>Yul AST to newyork IR translation (two-pass with forward reference support)</td></tr>
<tr><td><code>to_llvm.rs</code></td><td>newyork IR to LLVM IR codegen with outlined helpers and narrowing</td></tr>
<tr><td><code>simplify.rs</code></td><td>Constant folding, algebraic identities, strength reduction, copy propagation, DCE, environment read CSE, revert outlining, callvalue hoisting, function deduplication (exact and fuzzy), constant keccak folding</td></tr>
<tr><td><code>inline.rs</code></td><td>Function inlining with PolkaVM-tuned heuristics (Tarjan SCC, leave elimination)</td></tr>
<tr><td><code>type_inference.rs</code></td><td>Bidirectional integer width narrowing with transparent demand propagation</td></tr>
<tr><td><code>mem_opt.rs</code></td><td>Load-after-store elimination, keccak256 fusion, FMP propagation</td></tr>
<tr><td><code>heap_opt.rs</code></td><td>Heap access pattern analysis, alignment tracking, byte-swap elimination</td></tr>
<tr><td><code>mapping_access_outlining.rs</code></td><td>Mapping access pattern detection and fusion (<code>keccak256_pair</code> + <code>sload</code>/<code>sstore</code>)</td></tr>
<tr><td><code>guard_narrow.rs</code></td><td>Guard pattern detection and AND-mask narrowing insertion</td></tr>
<tr><td><code>validate.rs</code></td><td>IR well-formedness checks (SSA, yields, function references)</td></tr>
<tr><td><code>printer.rs</code></td><td>Human-readable IR pretty printer with configurable output</td></tr>
<tr><td><code>ssa.rs</code></td><td>SSA construction helpers (scope management, phi-node merging)</td></tr>
</tbody>
</table>
</div>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="newyork-ir-reference"><a class="header" href="#newyork-ir-reference">newyork IR reference</a></h1>
<p>A per-operation reference for the newyork IR: textual syntax, operand and result types, purity, region and static-slot annotations, and examples.</p>
<h2 id="how-to-read-this-reference"><a class="header" href="#how-to-read-this-reference">How to read this reference</a></h2>
<p>This reference page enumerates every operation the newyork IR supports. It is a lookup, not a walkthrough: each entry is self-contained and intended to be reachable by anchor.</p>
<p>Operations are grouped by function (memory and storage writes, pure expressions, control flow, and so on) rather than alphabetically. Jump to a specific operation from the <a href="#operation-index">operation index</a> below, or use the sidebar.</p>
<p>Every operation appears in two places in the codebase. The canonical Rust definition is a variant of either <code>Expression</code> or <code>Statement</code> in <code>ir.rs</code>. The textual rendering used by debug dumps and by this reference page is produced by the printer in <code>printer.rs</code>.</p>
<blockquote class="blockquote-tag blockquote-tag-note">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
<p>Treat the printed syntax as a debug surface, not a stable input language: there is no parser for it, and printer details change when passes add new annotations.</p>
</blockquote>
<h3 id="entry-format"><a class="header" href="#entry-format">Entry format</a></h3>
<p>Each operation entry has the same shape:</p>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Field</th><th>What it shows</th></tr>
</thead>
<tbody>
<tr><td><strong>Heading</strong></td><td>The printed operation name (e.g. <code>mstore</code>) followed by the <code>Expression</code> or <code>Statement</code> variant it corresponds to in <code>ir.rs</code>.</td></tr>
<tr><td><strong>Description</strong></td><td>A short prose summary of what the operation does and any semantic notes worth knowing before reading the rest of the entry.</td></tr>
<tr><td><strong>Syntax</strong></td><td>The literal printer output, including any optional debug annotations (region tags, static-slot comments). Anything inside <code>/* ... */</code> is a debug-only annotation and is not part of the operation itself.</td></tr>
<tr><td><strong>Example</strong></td><td>A minimal printed snippet, using the printer’s actual <code>v0</code>/<code>v1</code>/… naming.</td></tr>
<tr><td><strong>Operands</strong></td><td>One row per input or structural participant in the printed syntax. Value operands list the narrowest type the operation guarantees (default <code>i256</code>; narrower widths only appear when type inference has narrowed an upstream definition). Vector-of-operands fields show <code>Vec&lt;…&gt;</code> as the type. Non-value participants such as nested regions are listed with an em-dash type to mark them as structural rather than as operands.</td></tr>
<tr><td><strong>Result and purity</strong></td><td>The type the operation produces (or <em>none</em> for statements that bind no value), followed by a purity label, either <em>Pure</em> or <em>Effectful</em>. Pure operations may be reordered, deduplicated, or eliminated by the simplifier; effectful ones may not. Effectful entries may carry a parenthetical describing the nature of the side effect when informative (e.g. “control flow”, “terminator”, or a note about revert/trap behavior).</td></tr>
<tr><td><strong>Annotations</strong></td><td>Operation-specific fields the printer surfaces as <code>/* ... */</code> comments in the dump (region tag for memory ops, static-slot hint for storage ops, type suffix for non-default widths). Listed here as a table of <em>source field</em> → <em>printed form</em>.</td></tr>
</tbody>
</table>
</div>
<h3 id="syntax-notation"><a class="header" href="#syntax-notation">Syntax notation</a></h3>
<p>Syntax templates in each entry use the following conventions:</p>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Notation</th><th>Meaning</th></tr>
</thead>
<tbody>
<tr><td><code>add</code>, <code>mload</code>, <code>if</code>, <code>else</code>, <code>case</code>, <code>let</code>, <code>yield</code>, …</td><td>Literal printer tokens: bare lowercase identifiers and keywords that the printer emits verbatim.</td></tr>
<tr><td><code>$offset</code>, <code>$value</code>, <code>$key</code>, <code>$lhs</code>, <code>$rhs</code>, …</td><td>Role names (<code>$</code>-prefixed): placeholders for SSA value references the printer renders as <code>v</code> followed by a decimal id (<code>v0</code>, <code>v1</code>, …).</td></tr>
<tr><td><code>&lt;type&gt;</code>, <code>&lt;region&gt;</code>, <code>&lt;hex&gt;</code>, <code>&lt;id&gt;</code>, <code>&lt;bits&gt;</code>, <code>&lt;func_name&gt;</code>, <code>&lt;N&gt;</code>, <code>&lt;length&gt;</code>, …</td><td>Metavariables: stand for compile-time fields (type tags, hex values, identifier strings, integer counts), not SSA values. The concrete values they take are enumerated in the Annotations section of each entry or in the type system reference.</td></tr>
<tr><td><code>[…]</code></td><td>Optional parts. Anything inside the brackets may or may not appear in any given dump, depending on the conditions described in the operation’s Annotations section.</td></tr>
<tr><td><code>[: &lt;type&gt;]</code></td><td>Optional type suffix on a value reference. Suppressed when the value’s type is the default <code>i256</code> integer; present otherwise (<code>: i32</code>, <code>: ptr&lt;heap&gt;</code>, …).</td></tr>
<tr><td><code>/* … */</code></td><td>Debug-only annotations the printer attaches to certain operations (memory region tag, static-slot hint, etc.).</td></tr>
<tr><td><code>…</code></td><td>Repetition: “more entries of the same shape.” Used in vector operand lists (<code>$arg_0, $arg_1, …</code>) and in multi-line block bodies (<code>{ … }</code>).</td></tr>
</tbody>
</table>
</div>
<p>For instance, this template:</p>
<pre><code class="language-text">let $result[: &lt;type&gt;] := and($lhs[: &lt;type&gt;], $rhs[: &lt;type&gt;])
</code></pre>
<p>prints as:</p>
<pre><code class="language-text">let v2: i8 := and(v0, v1: i8)
</code></pre>
<p><code>$result</code> rendered as <code>v2</code> with an <code>i8</code> type suffix, <code>$lhs</code> as <code>v0</code> at the default <code>i256</code> (type suffix omitted), and <code>$rhs</code> as <code>v1</code> with an <code>i8</code> type suffix.</p>
<blockquote class="blockquote-tag blockquote-tag-note">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
<p><strong>A value’s printed width is use-driven.</strong> Type inference assigns each value a forward width from its definition, then widens it to satisfy its uses. The type suffix shown for a value in an example (such as <code>i8</code>) is therefore only illustrative — a short example may not show the uses that determine it, and the same operation can appear with a wider suffix, or none (it is omitted for the default <code>i256</code>), in another program.</p>
<p>For instance, a value used as a memory offset widens to <code>i64</code>; as an address (a <code>call</code> target, <code>extcodesize</code>) to <code>i160</code>; stored as a full word (an <code>mstore</code>/<code>sstore</code> value) to <code>i256</code>; and an <code>add</code>/<code>mul</code> operand up to the <code>i64</code> register width.</p>
</blockquote>
<h3 id="operation-index"><a class="header" href="#operation-index">Operation index</a></h3>
<h4 id="pure-expressions"><a class="header" href="#pure-expressions">Pure expressions</a></h4>
<h5 id="constants-and-variables"><a class="header" href="#constants-and-variables">Constants and variables</a></h5>
<ul>
<li><a href="#0xhex"><code>0x&lt;hex&gt;</code></a></li>
<li><a href="#vid"><code>v&lt;id&gt;</code></a></li>
</ul>
<h5 id="arithmetic"><a class="header" href="#arithmetic">Arithmetic</a></h5>
<ul>
<li><a href="#add"><code>add</code></a></li>
<li><a href="#sub"><code>sub</code></a></li>
<li><a href="#mul"><code>mul</code></a></li>
<li><a href="#div"><code>div</code></a></li>
<li><a href="#sdiv"><code>sdiv</code></a></li>
<li><a href="#mod"><code>mod</code></a></li>
<li><a href="#smod"><code>smod</code></a></li>
<li><a href="#exp"><code>exp</code></a></li>
<li><a href="#and"><code>and</code></a></li>
<li><a href="#or"><code>or</code></a></li>
<li><a href="#xor"><code>xor</code></a></li>
<li><a href="#shl"><code>shl</code></a></li>
<li><a href="#shr"><code>shr</code></a></li>
<li><a href="#sar"><code>sar</code></a></li>
<li><a href="#lt"><code>lt</code></a></li>
<li><a href="#gt"><code>gt</code></a></li>
<li><a href="#slt"><code>slt</code></a></li>
<li><a href="#sgt"><code>sgt</code></a></li>
<li><a href="#eq"><code>eq</code></a></li>
<li><a href="#byte"><code>byte</code></a></li>
<li><a href="#signextend"><code>signextend</code></a></li>
<li><a href="#addmod"><code>addmod</code></a></li>
<li><a href="#mulmod"><code>mulmod</code></a></li>
<li><a href="#iszero"><code>iszero</code></a></li>
<li><a href="#not"><code>not</code></a></li>
<li><a href="#clz"><code>clz</code></a></li>
</ul>
<h5 id="bit-width-conversions"><a class="header" href="#bit-width-conversions">Bit-width conversions</a></h5>
<ul>
<li><a href="#truncateibits"><code>truncate&lt;i&lt;bits&gt;&gt;</code></a></li>
<li><a href="#zextibits"><code>zext&lt;i&lt;bits&gt;&gt;</code></a></li>
<li><a href="#sextibits"><code>sext&lt;i&lt;bits&gt;&gt;</code></a></li>
</ul>
<h5 id="hashing"><a class="header" href="#hashing">Hashing</a></h5>
<ul>
<li><a href="#keccak256"><code>keccak256</code></a></li>
<li><a href="#keccak256_pair"><code>keccak256_pair</code></a></li>
<li><a href="#keccak256_single"><code>keccak256_single</code></a></li>
</ul>
<h5 id="environment-reads"><a class="header" href="#environment-reads">Environment reads</a></h5>
<ul>
<li><a href="#caller"><code>caller</code></a></li>
<li><a href="#callvalue"><code>callvalue</code></a></li>
<li><a href="#origin"><code>origin</code></a></li>
<li><a href="#address"><code>address</code></a></li>
<li><a href="#chainid"><code>chainid</code></a></li>
<li><a href="#gas"><code>gas</code></a></li>
<li><a href="#msize"><code>msize</code></a></li>
<li><a href="#coinbase"><code>coinbase</code></a></li>
<li><a href="#timestamp"><code>timestamp</code></a></li>
<li><a href="#number"><code>number</code></a></li>
<li><a href="#difficulty"><code>difficulty</code></a></li>
<li><a href="#gaslimit"><code>gaslimit</code></a></li>
<li><a href="#basefee"><code>basefee</code></a></li>
<li><a href="#blobbasefee"><code>blobbasefee</code></a></li>
<li><a href="#blobhash"><code>blobhash</code></a></li>
<li><a href="#blockhash"><code>blockhash</code></a></li>
<li><a href="#selfbalance"><code>selfbalance</code></a></li>
<li><a href="#gasprice"><code>gasprice</code></a></li>
</ul>
<h5 id="calldata-returndata-and-code"><a class="header" href="#calldata-returndata-and-code">Calldata, returndata, and code</a></h5>
<ul>
<li><a href="#calldataload"><code>calldataload</code></a></li>
<li><a href="#calldatasize"><code>calldatasize</code></a></li>
<li><a href="#returndatasize"><code>returndatasize</code></a></li>
<li><a href="#codesize"><code>codesize</code></a></li>
<li><a href="#extcodesize"><code>extcodesize</code></a></li>
<li><a href="#extcodehash"><code>extcodehash</code></a></li>
<li><a href="#balance"><code>balance</code></a></li>
</ul>
<h5 id="memory-and-storage-loads"><a class="header" href="#memory-and-storage-loads">Memory and storage loads</a></h5>
<ul>
<li><a href="#mload"><code>mload</code></a></li>
<li><a href="#sload"><code>sload</code></a></li>
<li><a href="#tload"><code>tload</code></a></li>
<li><a href="#mapping_sload"><code>mapping_sload</code></a></li>
</ul>
<h5 id="linker"><a class="header" href="#linker">Linker</a></h5>
<ul>
<li><a href="#dataoffset-1"><code>dataoffset</code></a></li>
<li><a href="#datasize-1"><code>datasize</code></a></li>
<li><a href="#loadimmutable"><code>loadimmutable</code></a></li>
<li><a href="#linkersymbol"><code>linkersymbol</code></a></li>
</ul>
<h5 id="function-call"><a class="header" href="#function-call">Function call</a></h5>
<ul>
<li><a href="#func_name"><code>&lt;func_name&gt;</code></a></li>
</ul>
<h4 id="memory-and-storage-writes"><a class="header" href="#memory-and-storage-writes">Memory and storage writes</a></h4>
<ul>
<li><a href="#mstore"><code>mstore</code></a></li>
<li><a href="#mstore8"><code>mstore8</code></a></li>
<li><a href="#mcopy"><code>mcopy</code></a></li>
<li><a href="#sstore"><code>sstore</code></a></li>
<li><a href="#tstore"><code>tstore</code></a></li>
<li><a href="#mapping_sstore"><code>mapping_sstore</code></a></li>
</ul>
<h4 id="bulk-copies"><a class="header" href="#bulk-copies">Bulk copies</a></h4>
<ul>
<li><a href="#codecopy-1"><code>codecopy</code></a></li>
<li><a href="#extcodecopy"><code>extcodecopy</code></a></li>
<li><a href="#returndatacopy"><code>returndatacopy</code></a></li>
<li><a href="#datacopy"><code>datacopy</code></a></li>
<li><a href="#calldatacopy"><code>calldatacopy</code></a></li>
</ul>
<h4 id="bindings-and-wrappers"><a class="header" href="#bindings-and-wrappers">Bindings and wrappers</a></h4>
<ul>
<li><a href="#let"><code>let</code></a></li>
<li><a href="#expression-statement">expression statement</a></li>
<li><a href="#setimmutable"><code>setimmutable</code></a></li>
</ul>
<h4 id="structured-control-flow"><a class="header" href="#structured-control-flow">Structured control flow</a></h4>
<ul>
<li><a href="#if"><code>if</code></a></li>
<li><a href="#switch"><code>switch</code></a></li>
<li><a href="#for"><code>for</code></a></li>
<li><a href="#break"><code>break</code></a></li>
<li><a href="#continue"><code>continue</code></a></li>
<li><a href="#leave"><code>leave</code></a></li>
<li><a href="#nested-block">nested block</a></li>
</ul>
<h4 id="external-interaction"><a class="header" href="#external-interaction">External interaction</a></h4>
<ul>
<li><a href="#call"><code>call</code></a></li>
<li><a href="#callcode"><code>callcode</code></a></li>
<li><a href="#delegatecall"><code>delegatecall</code></a></li>
<li><a href="#staticcall"><code>staticcall</code></a></li>
<li><a href="#create"><code>create</code></a></li>
<li><a href="#create2"><code>create2</code></a></li>
<li><a href="#logn"><code>log&lt;N&gt;</code></a></li>
</ul>
<h4 id="termination"><a class="header" href="#termination">Termination</a></h4>
<ul>
<li><a href="#return"><code>return</code></a></li>
<li><a href="#revert"><code>revert</code></a></li>
<li><a href="#stop"><code>stop</code></a></li>
<li><a href="#invalid"><code>invalid</code></a></li>
<li><a href="#selfdestruct"><code>selfdestruct</code></a></li>
<li><a href="#panic_revert"><code>panic_revert</code></a></li>
<li><a href="#error_string_revert"><code>error_string_revert</code></a></li>
<li><a href="#custom_error_revert"><code>custom_error_revert</code></a></li>
</ul>
<h2 id="type-system"><a class="header" href="#type-system">Type system</a></h2>
<p>Every value in the IR carries a <code>Type</code>. The operation entries below refer to widths (<code>i1</code>…<code>i256</code>), address spaces (<code>ptr&lt;heap&gt;</code>, etc.), and memory regions (<code>scratch</code>, etc.) by their printed form; this section is the reference for those names.</p>
<h3 id="type"><a class="header" href="#type"><code>Type</code></a></h3>
<p>The umbrella enum, with these variants:</p>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Variant</th><th>Printed as</th><th>Description</th></tr>
</thead>
<tbody>
<tr><td><code>Int(BitWidth)</code></td><td><code>i1</code>, <code>i8</code>, …, <code>i256</code></td><td>An integer at one of the <a href="#bitwidth">BitWidth</a> widths.</td></tr>
<tr><td><code>Ptr(AddressSpace)</code></td><td><code>ptr&lt;heap&gt;</code>, <code>ptr&lt;stack&gt;</code>, <code>ptr&lt;storage&gt;</code>, <code>ptr&lt;code&gt;</code></td><td>A pointer tagged with its address space; see <a href="#addressspace">AddressSpace</a>.</td></tr>
<tr><td><code>Void</code></td><td><code>void</code></td><td>Unit type. Used for statements that produce no value and for <code>void</code>-returning functions.</td></tr>
</tbody>
</table>
</div>
<h3 id="bitwidth"><a class="header" href="#bitwidth"><code>BitWidth</code></a></h3>
<p>The rungs of integer width. Newly minted values default to <code>I256</code>; type inference narrows them down to one of the lower rungs when it can prove the upper bits are zero or unused.</p>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Variant</th><th>Printed as</th><th>Typical use</th></tr>
</thead>
<tbody>
<tr><td><code>I1</code></td><td><code>i1</code></td><td>Boolean. Result type of every comparison and <code>iszero</code>.</td></tr>
<tr><td><code>I8</code></td><td><code>i8</code></td><td>Byte values. The narrowest meaningful integer.</td></tr>
<tr><td><code>I32</code></td><td><code>i32</code></td><td>PolkaVM pointer width (XLEN); minimum width for function parameters under the rv64e ABI.</td></tr>
<tr><td><code>I64</code></td><td><code>i64</code></td><td>PolkaVM native register width; most narrowed values land here.</td></tr>
<tr><td><code>I128</code></td><td><code>i128</code></td><td>Two registers; arithmetic that overflows <code>i64</code> but doesn’t need full 256-bit emulation.</td></tr>
<tr><td><code>I160</code></td><td><code>i160</code></td><td>Ethereum addresses; result of <code>caller</code>, <code>origin</code>, mapping keys.</td></tr>
<tr><td><code>I256</code></td><td><code>i256</code></td><td>EVM word width. The default and conservative ceiling.</td></tr>
</tbody>
</table>
</div>
<h3 id="addressspace"><a class="header" href="#addressspace"><code>AddressSpace</code></a></h3>
<p>The address space a pointer points into. Carried on every <code>Ptr</code> value so the codegen can lower loads and stores without a separate alias-analysis pass.</p>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Variant</th><th>Printed as</th><th>Points into</th><th>Endianness</th></tr>
</thead>
<tbody>
<tr><td><code>Heap</code></td><td><code>ptr&lt;heap&gt;</code></td><td>Emulated EVM linear memory (the simulated <code>mload</code>/<code>mstore</code> region).</td><td>Big-endian (by EVM contract).</td></tr>
<tr><td><code>Stack</code></td><td><code>ptr&lt;stack&gt;</code></td><td>Native PolkaVM stack allocations.</td><td>Little-endian (no swap).</td></tr>
<tr><td><code>Storage</code></td><td><code>ptr&lt;storage&gt;</code></td><td>Contract storage; key/value with 256-bit slots.</td><td>Big-endian on the wire.</td></tr>
<tr><td><code>Code</code></td><td><code>ptr&lt;code&gt;</code></td><td>Read-only code/data segment.</td><td>Big-endian.</td></tr>
</tbody>
</table>
</div>
<h3 id="memoryregion"><a class="header" href="#memoryregion"><code>MemoryRegion</code></a></h3>
<p>A refinement carried by every memory load and store on top of <code>AddressSpace::Heap</code>. The tag tells later passes what kind of heap address an offset is hitting, which drives both free-memory-pointer propagation and byte-swap elimination.</p>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Variant</th><th>Address range</th><th>Printed as</th><th>Meaning</th></tr>
</thead>
<tbody>
<tr><td><code>Scratch</code></td><td><code>0x00</code>–<code>0x3f</code></td><td><code>/* scratch */</code></td><td>EVM scratch space; safe to touch without consulting the free memory pointer.</td></tr>
<tr><td><code>FreePointerSlot</code></td><td>exactly <code>0x40</code></td><td><code>/* free_ptr */</code></td><td>Slot that stores the free memory pointer itself.</td></tr>
<tr><td><code>Dynamic</code></td><td><code>0x80</code> and above</td><td><code>/* dynamic */</code></td><td>Real heap allocations.</td></tr>
<tr><td><code>Unknown</code></td><td>everything else (constants in <code>0x41</code>–<code>0x7f</code>, plus all non-constant offsets)</td><td>(suppressed)</td><td>Conservative fallback used when the offset isn’t a constant or doesn’t slot cleanly.</td></tr>
</tbody>
</table>
</div>
<h2 id="pure-expressions-1"><a class="header" href="#pure-expressions-1">Pure expressions</a></h2>
<p>Pure expressions produce values without side effects. The simplifier may freely reorder, deduplicate, and eliminate them. They appear on the right-hand side of a <code>let</code> binding, or as operands of other expressions and effectful statements; the operand positions accept SSA value references only, so any pure expression that is consumed elsewhere is first bound by a <code>let</code>. Examples in this section wrap each expression in a <code>let v := …</code> to give it somewhere to land.</p>
<h3 id="0xhex"><a class="header" href="#0xhex"><code>0x&lt;hex&gt;</code></a></h3>
<p>(<code>Expression::Literal</code>)</p>
<h4 id="description"><a class="header" href="#description">Description</a></h4>
<p>A compile-time constant value with a declared type. New literals minted by the translator default to <code>Int(I256)</code>; passes that synthesize constants at narrower widths (e.g. a one-bit boolean from a constant comparison) attach the narrower type directly.</p>
<h4 id="syntax"><a class="header" href="#syntax">Syntax</a></h4>
<pre><code class="language-text">0x&lt;hex&gt;[: &lt;type&gt;]
</code></pre>
<h4 id="example-1"><a class="header" href="#example-1">Example</a></h4>
<pre><code class="language-text">let v0: i8 := 0x2a
let v1: i1 := 0x1 // boolean true
let v2: i64 := 0x80
</code></pre>
<h4 id="operands"><a class="header" href="#operands">Operands</a></h4>
<p>None — literals are leaves.</p>
<h4 id="result-and-purity"><a class="header" href="#result-and-purity">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>Same as the literal’s <code>value_type</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations"><a class="header" href="#annotations">Annotations</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Source field</th><th>Printed as</th></tr>
</thead>
<tbody>
<tr><td><code>value: BigUint</code></td><td><code>0x&lt;hex&gt;</code> in the syntax position (not a comment annotation; it is the expression itself)</td></tr>
<tr><td><code>value_type:</code> <a href="#type"><code>Type</code></a></td><td><code>: &lt;type&gt;</code> suffix when <code>value_type</code> is not the default <code>Int(I256)</code>; suppressed otherwise</td></tr>
</tbody>
</table>
</div>
<h3 id="vid"><a class="header" href="#vid"><code>v&lt;id&gt;</code></a></h3>
<p>(<code>Expression::Var</code>)</p>
<h4 id="description-1"><a class="header" href="#description-1">Description</a></h4>
<p>A reference to an existing SSA value, used as the entire right-hand side of a <code>let</code>. In a typical dump this is rare because the simplifier collapses <code>let v := v&lt;id&gt;</code> into the consumers of <code>v</code> via copy propagation; expect to see it only in dumps taken before simplification has run.</p>
<h4 id="syntax-1"><a class="header" href="#syntax-1">Syntax</a></h4>
<pre><code class="language-text">v&lt;id&gt;
</code></pre>
<h4 id="example-1-1"><a class="header" href="#example-1-1">Example</a></h4>
<pre><code class="language-text">let v5 := v3 // copy; usually eliminated by simplify
</code></pre>
<h4 id="operands-1"><a class="header" href="#operands-1">Operands</a></h4>
<p>None — the expression is the value reference itself.</p>
<h4 id="result-and-purity-1"><a class="header" href="#result-and-purity-1">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>Same as the referenced value’s type</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-1"><a class="header" href="#annotations-1">Annotations</a></h4>
<p>None.</p>
<h3 id="add"><a class="header" href="#add"><code>add</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Add</code>)</p>
<h4 id="description-2"><a class="header" href="#description-2">Description</a></h4>
<p>Modular addition. Wraps on overflow; per EVM, the result is <code>(lhs + rhs) mod 2^N</code> where <code>N</code> is the operand width.</p>
<h4 id="syntax-2"><a class="header" href="#syntax-2">Syntax</a></h4>
<pre><code class="language-text">add($lhs[: &lt;type&gt;], $rhs[: &lt;type&gt;])
</code></pre>
<h4 id="example-2"><a class="header" href="#example-2">Example</a></h4>
<pre><code class="language-text">let v2 := add(v0, v1)
</code></pre>
<h4 id="operands-2"><a class="header" href="#operands-2">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>lhs</code></td><td><code>i256</code></td><td>—</td></tr>
<tr><td><code>rhs</code></td><td><code>i256</code></td><td>—</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-2"><a class="header" href="#result-and-purity-2">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>widen_by_one(max(width(lhs), width(rhs)))</code> — one tier above the wider operand to account for the carry bit</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-2"><a class="header" href="#annotations-2">Annotations</a></h4>
<p>None.</p>
<h3 id="sub"><a class="header" href="#sub"><code>sub</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Sub</code>)</p>
<h4 id="description-3"><a class="header" href="#description-3">Description</a></h4>
<p>Modular subtraction. Wraps on underflow; the result is <code>(lhs - rhs) mod 2^256</code> regardless of operand widths.</p>
<h4 id="syntax-3"><a class="header" href="#syntax-3">Syntax</a></h4>
<pre><code class="language-text">sub($lhs[: &lt;type&gt;], $rhs[: &lt;type&gt;])
</code></pre>
<h4 id="example-3"><a class="header" href="#example-3">Example</a></h4>
<pre><code class="language-text">let v2 := sub(v0, v1)
</code></pre>
<h4 id="operands-3"><a class="header" href="#operands-3">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>lhs</code></td><td><code>i256</code></td><td>—</td></tr>
<tr><td><code>rhs</code></td><td><code>i256</code></td><td>—</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-3"><a class="header" href="#result-and-purity-3">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code> — conservative; underflow on narrower operands could borrow into upper bits</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-3"><a class="header" href="#annotations-3">Annotations</a></h4>
<p>None.</p>
<h3 id="mul"><a class="header" href="#mul"><code>mul</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Mul</code>)</p>
<h4 id="description-4"><a class="header" href="#description-4">Description</a></h4>
<p>Modular multiplication. The result is <code>(lhs * rhs) mod 2^256</code>.</p>
<h4 id="syntax-4"><a class="header" href="#syntax-4">Syntax</a></h4>
<pre><code class="language-text">mul($lhs[: &lt;type&gt;], $rhs[: &lt;type&gt;])
</code></pre>
<h4 id="example-4"><a class="header" href="#example-4">Example</a></h4>
<pre><code class="language-text">let v2 := mul(v0, v1)
</code></pre>
<h4 id="operands-4"><a class="header" href="#operands-4">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>lhs</code></td><td><code>i256</code></td><td>—</td></tr>
<tr><td><code>rhs</code></td><td><code>i256</code></td><td>—</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-4"><a class="header" href="#result-and-purity-4">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>double_width(max(width(lhs), width(rhs)))</code> — the tier holding twice the wider operand’s bits (skipping <code>i160</code> at the <code>i128</code> → <code>i256</code> transition)</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-4"><a class="header" href="#annotations-4">Annotations</a></h4>
<p>None.</p>
<h3 id="div"><a class="header" href="#div"><code>div</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Div</code>)</p>
<h4 id="description-5"><a class="header" href="#description-5">Description</a></h4>
<p>Unsigned integer division. Per EVM, <code>div(x, 0) = 0</code> (no trap on division by zero).</p>
<h4 id="syntax-5"><a class="header" href="#syntax-5">Syntax</a></h4>
<pre><code class="language-text">div($lhs[: &lt;type&gt;], $rhs[: &lt;type&gt;])
</code></pre>
<h4 id="example-5"><a class="header" href="#example-5">Example</a></h4>
<pre><code class="language-text">let v2 := div(v0, v1)
</code></pre>
<h4 id="operands-5"><a class="header" href="#operands-5">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>lhs</code></td><td><code>i256</code></td><td>Dividend.</td></tr>
<tr><td><code>rhs</code></td><td><code>i256</code></td><td>Divisor; <code>0</code> yields a result of <code>0</code>, not a trap.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-5"><a class="header" href="#result-and-purity-5">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>width(lhs)</code> — the quotient cannot exceed the dividend</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-5"><a class="header" href="#annotations-5">Annotations</a></h4>
<p>None.</p>
<h3 id="sdiv"><a class="header" href="#sdiv"><code>sdiv</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::SDiv</code>)</p>
<h4 id="description-6"><a class="header" href="#description-6">Description</a></h4>
<p>Signed two’s-complement integer division. Per EVM, <code>sdiv(x, 0) = 0</code>; quotient is truncated toward zero.</p>
<h4 id="syntax-6"><a class="header" href="#syntax-6">Syntax</a></h4>
<pre><code class="language-text">sdiv($lhs[: &lt;type&gt;], $rhs[: &lt;type&gt;])
</code></pre>
<h4 id="example-6"><a class="header" href="#example-6">Example</a></h4>
<pre><code class="language-text">let v2 := sdiv(v0, v1)
</code></pre>
<h4 id="operands-6"><a class="header" href="#operands-6">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>lhs</code></td><td><code>i256</code></td><td>Dividend, treated as signed.</td></tr>
<tr><td><code>rhs</code></td><td><code>i256</code></td><td>Divisor, treated as signed; <code>0</code> yields <code>0</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-6"><a class="header" href="#result-and-purity-6">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>max(width(lhs), width(rhs))</code> — a negative divisor can push the result to full width</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-6"><a class="header" href="#annotations-6">Annotations</a></h4>
<p>None.</p>
<h3 id="mod"><a class="header" href="#mod"><code>mod</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Mod</code>)</p>
<h4 id="description-7"><a class="header" href="#description-7">Description</a></h4>
<p>Unsigned modulo. Per EVM, <code>mod(x, 0) = 0</code>.</p>
<h4 id="syntax-7"><a class="header" href="#syntax-7">Syntax</a></h4>
<pre><code class="language-text">mod($lhs[: &lt;type&gt;], $rhs[: &lt;type&gt;])
</code></pre>
<h4 id="example-7"><a class="header" href="#example-7">Example</a></h4>
<pre><code class="language-text">let v2 := mod(v0, v1)
</code></pre>
<h4 id="operands-7"><a class="header" href="#operands-7">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>lhs</code></td><td><code>i256</code></td><td>Dividend.</td></tr>
<tr><td><code>rhs</code></td><td><code>i256</code></td><td>Divisor; <code>0</code> yields <code>0</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-7"><a class="header" href="#result-and-purity-7">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>width(lhs)</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-7"><a class="header" href="#annotations-7">Annotations</a></h4>
<p>None.</p>
<h3 id="smod"><a class="header" href="#smod"><code>smod</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::SMod</code>)</p>
<h4 id="description-8"><a class="header" href="#description-8">Description</a></h4>
<p>Signed modulo. Per EVM, <code>smod(x, 0) = 0</code>; the result takes the sign of the dividend.</p>
<h4 id="syntax-8"><a class="header" href="#syntax-8">Syntax</a></h4>
<pre><code class="language-text">smod($lhs[: &lt;type&gt;], $rhs[: &lt;type&gt;])
</code></pre>
<h4 id="example-8"><a class="header" href="#example-8">Example</a></h4>
<pre><code class="language-text">let v2 := smod(v0, v1)
</code></pre>
<h4 id="operands-8"><a class="header" href="#operands-8">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>lhs</code></td><td><code>i256</code></td><td>Dividend, treated as signed.</td></tr>
<tr><td><code>rhs</code></td><td><code>i256</code></td><td>Divisor, treated as signed; <code>0</code> yields <code>0</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-8"><a class="header" href="#result-and-purity-8">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>width(lhs)</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-8"><a class="header" href="#annotations-8">Annotations</a></h4>
<p>None.</p>
<h3 id="exp"><a class="header" href="#exp"><code>exp</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Exp</code>)</p>
<h4 id="description-9"><a class="header" href="#description-9">Description</a></h4>
<p>Modular exponentiation: <code>(base ^ exponent) mod 2^256</code>. The most expensive arithmetic opcode in EVM (variable gas cost proportional to the byte length of <code>exponent</code>).</p>
<h4 id="syntax-9"><a class="header" href="#syntax-9">Syntax</a></h4>
<pre><code class="language-text">exp($base[: &lt;type&gt;], $exponent[: &lt;type&gt;])
</code></pre>
<h4 id="example-9"><a class="header" href="#example-9">Example</a></h4>
<pre><code class="language-text">let v2 := exp(v0, v1)
</code></pre>
<h4 id="operands-9"><a class="header" href="#operands-9">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>base</code></td><td><code>i256</code></td><td>Base.</td></tr>
<tr><td><code>exponent</code></td><td><code>i256</code></td><td>Exponent.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-9"><a class="header" href="#result-and-purity-9">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code> — conservative; exponentiation can fill any width</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-9"><a class="header" href="#annotations-9">Annotations</a></h4>
<p>None.</p>
<h3 id="and"><a class="header" href="#and"><code>and</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::And</code>)</p>
<h4 id="description-10"><a class="header" href="#description-10">Description</a></h4>
<p>Bitwise AND. The common idiom for type narrowing: a constant mask on the right lets forward analysis pick up a tight result width.</p>
<h4 id="syntax-10"><a class="header" href="#syntax-10">Syntax</a></h4>
<pre><code class="language-text">and($lhs[: &lt;type&gt;], $rhs[: &lt;type&gt;])
</code></pre>
<h4 id="example-10"><a class="header" href="#example-10">Example</a></h4>
<pre><code class="language-text">let v2 := and(v0, v1)
let v3: i8 := 0xff
let v4: i8 := and(v0, v3: i8)
</code></pre>
<h4 id="operands-10"><a class="header" href="#operands-10">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>lhs</code></td><td><code>i256</code></td><td>—</td></tr>
<tr><td><code>rhs</code></td><td><code>i256</code></td><td>—</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-10"><a class="header" href="#result-and-purity-10">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>min(width(lhs), width(rhs))</code> — AND can only clear bits, so the result fits in the narrower operand</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-10"><a class="header" href="#annotations-10">Annotations</a></h4>
<p>None.</p>
<h3 id="or"><a class="header" href="#or"><code>or</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Or</code>)</p>
<h4 id="description-11"><a class="header" href="#description-11">Description</a></h4>
<p>Bitwise OR.</p>
<h4 id="syntax-11"><a class="header" href="#syntax-11">Syntax</a></h4>
<pre><code class="language-text">or($lhs[: &lt;type&gt;], $rhs[: &lt;type&gt;])
</code></pre>
<h4 id="example-11"><a class="header" href="#example-11">Example</a></h4>
<pre><code class="language-text">let v2 := or(v0, v1)
</code></pre>
<h4 id="operands-11"><a class="header" href="#operands-11">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>lhs</code></td><td><code>i256</code></td><td>—</td></tr>
<tr><td><code>rhs</code></td><td><code>i256</code></td><td>—</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-11"><a class="header" href="#result-and-purity-11">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>max(width(lhs), width(rhs))</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-11"><a class="header" href="#annotations-11">Annotations</a></h4>
<p>None.</p>
<h3 id="xor"><a class="header" href="#xor"><code>xor</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Xor</code>)</p>
<h4 id="description-12"><a class="header" href="#description-12">Description</a></h4>
<p>Bitwise XOR.</p>
<h4 id="syntax-12"><a class="header" href="#syntax-12">Syntax</a></h4>
<pre><code class="language-text">xor($lhs[: &lt;type&gt;], $rhs[: &lt;type&gt;])
</code></pre>
<h4 id="example-12"><a class="header" href="#example-12">Example</a></h4>
<pre><code class="language-text">let v2 := xor(v0, v1)
</code></pre>
<h4 id="operands-12"><a class="header" href="#operands-12">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>lhs</code></td><td><code>i256</code></td><td>—</td></tr>
<tr><td><code>rhs</code></td><td><code>i256</code></td><td>—</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-12"><a class="header" href="#result-and-purity-12">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>max(width(lhs), width(rhs))</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-12"><a class="header" href="#annotations-12">Annotations</a></h4>
<p>None.</p>
<h3 id="shl"><a class="header" href="#shl"><code>shl</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Shl</code>)</p>
<h4 id="description-13"><a class="header" href="#description-13">Description</a></h4>
<p>Logical left shift. Operand order follows EVM: <code>shl(shift, value)</code> computes <code>value &lt;&lt; shift</code>. Shifts ≥ 256 produce <code>0</code>.</p>
<h4 id="syntax-13"><a class="header" href="#syntax-13">Syntax</a></h4>
<pre><code class="language-text">shl($shift[: &lt;type&gt;], $value[: &lt;type&gt;])
</code></pre>
<h4 id="example-13"><a class="header" href="#example-13">Example</a></h4>
<pre><code class="language-text">let v2 := shl(v0, v1)
</code></pre>
<h4 id="operands-13"><a class="header" href="#operands-13">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>shift</code></td><td><code>i256</code></td><td>Shift amount in bits.</td></tr>
<tr><td><code>value</code></td><td><code>i256</code></td><td>Value to shift.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-13"><a class="header" href="#result-and-purity-13">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code> — conservative; bits may shift into any width</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-13"><a class="header" href="#annotations-13">Annotations</a></h4>
<p>None.</p>
<h3 id="shr"><a class="header" href="#shr"><code>shr</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Shr</code>)</p>
<h4 id="description-14"><a class="header" href="#description-14">Description</a></h4>
<p>Logical right shift. Operand order follows EVM: <code>shr(shift, value)</code> computes <code>value &gt;&gt; shift</code> with zero-fill from the left. Shifts ≥ 256 produce <code>0</code>.</p>
<h4 id="syntax-14"><a class="header" href="#syntax-14">Syntax</a></h4>
<pre><code class="language-text">shr($shift[: &lt;type&gt;], $value[: &lt;type&gt;])
</code></pre>
<h4 id="example-14"><a class="header" href="#example-14">Example</a></h4>
<pre><code class="language-text">let v2 := shr(v0, v1)
</code></pre>
<h4 id="operands-14"><a class="header" href="#operands-14">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>shift</code></td><td><code>i256</code></td><td>Shift amount in bits.</td></tr>
<tr><td><code>value</code></td><td><code>i256</code></td><td>Value to shift.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-14"><a class="header" href="#result-and-purity-14">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>If <code>shift</code> is a known constant <code>k</code>: tier holding <code>256 - k</code> bits (or <code>i1</code> for <code>k ≥ 256</code>). Otherwise: <code>width(value)</code>.</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-14"><a class="header" href="#annotations-14">Annotations</a></h4>
<p>None.</p>
<h3 id="sar"><a class="header" href="#sar"><code>sar</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Sar</code>)</p>
<h4 id="description-15"><a class="header" href="#description-15">Description</a></h4>
<p>Arithmetic (signed) right shift. Operand order follows EVM: <code>sar(shift, value)</code> shifts <code>value</code> right by <code>shift</code> bits, preserving the sign bit. Shifts ≥ 256 saturate to <code>0</code> for non-negative values and to <code>-1</code> (all-ones) for negative values.</p>
<h4 id="syntax-15"><a class="header" href="#syntax-15">Syntax</a></h4>
<pre><code class="language-text">sar($shift[: &lt;type&gt;], $value[: &lt;type&gt;])
</code></pre>
<h4 id="example-15"><a class="header" href="#example-15">Example</a></h4>
<pre><code class="language-text">let v2 := sar(v0, v1)
</code></pre>
<h4 id="operands-15"><a class="header" href="#operands-15">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>shift</code></td><td><code>i256</code></td><td>Shift amount in bits.</td></tr>
<tr><td><code>value</code></td><td><code>i256</code></td><td>Value to shift, treated as signed.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-15"><a class="header" href="#result-and-purity-15">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>width(value)</code> — unlike <a href="#shr"><code>shr</code></a>, sign-extension means a constant shift cannot narrow the result</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-15"><a class="header" href="#annotations-15">Annotations</a></h4>
<p>None.</p>
<h3 id="lt"><a class="header" href="#lt"><code>lt</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Lt</code>)</p>
<h4 id="description-16"><a class="header" href="#description-16">Description</a></h4>
<p>Unsigned less-than comparison. Returns <code>1</code> if <code>lhs &lt; rhs</code>, else <code>0</code>.</p>
<h4 id="syntax-16"><a class="header" href="#syntax-16">Syntax</a></h4>
<pre><code class="language-text">lt($lhs[: &lt;type&gt;], $rhs[: &lt;type&gt;])
</code></pre>
<h4 id="example-16"><a class="header" href="#example-16">Example</a></h4>
<pre><code class="language-text">let v2: i1 := lt(v0, v1)
</code></pre>
<h4 id="operands-16"><a class="header" href="#operands-16">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>lhs</code></td><td><code>i256</code></td><td>Compared unsigned.</td></tr>
<tr><td><code>rhs</code></td><td><code>i256</code></td><td>Compared unsigned.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-16"><a class="header" href="#result-and-purity-16">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i1</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-16"><a class="header" href="#annotations-16">Annotations</a></h4>
<p>None.</p>
<h3 id="gt"><a class="header" href="#gt"><code>gt</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Gt</code>)</p>
<h4 id="description-17"><a class="header" href="#description-17">Description</a></h4>
<p>Unsigned greater-than comparison. Returns <code>1</code> if <code>lhs &gt; rhs</code>, else <code>0</code>.</p>
<h4 id="syntax-17"><a class="header" href="#syntax-17">Syntax</a></h4>
<pre><code class="language-text">gt($lhs[: &lt;type&gt;], $rhs[: &lt;type&gt;])
</code></pre>
<h4 id="example-17"><a class="header" href="#example-17">Example</a></h4>
<pre><code class="language-text">let v2: i1 := gt(v0, v1)
</code></pre>
<h4 id="operands-17"><a class="header" href="#operands-17">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>lhs</code></td><td><code>i256</code></td><td>Compared unsigned.</td></tr>
<tr><td><code>rhs</code></td><td><code>i256</code></td><td>Compared unsigned.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-17"><a class="header" href="#result-and-purity-17">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i1</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-17"><a class="header" href="#annotations-17">Annotations</a></h4>
<p>None.</p>
<h3 id="slt"><a class="header" href="#slt"><code>slt</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Slt</code>)</p>
<h4 id="description-18"><a class="header" href="#description-18">Description</a></h4>
<p>Signed less-than comparison. Operands are treated as two’s complement.</p>
<h4 id="syntax-18"><a class="header" href="#syntax-18">Syntax</a></h4>
<pre><code class="language-text">slt($lhs[: &lt;type&gt;], $rhs[: &lt;type&gt;])
</code></pre>
<h4 id="example-18"><a class="header" href="#example-18">Example</a></h4>
<pre><code class="language-text">let v2: i1 := slt(v0, v1)
</code></pre>
<h4 id="operands-18"><a class="header" href="#operands-18">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>lhs</code></td><td><code>i256</code></td><td>Compared signed.</td></tr>
<tr><td><code>rhs</code></td><td><code>i256</code></td><td>Compared signed.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-18"><a class="header" href="#result-and-purity-18">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i1</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-18"><a class="header" href="#annotations-18">Annotations</a></h4>
<p>None.</p>
<h3 id="sgt"><a class="header" href="#sgt"><code>sgt</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Sgt</code>)</p>
<h4 id="description-19"><a class="header" href="#description-19">Description</a></h4>
<p>Signed greater-than comparison. Operands are treated as two’s complement.</p>
<h4 id="syntax-19"><a class="header" href="#syntax-19">Syntax</a></h4>
<pre><code class="language-text">sgt($lhs[: &lt;type&gt;], $rhs[: &lt;type&gt;])
</code></pre>
<h4 id="example-19"><a class="header" href="#example-19">Example</a></h4>
<pre><code class="language-text">let v2: i1 := sgt(v0, v1)
</code></pre>
<h4 id="operands-19"><a class="header" href="#operands-19">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>lhs</code></td><td><code>i256</code></td><td>Compared signed.</td></tr>
<tr><td><code>rhs</code></td><td><code>i256</code></td><td>Compared signed.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-19"><a class="header" href="#result-and-purity-19">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i1</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-19"><a class="header" href="#annotations-19">Annotations</a></h4>
<p>None.</p>
<h3 id="eq"><a class="header" href="#eq"><code>eq</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Eq</code>)</p>
<h4 id="description-20"><a class="header" href="#description-20">Description</a></h4>
<p>Equality comparison. Returns <code>1</code> if <code>lhs == rhs</code>, else <code>0</code>. Signedness is irrelevant.</p>
<h4 id="syntax-20"><a class="header" href="#syntax-20">Syntax</a></h4>
<pre><code class="language-text">eq($lhs[: &lt;type&gt;], $rhs[: &lt;type&gt;])
</code></pre>
<h4 id="example-20"><a class="header" href="#example-20">Example</a></h4>
<pre><code class="language-text">let v2: i1 := eq(v0, v1)
</code></pre>
<h4 id="operands-20"><a class="header" href="#operands-20">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>lhs</code></td><td><code>i256</code></td><td>—</td></tr>
<tr><td><code>rhs</code></td><td><code>i256</code></td><td>—</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-20"><a class="header" href="#result-and-purity-20">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i1</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-20"><a class="header" href="#annotations-20">Annotations</a></h4>
<p>None.</p>
<h3 id="byte"><a class="header" href="#byte"><code>byte</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::Byte</code>)</p>
<h4 id="description-21"><a class="header" href="#description-21">Description</a></h4>
<p>Extract a single byte from a 256-bit word. <code>byte(i, x)</code> returns the <em>i</em>-th byte of <code>x</code> with byte 0 being the most significant. If <code>i ≥ 32</code>, the result is <code>0</code>.</p>
<h4 id="syntax-21"><a class="header" href="#syntax-21">Syntax</a></h4>
<pre><code class="language-text">byte($index[: &lt;type&gt;], $word[: &lt;type&gt;])
</code></pre>
<h4 id="example-21"><a class="header" href="#example-21">Example</a></h4>
<pre><code class="language-text">let v2: i8 := byte(v0, v1)
</code></pre>
<h4 id="operands-21"><a class="header" href="#operands-21">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>index</code></td><td><code>i256</code></td><td>Byte position; <code>0</code> = most significant byte. Values <code>≥ 32</code> yield <code>0</code>.</td></tr>
<tr><td><code>word</code></td><td><code>i256</code></td><td>Source word.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-21"><a class="header" href="#result-and-purity-21">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i8</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-21"><a class="header" href="#annotations-21">Annotations</a></h4>
<p>None.</p>
<h3 id="signextend"><a class="header" href="#signextend"><code>signextend</code></a></h3>
<p>(<code>Expression::Binary</code> with <code>BinaryOperation::SignExtend</code>)</p>
<h4 id="description-22"><a class="header" href="#description-22">Description</a></h4>
<p>Sign-extend an integer from a byte position. Per EVM, <code>signextend(b, x)</code> treats byte <code>b</code> of <code>x</code> as the most significant byte of a smaller signed integer and extends its sign through the upper bytes.</p>
<h4 id="syntax-22"><a class="header" href="#syntax-22">Syntax</a></h4>
<pre><code class="language-text">signextend($byte_position[: &lt;type&gt;], $value[: &lt;type&gt;])
</code></pre>
<h4 id="example-22"><a class="header" href="#example-22">Example</a></h4>
<pre><code class="language-text">let v2 := signextend(v0, v1)
</code></pre>
<h4 id="operands-22"><a class="header" href="#operands-22">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>byte_position</code></td><td><code>i256</code></td><td>Byte position of the sign byte (0–31).</td></tr>
<tr><td><code>value</code></td><td><code>i256</code></td><td>Source value.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-22"><a class="header" href="#result-and-purity-22">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code> — the extended value occupies the full word</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-22"><a class="header" href="#annotations-22">Annotations</a></h4>
<p>The width-targeted sign-extension primitive <a href="#sextibits"><code>sext&lt;i&lt;bits&gt;&gt;</code></a> (<code>Expression::SignExtendTo</code>) is a separate operation; see the bit-width conversions section.</p>
<h3 id="addmod"><a class="header" href="#addmod"><code>addmod</code></a></h3>
<p>(<code>Expression::Ternary</code> with <code>BinaryOperation::AddMod</code>)</p>
<h4 id="description-23"><a class="header" href="#description-23">Description</a></h4>
<p>Ternary modular addition: <code>(a + b) mod n</code>, computed without intermediate overflow. Per EVM, <code>n = 0</code> yields <code>0</code>.</p>
<h4 id="syntax-23"><a class="header" href="#syntax-23">Syntax</a></h4>
<pre><code class="language-text">addmod($a[: &lt;type&gt;], $b[: &lt;type&gt;], $n[: &lt;type&gt;])
</code></pre>
<h4 id="example-23"><a class="header" href="#example-23">Example</a></h4>
<pre><code class="language-text">let v3 := addmod(v0, v1, v2)
</code></pre>
<h4 id="operands-23"><a class="header" href="#operands-23">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>a</code></td><td><code>i256</code></td><td>First addend.</td></tr>
<tr><td><code>b</code></td><td><code>i256</code></td><td>Second addend.</td></tr>
<tr><td><code>n</code></td><td><code>i256</code></td><td>Modulus; <code>0</code> yields <code>0</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-23"><a class="header" href="#result-and-purity-23">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code> — conservative</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-23"><a class="header" href="#annotations-23">Annotations</a></h4>
<p>None.</p>
<h3 id="mulmod"><a class="header" href="#mulmod"><code>mulmod</code></a></h3>
<p>(<code>Expression::Ternary</code> with <code>BinaryOperation::MulMod</code>)</p>
<h4 id="description-24"><a class="header" href="#description-24">Description</a></h4>
<p>Ternary modular multiplication: <code>(a * b) mod n</code>, computed without intermediate overflow. Per EVM, <code>n = 0</code> yields <code>0</code>.</p>
<h4 id="syntax-24"><a class="header" href="#syntax-24">Syntax</a></h4>
<pre><code class="language-text">mulmod($a[: &lt;type&gt;], $b[: &lt;type&gt;], $n[: &lt;type&gt;])
</code></pre>
<h4 id="example-24"><a class="header" href="#example-24">Example</a></h4>
<pre><code class="language-text">let v3 := mulmod(v0, v1, v2)
</code></pre>
<h4 id="operands-24"><a class="header" href="#operands-24">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>a</code></td><td><code>i256</code></td><td>First factor.</td></tr>
<tr><td><code>b</code></td><td><code>i256</code></td><td>Second factor.</td></tr>
<tr><td><code>n</code></td><td><code>i256</code></td><td>Modulus; <code>0</code> yields <code>0</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-24"><a class="header" href="#result-and-purity-24">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code> — conservative</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-24"><a class="header" href="#annotations-24">Annotations</a></h4>
<p>None.</p>
<h3 id="iszero"><a class="header" href="#iszero"><code>iszero</code></a></h3>
<p>(<code>Expression::Unary</code> with <code>UnaryOperation::IsZero</code>)</p>
<h4 id="description-25"><a class="header" href="#description-25">Description</a></h4>
<p>Returns <code>1</code> if the operand is <code>0</code>, else <code>0</code>. Also serves as the logical NOT for boolean values.</p>
<h4 id="syntax-25"><a class="header" href="#syntax-25">Syntax</a></h4>
<pre><code class="language-text">iszero($operand[: &lt;type&gt;])
</code></pre>
<h4 id="example-25"><a class="header" href="#example-25">Example</a></h4>
<pre><code class="language-text">let v1: i1 := iszero(v0)
</code></pre>
<h4 id="operands-25"><a class="header" href="#operands-25">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>operand</code></td><td><code>i256</code></td><td>—</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-25"><a class="header" href="#result-and-purity-25">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i1</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-25"><a class="header" href="#annotations-25">Annotations</a></h4>
<p>None.</p>
<h3 id="not"><a class="header" href="#not"><code>not</code></a></h3>
<p>(<code>Expression::Unary</code> with <code>UnaryOperation::Not</code>)</p>
<h4 id="description-26"><a class="header" href="#description-26">Description</a></h4>
<p>Bitwise complement. Inverts every bit; equivalent to <code>xor(operand, 2^256 - 1)</code>.</p>
<h4 id="syntax-26"><a class="header" href="#syntax-26">Syntax</a></h4>
<pre><code class="language-text">not($operand[: &lt;type&gt;])
</code></pre>
<h4 id="example-26"><a class="header" href="#example-26">Example</a></h4>
<pre><code class="language-text">let v1 := not(v0)
</code></pre>
<h4 id="operands-26"><a class="header" href="#operands-26">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>operand</code></td><td><code>i256</code></td><td>—</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-26"><a class="header" href="#result-and-purity-26">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code> — the complement fills the full word regardless of operand width</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-26"><a class="header" href="#annotations-26">Annotations</a></h4>
<p>None.</p>
<h3 id="clz"><a class="header" href="#clz"><code>clz</code></a></h3>
<p>(<code>Expression::Unary</code> with <code>UnaryOperation::Clz</code>)</p>
<h4 id="description-27"><a class="header" href="#description-27">Description</a></h4>
<p>Count leading zeros. Returns the number of leading zero bits in the operand, where a value of <code>0</code> returns <code>256</code> (the full width). Not an EVM opcode; reaches newyork as a Yul builtin (<code>FunctionName::Clz</code>) and is translated directly by the Yul-to-newyork translator.</p>
<h4 id="syntax-27"><a class="header" href="#syntax-27">Syntax</a></h4>
<pre><code class="language-text">clz($operand[: &lt;type&gt;])
</code></pre>
<h4 id="example-27"><a class="header" href="#example-27">Example</a></h4>
<pre><code class="language-text">let v1 := clz(v0)
</code></pre>
<h4 id="operands-27"><a class="header" href="#operands-27">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>operand</code></td><td><code>i256</code></td><td>—</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-27"><a class="header" href="#result-and-purity-27">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code> — in practice the value fits in nine bits (max <code>256</code>), so type inference often narrows further</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-27"><a class="header" href="#annotations-27">Annotations</a></h4>
<p>None.</p>
<h3 id="truncateibits"><a class="header" href="#truncateibits"><code>truncate&lt;i&lt;bits&gt;&gt;</code></a></h3>
<p>(<code>Expression::Truncate</code>)</p>
<h4 id="description-28"><a class="header" href="#description-28">Description</a></h4>
<p>Reinterpret a wider integer as a narrower one by discarding the upper bits. The destination width is carried in the IR’s <code>to: BitWidth</code> field and is rendered inside the angle brackets of the printer mnemonic. Narrowing-only; the source width must be greater than or equal to the destination width.</p>
<h4 id="syntax-28"><a class="header" href="#syntax-28">Syntax</a></h4>
<pre><code class="language-text">truncate&lt;i&lt;bits&gt;&gt;($value[: &lt;type&gt;])
</code></pre>
<h4 id="example-28"><a class="header" href="#example-28">Example</a></h4>
<pre><code class="language-text">let v1: i64 := truncate&lt;i64&gt;(v0)
let v2: i8 := truncate&lt;i8&gt;(v1: i64)
</code></pre>
<h4 id="operands-28"><a class="header" href="#operands-28">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>value</code></td><td><code>i256</code></td><td>Source value; must be at least as wide as the destination.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-28"><a class="header" href="#result-and-purity-28">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>The destination width from the <code>to</code> field</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-28"><a class="header" href="#annotations-28">Annotations</a></h4>
<p>None. The destination width is part of the operation name, not a debug annotation.</p>
<h3 id="zextibits"><a class="header" href="#zextibits"><code>zext&lt;i&lt;bits&gt;&gt;</code></a></h3>
<p>(<code>Expression::ZeroExtend</code>)</p>
<h4 id="description-29"><a class="header" href="#description-29">Description</a></h4>
<p>Reinterpret a narrower integer as a wider one by zero-filling the upper bits. The destination width is carried in the IR’s <code>to: BitWidth</code> field. Widening-only.</p>
<h4 id="syntax-29"><a class="header" href="#syntax-29">Syntax</a></h4>
<pre><code class="language-text">zext&lt;i&lt;bits&gt;&gt;($value[: &lt;type&gt;])
</code></pre>
<h4 id="example-29"><a class="header" href="#example-29">Example</a></h4>
<pre><code class="language-text">let v1 := zext&lt;i256&gt;(v0: i8)
</code></pre>
<h4 id="operands-29"><a class="header" href="#operands-29">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>value</code></td><td><code>i256</code></td><td>Source value; must be no wider than the destination.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-29"><a class="header" href="#result-and-purity-29">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>The destination width from the <code>to</code> field</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-29"><a class="header" href="#annotations-29">Annotations</a></h4>
<p>None.</p>
<h3 id="sextibits"><a class="header" href="#sextibits"><code>sext&lt;i&lt;bits&gt;&gt;</code></a></h3>
<p>(<code>Expression::SignExtendTo</code>)</p>
<h4 id="description-30"><a class="header" href="#description-30">Description</a></h4>
<p>Reinterpret a narrower signed integer as a wider one by sign-extending the high bit. The destination width is carried in the IR’s <code>to: BitWidth</code> field. Distinct from <a href="#signextend"><code>signextend</code></a> (<code>Expression::Binary</code>), which is the EVM byte-position primitive; this one specifies the destination width directly and is introduced by passes that produce a sign-extended value at a known target width.</p>
<h4 id="syntax-30"><a class="header" href="#syntax-30">Syntax</a></h4>
<pre><code class="language-text">sext&lt;i&lt;bits&gt;&gt;($value[: &lt;type&gt;])
</code></pre>
<h4 id="example-30"><a class="header" href="#example-30">Example</a></h4>
<pre><code class="language-text">let v1 := sext&lt;i256&gt;(v0: i64)
</code></pre>
<h4 id="operands-30"><a class="header" href="#operands-30">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>value</code></td><td><code>i256</code></td><td>Source value; must be no wider than the destination.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-30"><a class="header" href="#result-and-purity-30">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>The destination width from the <code>to</code> field</td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-30"><a class="header" href="#annotations-30">Annotations</a></h4>
<p>None.</p>
<h3 id="keccak256"><a class="header" href="#keccak256"><code>keccak256</code></a></h3>
<p>(<code>Expression::Keccak256</code>)</p>
<h4 id="description-31"><a class="header" href="#description-31">Description</a></h4>
<p>Compute the Keccak-256 hash of <code>length</code> bytes of emulated EVM linear memory starting at <code>offset</code>. The general-purpose hashing primitive; the specialized variants below cover the common scratch-space patterns more compactly.</p>
<h4 id="syntax-31"><a class="header" href="#syntax-31">Syntax</a></h4>
<pre><code class="language-text">keccak256($offset[: &lt;type&gt;], $length[: &lt;type&gt;])
</code></pre>
<h4 id="example-31"><a class="header" href="#example-31">Example</a></h4>
<pre><code class="language-text">let v2 := keccak256(v0, v1)
</code></pre>
<h4 id="operands-31"><a class="header" href="#operands-31">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>offset</code></td><td><code>i256</code></td><td>Byte offset into linear memory; forward analysis widens to at least <code>i64</code>.</td></tr>
<tr><td><code>length</code></td><td><code>i256</code></td><td>Length of the region to hash, in bytes; forward analysis widens to at least <code>i64</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-31"><a class="header" href="#result-and-purity-31">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure — the hash is a deterministic function of the memory contents at evaluation time. Passes that hoist or dedupe must respect intervening memory writes.</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-31"><a class="header" href="#annotations-31">Annotations</a></h4>
<p>None.</p>
<h3 id="keccak256_pair"><a class="header" href="#keccak256_pair"><code>keccak256_pair</code></a></h3>
<p>(<code>Expression::Keccak256Pair</code>)</p>
<h4 id="description-32"><a class="header" href="#description-32">Description</a></h4>
<p>Compound hash of two 256-bit words. Equivalent to <code>mstore(0, word0); mstore(32, word1); keccak256(0, 64)</code> but emitted as a single outlined call after <code>mem_opt</code>’s keccak fusion recognizes the pattern. The mapping-key idiom; see also <a href="#mapping_sload"><code>mapping_sload</code></a>.</p>
<h4 id="syntax-32"><a class="header" href="#syntax-32">Syntax</a></h4>
<pre><code class="language-text">keccak256_pair($word0[: &lt;type&gt;], $word1[: &lt;type&gt;])
</code></pre>
<h4 id="example-32"><a class="header" href="#example-32">Example</a></h4>
<pre><code class="language-text">let v2 := keccak256_pair(v0, v1)
</code></pre>
<h4 id="operands-32"><a class="header" href="#operands-32">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>word0</code></td><td><code>i256</code></td><td>First word; the high 32 bytes of the hash input.</td></tr>
<tr><td><code>word1</code></td><td><code>i256</code></td><td>Second word; the low 32 bytes of the hash input.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-32"><a class="header" href="#result-and-purity-32">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-32"><a class="header" href="#annotations-32">Annotations</a></h4>
<p>None.</p>
<h3 id="keccak256_single"><a class="header" href="#keccak256_single"><code>keccak256_single</code></a></h3>
<p>(<code>Expression::Keccak256Single</code>)</p>
<h4 id="description-33"><a class="header" href="#description-33">Description</a></h4>
<p>Compound hash of a single 256-bit word. Equivalent to <code>mstore(0, word0); keccak256(0, 32)</code> but emitted as a single outlined call after <code>mem_opt</code>’s keccak fusion.</p>
<h4 id="syntax-33"><a class="header" href="#syntax-33">Syntax</a></h4>
<pre><code class="language-text">keccak256_single($word0[: &lt;type&gt;])
</code></pre>
<h4 id="example-33"><a class="header" href="#example-33">Example</a></h4>
<pre><code class="language-text">let v1 := keccak256_single(v0)
</code></pre>
<h4 id="operands-33"><a class="header" href="#operands-33">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>word0</code></td><td><code>i256</code></td><td>The word to hash.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-33"><a class="header" href="#result-and-purity-33">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-33"><a class="header" href="#annotations-33">Annotations</a></h4>
<p>None.</p>
<h3 id="caller"><a class="header" href="#caller"><code>caller</code></a></h3>
<p>(<code>Expression::Caller</code>)</p>
<h4 id="description-34"><a class="header" href="#description-34">Description</a></h4>
<p>Address of the immediate caller of the current call frame.</p>
<h4 id="syntax-34"><a class="header" href="#syntax-34">Syntax</a></h4>
<pre><code class="language-text">caller()
</code></pre>
<h4 id="example-34"><a class="header" href="#example-34">Example</a></h4>
<pre><code class="language-text">let v0: i160 := caller()
</code></pre>
<h4 id="operands-34"><a class="header" href="#operands-34">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-34"><a class="header" href="#result-and-purity-34">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i160</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-34"><a class="header" href="#annotations-34">Annotations</a></h4>
<p>None.</p>
<h3 id="callvalue"><a class="header" href="#callvalue"><code>callvalue</code></a></h3>
<p>(<code>Expression::CallValue</code>)</p>
<h4 id="description-35"><a class="header" href="#description-35">Description</a></h4>
<p>Value (wei) attached to the current call.</p>
<h4 id="syntax-35"><a class="header" href="#syntax-35">Syntax</a></h4>
<pre><code class="language-text">callvalue()
</code></pre>
<h4 id="example-35"><a class="header" href="#example-35">Example</a></h4>
<pre><code class="language-text">let v0 := callvalue()
</code></pre>
<h4 id="operands-35"><a class="header" href="#operands-35">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-35"><a class="header" href="#result-and-purity-35">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-35"><a class="header" href="#annotations-35">Annotations</a></h4>
<p>None.</p>
<h3 id="origin"><a class="header" href="#origin"><code>origin</code></a></h3>
<p>(<code>Expression::Origin</code>)</p>
<h4 id="description-36"><a class="header" href="#description-36">Description</a></h4>
<p>Address of the original externally owned account that initiated the transaction.</p>
<h4 id="syntax-36"><a class="header" href="#syntax-36">Syntax</a></h4>
<pre><code class="language-text">origin()
</code></pre>
<h4 id="example-36"><a class="header" href="#example-36">Example</a></h4>
<pre><code class="language-text">let v0: i160 := origin()
</code></pre>
<h4 id="operands-36"><a class="header" href="#operands-36">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-36"><a class="header" href="#result-and-purity-36">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i160</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-36"><a class="header" href="#annotations-36">Annotations</a></h4>
<p>None.</p>
<h3 id="address"><a class="header" href="#address"><code>address</code></a></h3>
<p>(<code>Expression::Address</code>)</p>
<h4 id="description-37"><a class="header" href="#description-37">Description</a></h4>
<p>Address of the contract executing the current call frame.</p>
<h4 id="syntax-37"><a class="header" href="#syntax-37">Syntax</a></h4>
<pre><code class="language-text">address()
</code></pre>
<h4 id="example-37"><a class="header" href="#example-37">Example</a></h4>
<pre><code class="language-text">let v0: i160 := address()
</code></pre>
<h4 id="operands-37"><a class="header" href="#operands-37">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-37"><a class="header" href="#result-and-purity-37">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i160</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-37"><a class="header" href="#annotations-37">Annotations</a></h4>
<p>None.</p>
<h3 id="chainid"><a class="header" href="#chainid"><code>chainid</code></a></h3>
<p>(<code>Expression::ChainId</code>)</p>
<h4 id="description-38"><a class="header" href="#description-38">Description</a></h4>
<p>Chain identifier of the network the contract is executing on.</p>
<h4 id="syntax-38"><a class="header" href="#syntax-38">Syntax</a></h4>
<pre><code class="language-text">chainid()
</code></pre>
<h4 id="example-38"><a class="header" href="#example-38">Example</a></h4>
<pre><code class="language-text">let v0 := chainid()
</code></pre>
<h4 id="operands-38"><a class="header" href="#operands-38">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-38"><a class="header" href="#result-and-purity-38">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-38"><a class="header" href="#annotations-38">Annotations</a></h4>
<p>None.</p>
<h3 id="gas"><a class="header" href="#gas"><code>gas</code></a></h3>
<p>(<code>Expression::Gas</code>)</p>
<h4 id="description-39"><a class="header" href="#description-39">Description</a></h4>
<p>Remaining gas at the point of evaluation. Modeled as a pure expression for IR purposes; in practice it changes between evaluations, so any simplifier that deduplicates pure expressions must respect <code>gas</code> as a barrier.</p>
<h4 id="syntax-39"><a class="header" href="#syntax-39">Syntax</a></h4>
<pre><code class="language-text">gas()
</code></pre>
<h4 id="example-39"><a class="header" href="#example-39">Example</a></h4>
<pre><code class="language-text">let v0: i64 := gas()
</code></pre>
<h4 id="operands-39"><a class="header" href="#operands-39">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-39"><a class="header" href="#result-and-purity-39">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i64</code></td><td>Pure (per IR; see Description)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-39"><a class="header" href="#annotations-39">Annotations</a></h4>
<p>None.</p>
<h3 id="msize"><a class="header" href="#msize"><code>msize</code></a></h3>
<p>(<code>Expression::MSize</code>)</p>
<h4 id="description-40"><a class="header" href="#description-40">Description</a></h4>
<p>Highest byte offset of emulated EVM linear memory that has been touched, rounded up to the next 32-byte boundary. Unlike <a href="#gas"><code>gas</code></a>, classified as side-effectful by the simplifier: unused <code>msize()</code> bindings are not eliminated, because the result depends on the program’s memory-access history and would change if the surrounding statements were reordered.</p>
<h4 id="syntax-40"><a class="header" href="#syntax-40">Syntax</a></h4>
<pre><code class="language-text">msize()
</code></pre>
<h4 id="example-40"><a class="header" href="#example-40">Example</a></h4>
<pre><code class="language-text">let v0: i64 := msize()
</code></pre>
<h4 id="operands-40"><a class="header" href="#operands-40">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-40"><a class="header" href="#result-and-purity-40">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i64</code></td><td>Effectful (see Description)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-40"><a class="header" href="#annotations-40">Annotations</a></h4>
<p>None.</p>
<h3 id="coinbase"><a class="header" href="#coinbase"><code>coinbase</code></a></h3>
<p>(<code>Expression::Coinbase</code>)</p>
<h4 id="description-41"><a class="header" href="#description-41">Description</a></h4>
<p>Address of the block’s coinbase (block author).</p>
<h4 id="syntax-41"><a class="header" href="#syntax-41">Syntax</a></h4>
<pre><code class="language-text">coinbase()
</code></pre>
<h4 id="example-41"><a class="header" href="#example-41">Example</a></h4>
<pre><code class="language-text">let v0: i160 := coinbase()
</code></pre>
<h4 id="operands-41"><a class="header" href="#operands-41">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-41"><a class="header" href="#result-and-purity-41">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i160</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-41"><a class="header" href="#annotations-41">Annotations</a></h4>
<p>None.</p>
<h3 id="timestamp"><a class="header" href="#timestamp"><code>timestamp</code></a></h3>
<p>(<code>Expression::Timestamp</code>)</p>
<h4 id="description-42"><a class="header" href="#description-42">Description</a></h4>
<p>Block timestamp, as a Unix epoch second.</p>
<h4 id="syntax-42"><a class="header" href="#syntax-42">Syntax</a></h4>
<pre><code class="language-text">timestamp()
</code></pre>
<h4 id="example-42"><a class="header" href="#example-42">Example</a></h4>
<pre><code class="language-text">let v0: i64 := timestamp()
</code></pre>
<h4 id="operands-42"><a class="header" href="#operands-42">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-42"><a class="header" href="#result-and-purity-42">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i64</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-42"><a class="header" href="#annotations-42">Annotations</a></h4>
<p>None.</p>
<h3 id="number"><a class="header" href="#number"><code>number</code></a></h3>
<p>(<code>Expression::Number</code>)</p>
<h4 id="description-43"><a class="header" href="#description-43">Description</a></h4>
<p>Current block number.</p>
<h4 id="syntax-43"><a class="header" href="#syntax-43">Syntax</a></h4>
<pre><code class="language-text">number()
</code></pre>
<h4 id="example-43"><a class="header" href="#example-43">Example</a></h4>
<pre><code class="language-text">let v0: i64 := number()
</code></pre>
<h4 id="operands-43"><a class="header" href="#operands-43">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-43"><a class="header" href="#result-and-purity-43">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i64</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-43"><a class="header" href="#annotations-43">Annotations</a></h4>
<p>None.</p>
<h3 id="difficulty"><a class="header" href="#difficulty"><code>difficulty</code></a></h3>
<p>(<code>Expression::Difficulty</code>)</p>
<h4 id="description-44"><a class="header" href="#description-44">Description</a></h4>
<p>Pre-merge block difficulty. On post-merge chains this is the block’s <code>prevrandao</code> value.</p>
<h4 id="syntax-44"><a class="header" href="#syntax-44">Syntax</a></h4>
<pre><code class="language-text">difficulty()
</code></pre>
<h4 id="example-44"><a class="header" href="#example-44">Example</a></h4>
<pre><code class="language-text">let v0 := difficulty()
</code></pre>
<h4 id="operands-44"><a class="header" href="#operands-44">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-44"><a class="header" href="#result-and-purity-44">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-44"><a class="header" href="#annotations-44">Annotations</a></h4>
<p>None.</p>
<h3 id="gaslimit"><a class="header" href="#gaslimit"><code>gaslimit</code></a></h3>
<p>(<code>Expression::GasLimit</code>)</p>
<h4 id="description-45"><a class="header" href="#description-45">Description</a></h4>
<p>Block gas limit.</p>
<h4 id="syntax-45"><a class="header" href="#syntax-45">Syntax</a></h4>
<pre><code class="language-text">gaslimit()
</code></pre>
<h4 id="example-45"><a class="header" href="#example-45">Example</a></h4>
<pre><code class="language-text">let v0: i64 := gaslimit()
</code></pre>
<h4 id="operands-45"><a class="header" href="#operands-45">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-45"><a class="header" href="#result-and-purity-45">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i64</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-45"><a class="header" href="#annotations-45">Annotations</a></h4>
<p>None.</p>
<h3 id="basefee"><a class="header" href="#basefee"><code>basefee</code></a></h3>
<p>(<code>Expression::BaseFee</code>)</p>
<h4 id="description-46"><a class="header" href="#description-46">Description</a></h4>
<p>Current block’s EIP-1559 base fee per gas.</p>
<h4 id="syntax-46"><a class="header" href="#syntax-46">Syntax</a></h4>
<pre><code class="language-text">basefee()
</code></pre>
<h4 id="example-46"><a class="header" href="#example-46">Example</a></h4>
<pre><code class="language-text">let v0 := basefee()
</code></pre>
<h4 id="operands-46"><a class="header" href="#operands-46">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-46"><a class="header" href="#result-and-purity-46">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-46"><a class="header" href="#annotations-46">Annotations</a></h4>
<p>None.</p>
<h3 id="blobbasefee"><a class="header" href="#blobbasefee"><code>blobbasefee</code></a></h3>
<p>(<code>Expression::BlobBaseFee</code>)</p>
<h4 id="description-47"><a class="header" href="#description-47">Description</a></h4>
<p>Current block’s EIP-4844 blob base fee per gas.</p>
<h4 id="syntax-47"><a class="header" href="#syntax-47">Syntax</a></h4>
<pre><code class="language-text">blobbasefee()
</code></pre>
<h4 id="example-47"><a class="header" href="#example-47">Example</a></h4>
<pre><code class="language-text">let v0 := blobbasefee()
</code></pre>
<h4 id="operands-47"><a class="header" href="#operands-47">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-47"><a class="header" href="#result-and-purity-47">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-47"><a class="header" href="#annotations-47">Annotations</a></h4>
<p>None.</p>
<h3 id="blobhash"><a class="header" href="#blobhash"><code>blobhash</code></a></h3>
<p>(<code>Expression::BlobHash</code>)</p>
<h4 id="description-48"><a class="header" href="#description-48">Description</a></h4>
<p>Versioned hash of the blob at the given index in the current transaction’s blob list.</p>
<h4 id="syntax-48"><a class="header" href="#syntax-48">Syntax</a></h4>
<pre><code class="language-text">blobhash($index[: &lt;type&gt;])
</code></pre>
<h4 id="example-48"><a class="header" href="#example-48">Example</a></h4>
<pre><code class="language-text">let v1 := blobhash(v0)
</code></pre>
<h4 id="operands-48"><a class="header" href="#operands-48">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>index</code></td><td><code>i256</code></td><td>Blob index; forward analysis widens to at least <code>i64</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-48"><a class="header" href="#result-and-purity-48">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-48"><a class="header" href="#annotations-48">Annotations</a></h4>
<p>None.</p>
<h3 id="blockhash"><a class="header" href="#blockhash"><code>blockhash</code></a></h3>
<p>(<code>Expression::BlockHash</code>)</p>
<h4 id="description-49"><a class="header" href="#description-49">Description</a></h4>
<p>Hash of the block with the given number. Per EVM, valid only for the most recent 256 blocks; outside that range the result is <code>0</code>.</p>
<h4 id="syntax-49"><a class="header" href="#syntax-49">Syntax</a></h4>
<pre><code class="language-text">blockhash($number[: &lt;type&gt;])
</code></pre>
<h4 id="example-49"><a class="header" href="#example-49">Example</a></h4>
<pre><code class="language-text">let v1 := blockhash(v0)
</code></pre>
<h4 id="operands-49"><a class="header" href="#operands-49">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>number</code></td><td><code>i256</code></td><td>Block number; forward analysis widens to <code>i256</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-49"><a class="header" href="#result-and-purity-49">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-49"><a class="header" href="#annotations-49">Annotations</a></h4>
<p>None.</p>
<h3 id="selfbalance"><a class="header" href="#selfbalance"><code>selfbalance</code></a></h3>
<p>(<code>Expression::SelfBalance</code>)</p>
<h4 id="description-50"><a class="header" href="#description-50">Description</a></h4>
<p>Balance (in wei) of the contract executing the current call frame. Cheaper than <code>balance(address())</code>.</p>
<h4 id="syntax-50"><a class="header" href="#syntax-50">Syntax</a></h4>
<pre><code class="language-text">selfbalance()
</code></pre>
<h4 id="example-50"><a class="header" href="#example-50">Example</a></h4>
<pre><code class="language-text">let v0 := selfbalance()
</code></pre>
<h4 id="operands-50"><a class="header" href="#operands-50">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-50"><a class="header" href="#result-and-purity-50">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-50"><a class="header" href="#annotations-50">Annotations</a></h4>
<p>None.</p>
<h3 id="gasprice"><a class="header" href="#gasprice"><code>gasprice</code></a></h3>
<p>(<code>Expression::GasPrice</code>)</p>
<h4 id="description-51"><a class="header" href="#description-51">Description</a></h4>
<p>Effective gas price of the current transaction.</p>
<h4 id="syntax-51"><a class="header" href="#syntax-51">Syntax</a></h4>
<pre><code class="language-text">gasprice()
</code></pre>
<h4 id="example-51"><a class="header" href="#example-51">Example</a></h4>
<pre><code class="language-text">let v0 := gasprice()
</code></pre>
<h4 id="operands-51"><a class="header" href="#operands-51">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-51"><a class="header" href="#result-and-purity-51">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-51"><a class="header" href="#annotations-51">Annotations</a></h4>
<p>None.</p>
<h3 id="calldataload"><a class="header" href="#calldataload"><code>calldataload</code></a></h3>
<p>(<code>Expression::CallDataLoad</code>)</p>
<h4 id="description-52"><a class="header" href="#description-52">Description</a></h4>
<p>Read 32 bytes from the current call’s calldata at the given offset. Reads past the end of calldata return zero bytes.</p>
<h4 id="syntax-52"><a class="header" href="#syntax-52">Syntax</a></h4>
<pre><code class="language-text">calldataload($offset[: &lt;type&gt;])
</code></pre>
<h4 id="example-52"><a class="header" href="#example-52">Example</a></h4>
<pre><code class="language-text">let v1 := calldataload(v0)
</code></pre>
<h4 id="operands-52"><a class="header" href="#operands-52">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>offset</code></td><td><code>i256</code></td><td>Byte offset into calldata.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-52"><a class="header" href="#result-and-purity-52">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-52"><a class="header" href="#annotations-52">Annotations</a></h4>
<p>None.</p>
<h3 id="calldatasize"><a class="header" href="#calldatasize"><code>calldatasize</code></a></h3>
<p>(<code>Expression::CallDataSize</code>)</p>
<h4 id="description-53"><a class="header" href="#description-53">Description</a></h4>
<p>Length of the current call’s calldata, in bytes.</p>
<h4 id="syntax-53"><a class="header" href="#syntax-53">Syntax</a></h4>
<pre><code class="language-text">calldatasize()
</code></pre>
<h4 id="example-53"><a class="header" href="#example-53">Example</a></h4>
<pre><code class="language-text">let v0: i64 := calldatasize()
</code></pre>
<h4 id="operands-53"><a class="header" href="#operands-53">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-53"><a class="header" href="#result-and-purity-53">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i64</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-53"><a class="header" href="#annotations-53">Annotations</a></h4>
<p>None.</p>
<h3 id="returndatasize"><a class="header" href="#returndatasize"><code>returndatasize</code></a></h3>
<p>(<code>Expression::ReturnDataSize</code>)</p>
<h4 id="description-54"><a class="header" href="#description-54">Description</a></h4>
<p>Length of the most recently returned data buffer from a sub-call, in bytes. Modeled as pure per IR but reflects the last <code>ExternalCall</code> / <code>Create</code> result; consumers must respect that ordering.</p>
<h4 id="syntax-54"><a class="header" href="#syntax-54">Syntax</a></h4>
<pre><code class="language-text">returndatasize()
</code></pre>
<h4 id="example-54"><a class="header" href="#example-54">Example</a></h4>
<pre><code class="language-text">let v0: i64 := returndatasize()
</code></pre>
<h4 id="operands-54"><a class="header" href="#operands-54">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-54"><a class="header" href="#result-and-purity-54">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i64</code></td><td>Pure (per IR; see Description)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-54"><a class="header" href="#annotations-54">Annotations</a></h4>
<p>None.</p>
<h3 id="codesize"><a class="header" href="#codesize"><code>codesize</code></a></h3>
<p>(<code>Expression::CodeSize</code>)</p>
<h4 id="description-55"><a class="header" href="#description-55">Description</a></h4>
<p>Size of the currently executing code, in bytes.</p>
<h4 id="syntax-55"><a class="header" href="#syntax-55">Syntax</a></h4>
<pre><code class="language-text">codesize()
</code></pre>
<h4 id="example-55"><a class="header" href="#example-55">Example</a></h4>
<pre><code class="language-text">let v0: i64 := codesize()
</code></pre>
<h4 id="operands-55"><a class="header" href="#operands-55">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-55"><a class="header" href="#result-and-purity-55">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i64</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-55"><a class="header" href="#annotations-55">Annotations</a></h4>
<p>None.</p>
<h3 id="extcodesize"><a class="header" href="#extcodesize"><code>extcodesize</code></a></h3>
<p>(<code>Expression::ExtCodeSize</code>)</p>
<h4 id="description-56"><a class="header" href="#description-56">Description</a></h4>
<p>Size of the code deployed at the given address, in bytes. Returns <code>0</code> for accounts with no deployed code.</p>
<h4 id="syntax-56"><a class="header" href="#syntax-56">Syntax</a></h4>
<pre><code class="language-text">extcodesize($address[: &lt;type&gt;])
</code></pre>
<h4 id="example-56"><a class="header" href="#example-56">Example</a></h4>
<pre><code class="language-text">let v1: i64 := extcodesize(v0: i160)
</code></pre>
<h4 id="operands-56"><a class="header" href="#operands-56">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>address</code></td><td><code>i256</code></td><td>Account address; forward analysis widens to at least <code>i160</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-56"><a class="header" href="#result-and-purity-56">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i64</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-56"><a class="header" href="#annotations-56">Annotations</a></h4>
<p>None.</p>
<h3 id="extcodehash"><a class="header" href="#extcodehash"><code>extcodehash</code></a></h3>
<p>(<code>Expression::ExtCodeHash</code>)</p>
<h4 id="description-57"><a class="header" href="#description-57">Description</a></h4>
<p>Keccak-256 hash of the code deployed at the given address. Returns <code>0</code> for non-existent accounts.</p>
<h4 id="syntax-57"><a class="header" href="#syntax-57">Syntax</a></h4>
<pre><code class="language-text">extcodehash($address[: &lt;type&gt;])
</code></pre>
<h4 id="example-57"><a class="header" href="#example-57">Example</a></h4>
<pre><code class="language-text">let v1 := extcodehash(v0: i160)
</code></pre>
<h4 id="operands-57"><a class="header" href="#operands-57">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>address</code></td><td><code>i256</code></td><td>Account address; forward analysis widens to at least <code>i160</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-57"><a class="header" href="#result-and-purity-57">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-57"><a class="header" href="#annotations-57">Annotations</a></h4>
<p>None.</p>
<h3 id="balance"><a class="header" href="#balance"><code>balance</code></a></h3>
<p>(<code>Expression::Balance</code>)</p>
<h4 id="description-58"><a class="header" href="#description-58">Description</a></h4>
<p>Balance (in wei) of the given account address. Use <a href="#selfbalance"><code>selfbalance</code></a> for the contract executing the current call frame (cheaper).</p>
<h4 id="syntax-58"><a class="header" href="#syntax-58">Syntax</a></h4>
<pre><code class="language-text">balance($address[: &lt;type&gt;])
</code></pre>
<h4 id="example-58"><a class="header" href="#example-58">Example</a></h4>
<pre><code class="language-text">let v1 := balance(v0: i160)
</code></pre>
<h4 id="operands-58"><a class="header" href="#operands-58">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>address</code></td><td><code>i256</code></td><td>Account address; forward analysis widens to at least <code>i160</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-58"><a class="header" href="#result-and-purity-58">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-58"><a class="header" href="#annotations-58">Annotations</a></h4>
<p>None.</p>
<h3 id="mload"><a class="header" href="#mload"><code>mload</code></a></h3>
<p>(<code>Expression::MLoad</code>)</p>
<h4 id="description-59"><a class="header" href="#description-59">Description</a></h4>
<p>Read a 32-byte word from emulated EVM linear memory at <code>offset</code>. The word is read big-endian per EVM semantics. Pure per IR, but reads after writes return the new value; the memory passes track read/write dependencies separately.</p>
<h4 id="syntax-59"><a class="header" href="#syntax-59">Syntax</a></h4>
<pre><code class="language-text">mload($offset[: &lt;type&gt;]) [/* &lt;region&gt; */]
</code></pre>
<h4 id="example-59"><a class="header" href="#example-59">Example</a></h4>
<pre><code class="language-text">let v1 := mload(v0: i64)
let v2: i32 := mload(v3: i64) /* free_ptr */
</code></pre>
<h4 id="operands-59"><a class="header" href="#operands-59">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>offset</code></td><td><code>i256</code></td><td>Byte offset into linear memory; forward analysis widens to at least <code>i64</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-59"><a class="header" href="#result-and-purity-59">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i32</code> when region is <code>FreePointerSlot</code>; <code>i256</code> otherwise</td><td>Pure (per IR; see Description)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-59"><a class="header" href="#annotations-59">Annotations</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Source field</th><th>Printed as</th></tr>
</thead>
<tbody>
<tr><td><code>region:</code> <a href="#memoryregion"><code>MemoryRegion</code></a></td><td><code>/* scratch */</code> · <code>/* free_ptr */</code> · <code>/* dynamic */</code> (<code>Unknown</code> is suppressed)</td></tr>
</tbody>
</table>
</div>
<p>Same tagging rules as <a href="#mstore"><code>mstore</code></a>. The region also determines the result width: a load from <code>FreePointerSlot</code> produces an <code>i32</code> since the FMP fits in a pointer-sized word.</p>
<h3 id="sload"><a class="header" href="#sload"><code>sload</code></a></h3>
<p>(<code>Expression::SLoad</code>)</p>
<h4 id="description-60"><a class="header" href="#description-60">Description</a></h4>
<p>Read a 32-byte word from persistent contract storage at the given key. Pure per IR; reads after writes to the same slot return the new value.</p>
<h4 id="syntax-60"><a class="header" href="#syntax-60">Syntax</a></h4>
<pre><code class="language-text">sload($key[: &lt;type&gt;]) [/* slot: 0x&lt;hex&gt; */]
</code></pre>
<h4 id="example-60"><a class="header" href="#example-60">Example</a></h4>
<pre><code class="language-text">let v1 := sload(v0)
let v2 := sload(v3) /* slot: 0x0 */
</code></pre>
<h4 id="operands-60"><a class="header" href="#operands-60">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>key</code></td><td><code>i256</code></td><td>Storage slot.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-60"><a class="header" href="#result-and-purity-60">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure (per IR; see Description)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-60"><a class="header" href="#annotations-60">Annotations</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Source field</th><th>Printed as</th></tr>
</thead>
<tbody>
<tr><td><code>static_slot: Option&lt;BigUint&gt;</code></td><td><code>/* slot: 0x&lt;hex&gt; */</code> when set; suppressed otherwise</td></tr>
</tbody>
</table>
</div>
<p>Same tagging rules as <a href="#sstore"><code>sstore</code></a>. The printer renders the annotation whenever the field is <code>Some</code> and the deduplicator’s canonicalizer partitions signatures by slot; no pass currently writes <code>Some(...)</code>, however, so in present-day dumps the annotation is dormant.</p>
<h3 id="tload"><a class="header" href="#tload"><code>tload</code></a></h3>
<p>(<code>Expression::TLoad</code>)</p>
<h4 id="description-61"><a class="header" href="#description-61">Description</a></h4>
<p>Read a 32-byte word from transient storage at the given key. Transient storage is wiped at the end of the transaction; pair with <a href="#tstore"><code>tstore</code></a>.</p>
<h4 id="syntax-61"><a class="header" href="#syntax-61">Syntax</a></h4>
<pre><code class="language-text">tload($key[: &lt;type&gt;])
</code></pre>
<h4 id="example-61"><a class="header" href="#example-61">Example</a></h4>
<pre><code class="language-text">let v1 := tload(v0)
</code></pre>
<h4 id="operands-61"><a class="header" href="#operands-61">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>key</code></td><td><code>i256</code></td><td>Transient storage slot.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-61"><a class="header" href="#result-and-purity-61">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure (per IR; see Description)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-61"><a class="header" href="#annotations-61">Annotations</a></h4>
<p>None. The IR does not track a static slot for <code>tload</code>.</p>
<h3 id="mapping_sload"><a class="header" href="#mapping_sload"><code>mapping_sload</code></a></h3>
<p>(<code>Expression::MappingSLoad</code>)</p>
<h4 id="description-62"><a class="header" href="#description-62">Description</a></h4>
<p>Compound load for a Solidity mapping element. Equivalent to <code>mstore(0, key); mstore(32, slot); sload(keccak256(0, 64))</code> but emitted as a single outlined call after the <code>mapping_access_outlining</code> pass recognizes the pattern (it fuses a <code>keccak256_pair</code> — itself produced by <code>mem_opt</code>’s keccak fusion — followed by an <code>sload</code> whose key has a single consumer). Only valid when the intermediate hash is used exclusively by this load.</p>
<h4 id="syntax-62"><a class="header" href="#syntax-62">Syntax</a></h4>
<pre><code class="language-text">mapping_sload($key[: &lt;type&gt;], $slot[: &lt;type&gt;])
</code></pre>
<h4 id="example-62"><a class="header" href="#example-62">Example</a></h4>
<pre><code class="language-text">let v2 := mapping_sload(v0: i160, v1)
</code></pre>
<h4 id="operands-62"><a class="header" href="#operands-62">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>key</code></td><td><code>i256</code></td><td>Mapping key; often narrowed to <code>i160</code> for address keys.</td></tr>
<tr><td><code>slot</code></td><td><code>i256</code></td><td>The mapping’s declared storage slot.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-62"><a class="header" href="#result-and-purity-62">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure (per IR; see Description)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-62"><a class="header" href="#annotations-62">Annotations</a></h4>
<p>None. The fused statement’s effective storage slot is the keccak hash of the key and the declared slot, which is never a compile-time constant; no <code>static_slot</code> hint is surfaced.</p>
<h3 id="dataoffset-1"><a class="header" href="#dataoffset-1"><code>dataoffset</code></a></h3>
<p>(<code>Expression::DataOffset</code>)</p>
<h4 id="description-63"><a class="header" href="#description-63">Description</a></h4>
<p>Offset of a named data segment within the deployed code. The identifier is a string carried in the IR’s <code>id: String</code> field; the linker resolves it to a concrete offset.</p>
<h4 id="syntax-63"><a class="header" href="#syntax-63">Syntax</a></h4>
<pre><code class="language-text">dataoffset("&lt;id&gt;")
</code></pre>
<h4 id="example-63"><a class="header" href="#example-63">Example</a></h4>
<pre><code class="language-text">let v0 := dataoffset("MyContract_deployed")
</code></pre>
<h4 id="operands-63"><a class="header" href="#operands-63">Operands</a></h4>
<p>None — the identifier is a quoted string literal in the syntax position, not an operand.</p>
<h4 id="result-and-purity-63"><a class="header" href="#result-and-purity-63">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-63"><a class="header" href="#annotations-63">Annotations</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Source field</th><th>Printed as</th></tr>
</thead>
<tbody>
<tr><td><code>id: String</code></td><td>The quoted identifier in the syntax position (not a comment annotation; it is the expression itself).</td></tr>
</tbody>
</table>
</div>
<h3 id="datasize-1"><a class="header" href="#datasize-1"><code>datasize</code></a></h3>
<p>(<code>Expression::DataSize</code>)</p>
<h4 id="description-64"><a class="header" href="#description-64">Description</a></h4>
<p>Size of a named data segment within the deployed code, in bytes. The identifier is resolved by the linker.</p>
<h4 id="syntax-64"><a class="header" href="#syntax-64">Syntax</a></h4>
<pre><code class="language-text">datasize("&lt;id&gt;")
</code></pre>
<h4 id="example-64"><a class="header" href="#example-64">Example</a></h4>
<pre><code class="language-text">let v0: i64 := datasize("MyContract_deployed")
</code></pre>
<h4 id="operands-64"><a class="header" href="#operands-64">Operands</a></h4>
<p>None — the identifier is a quoted string literal in the syntax position, not an operand.</p>
<h4 id="result-and-purity-64"><a class="header" href="#result-and-purity-64">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i64</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-64"><a class="header" href="#annotations-64">Annotations</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Source field</th><th>Printed as</th></tr>
</thead>
<tbody>
<tr><td><code>id: String</code></td><td>The quoted identifier in the syntax position.</td></tr>
</tbody>
</table>
</div>
<h3 id="loadimmutable"><a class="header" href="#loadimmutable"><code>loadimmutable</code></a></h3>
<p>(<code>Expression::LoadImmutable</code>)</p>
<h4 id="description-65"><a class="header" href="#description-65">Description</a></h4>
<p>Read the value of a named immutable variable. Immutables are written once during contract construction by <code>SetImmutable</code> and read afterwards via this expression.</p>
<h4 id="syntax-65"><a class="header" href="#syntax-65">Syntax</a></h4>
<pre><code class="language-text">loadimmutable("&lt;key&gt;")
</code></pre>
<h4 id="example-65"><a class="header" href="#example-65">Example</a></h4>
<pre><code class="language-text">let v0 := loadimmutable("MyContract.owner")
</code></pre>
<h4 id="operands-65"><a class="header" href="#operands-65">Operands</a></h4>
<p>None — the key is a quoted string literal in the syntax position.</p>
<h4 id="result-and-purity-65"><a class="header" href="#result-and-purity-65">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-65"><a class="header" href="#annotations-65">Annotations</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Source field</th><th>Printed as</th></tr>
</thead>
<tbody>
<tr><td><code>key: String</code></td><td>The quoted identifier in the syntax position.</td></tr>
</tbody>
</table>
</div>
<h3 id="linkersymbol"><a class="header" href="#linkersymbol"><code>linkersymbol</code></a></h3>
<p>(<code>Expression::LinkerSymbol</code>)</p>
<h4 id="description-66"><a class="header" href="#description-66">Description</a></h4>
<p>Address of an external library, resolved by the linker. The path encodes the library’s source location and identifier.</p>
<h4 id="syntax-66"><a class="header" href="#syntax-66">Syntax</a></h4>
<pre><code class="language-text">linkersymbol("&lt;path&gt;")
</code></pre>
<h4 id="example-66"><a class="header" href="#example-66">Example</a></h4>
<pre><code class="language-text">let v0: i160 := linkersymbol("contracts/Library.sol:L")
</code></pre>
<h4 id="operands-66"><a class="header" href="#operands-66">Operands</a></h4>
<p>None — the path is a quoted string literal in the syntax position.</p>
<h4 id="result-and-purity-66"><a class="header" href="#result-and-purity-66">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i160</code></td><td>Pure</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-66"><a class="header" href="#annotations-66">Annotations</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Source field</th><th>Printed as</th></tr>
</thead>
<tbody>
<tr><td><code>path: String</code></td><td>The quoted path in the syntax position.</td></tr>
</tbody>
</table>
</div>
<h3 id="func_name"><a class="header" href="#func_name"><code>&lt;func_name&gt;</code></a></h3>
<p>(<code>Expression::Call</code>; the printer emits <code>func_&lt;id&gt;</code> when no function name is registered)</p>
<h4 id="description-67"><a class="header" href="#description-67">Description</a></h4>
<p>Internal function call. Invokes a user-defined function declared earlier in the same object; the mnemonic is the function’s Yul-level name, or <code>func_&lt;id&gt;</code> if the printer has no name registered for the <code>FunctionId</code>. Distinct from <a href="#call"><code>call</code></a> and the other EVM call-opcode statements, which cross the contract boundary.</p>
<h4 id="syntax-67"><a class="header" href="#syntax-67">Syntax</a></h4>
<pre><code class="language-text">&lt;func_name&gt;([$argument_0[: &lt;type&gt;], $argument_1[: &lt;type&gt;], …])
</code></pre>
<h4 id="example-67"><a class="header" href="#example-67">Example</a></h4>
<pre><code class="language-text">let v3 := abi_decode_uint256(v0, v1, v2)
let v4, v5 := returns_two(v0) // multi-return via let multi-binding
</code></pre>
<h4 id="operands-67"><a class="header" href="#operands-67">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>arguments</code></td><td><code>Vec&lt;Value&gt;</code></td><td>Zero or more argument values, in declaration order; each operand may carry a <code>: &lt;type&gt;</code> suffix.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-67"><a class="header" href="#result-and-purity-67">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>One or more values, widths taken from the callee’s declared return types (or the inferred return widths, narrowed via the interprocedural pass). Falls back to <code>i256</code> when the callee’s returns are unknown to type inference.</td><td>Effectful — the simplifier treats every call as side-effectful regardless of callee body, so unused call bindings are not DCE’d. The transitive purity of the callee is not tracked at the IR level.</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-67"><a class="header" href="#annotations-67">Annotations</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Source field</th><th>Printed as</th></tr>
</thead>
<tbody>
<tr><td><code>function: FunctionId</code></td><td>The callee’s name in the syntax position (or <code>func_&lt;id&gt;</code> if the printer has no name registered).</td></tr>
</tbody>
</table>
</div>
<h2 id="memory-and-storage-writes-1"><a class="header" href="#memory-and-storage-writes-1">Memory and storage writes</a></h2>
<p>The operations in this section all modify external state: emulated EVM linear memory, persistent storage, or transient storage. They are statements (not expressions) and they are never pure. Simplification and deduplication never reorder them with respect to each other or with respect to reverts; the memory passes treat them as the side-effect boundary for their analyses.</p>
<h3 id="mstore"><a class="header" href="#mstore"><code>mstore</code></a></h3>
<p>(<code>Statement::MStore</code>)</p>
<h4 id="description-68"><a class="header" href="#description-68">Description</a></h4>
<p>Write a 32-byte word to emulated EVM linear memory at <code>offset</code>. The word is stored big-endian, matching EVM semantics; the codegen handles the byte swap on PolkaVM’s little-endian RISC-V target.</p>
<h4 id="syntax-68"><a class="header" href="#syntax-68">Syntax</a></h4>
<pre><code class="language-text">mstore($offset[: &lt;type&gt;], $value[: &lt;type&gt;]) [/* &lt;region&gt; */]
</code></pre>
<h4 id="example-68"><a class="header" href="#example-68">Example</a></h4>
<pre><code class="language-text">mstore(v0, v1) // Unknown region; no annotation printed
mstore(v2, v3) /* scratch */ // offset proven to land in 0x00..0x3f
mstore(v4, v5) /* free_ptr */ // offset is exactly 0x40
</code></pre>
<h4 id="operands-68"><a class="header" href="#operands-68">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>offset</code></td><td><code>i256</code></td><td>Byte offset into linear memory; forward analysis widens to at least <code>i64</code>.</td></tr>
<tr><td><code>value</code></td><td><code>i256</code></td><td>The 32-byte word to store. Narrower values are zero-extended at codegen time.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-68"><a class="header" href="#result-and-purity-68">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-68"><a class="header" href="#annotations-68">Annotations</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Source field</th><th>Printed as</th></tr>
</thead>
<tbody>
<tr><td><code>region:</code> <a href="#memoryregion"><code>MemoryRegion</code></a></td><td><code>/* scratch */</code> · <code>/* free_ptr */</code> · <code>/* dynamic */</code> (<code>Unknown</code> is suppressed)</td></tr>
</tbody>
</table>
</div>
<p>Assigned at translation time from the constant offset (if any); consumed by mem_opt, FMP propagation, and byte-swap mode selection.</p>
<h3 id="mstore8"><a class="header" href="#mstore8"><code>mstore8</code></a></h3>
<p>(<code>Statement::MStore8</code>)</p>
<h4 id="description-69"><a class="header" href="#description-69">Description</a></h4>
<p>Write a single byte to emulated EVM linear memory at <code>offset</code>. The low 8 bits of <code>value</code> are stored; the upper bits are ignored. The operation is otherwise identical to <code>mstore</code>: same operand shape, same region tag, same side-effect classification.</p>
<h4 id="syntax-69"><a class="header" href="#syntax-69">Syntax</a></h4>
<pre><code class="language-text">mstore8($offset[: &lt;type&gt;], $value[: &lt;type&gt;]) [/* &lt;region&gt; */]
</code></pre>
<h4 id="example-69"><a class="header" href="#example-69">Example</a></h4>
<pre><code class="language-text">mstore8(v0, v1: i8)
</code></pre>
<h4 id="operands-69"><a class="header" href="#operands-69">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>offset</code></td><td><code>i256</code></td><td>Byte offset into linear memory; forward analysis widens to at least <code>i64</code>.</td></tr>
<tr><td><code>value</code></td><td><code>i256</code></td><td>Only the low 8 bits are stored. Often narrowed to <code>i8</code> by type inference.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-69"><a class="header" href="#result-and-purity-69">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-69"><a class="header" href="#annotations-69">Annotations</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Source field</th><th>Printed as</th></tr>
</thead>
<tbody>
<tr><td><code>region:</code> <a href="#memoryregion"><code>MemoryRegion</code></a></td><td><code>/* scratch */</code> · <code>/* free_ptr */</code> · <code>/* dynamic */</code> (<code>Unknown</code> is suppressed)</td></tr>
</tbody>
</table>
</div>
<p>Same tagging rules as <a href="#mstore"><code>mstore</code></a>. Most <code>mstore8</code>s carry an <code>Unknown</code> region in practice because single-byte writes typically target offsets the translator cannot prove constant.</p>
<h3 id="mcopy"><a class="header" href="#mcopy"><code>mcopy</code></a></h3>
<p>(<code>Statement::MCopy</code>)</p>
<h4 id="description-70"><a class="header" href="#description-70">Description</a></h4>
<p>Copy <code>length</code> bytes from <code>src</code> to <code>dest</code> within emulated EVM linear memory. The Yul builtin <code>mcopy</code> maps directly onto this statement; unlike <code>mstore</code>, it does not carry a region tag because the source and destination ranges may straddle multiple regions.</p>
<h4 id="syntax-70"><a class="header" href="#syntax-70">Syntax</a></h4>
<pre><code class="language-text">mcopy($dest[: &lt;type&gt;], $src[: &lt;type&gt;], $length[: &lt;type&gt;])
</code></pre>
<h4 id="example-70"><a class="header" href="#example-70">Example</a></h4>
<pre><code class="language-text">mcopy(v0, v1, v2)
</code></pre>
<h4 id="operands-70"><a class="header" href="#operands-70">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>dest</code></td><td><code>i256</code></td><td>Destination byte offset in linear memory.</td></tr>
<tr><td><code>src</code></td><td><code>i256</code></td><td>Source byte offset in linear memory.</td></tr>
<tr><td><code>length</code></td><td><code>i256</code></td><td>Number of bytes to copy. Overlapping ranges follow EVM-defined memmove semantics.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-70"><a class="header" href="#result-and-purity-70">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-70"><a class="header" href="#annotations-70">Annotations</a></h4>
<p>None. <code>mcopy</code> carries no region tag because the source and destination ranges may straddle multiple regions, and no static-slot hint because the copy is not storage-bound.</p>
<h3 id="sstore"><a class="header" href="#sstore"><code>sstore</code></a></h3>
<p>(<code>Statement::SStore</code>)</p>
<h4 id="description-71"><a class="header" href="#description-71">Description</a></h4>
<p>Write a 32-byte word to persistent contract storage at <code>key</code>. The operation is the durable counterpart of <code>mstore</code>: the value survives across transactions and is observable to subsequent calls to the contract.</p>
<h4 id="syntax-71"><a class="header" href="#syntax-71">Syntax</a></h4>
<pre><code class="language-text">sstore($key[: &lt;type&gt;], $value[: &lt;type&gt;]) [/* slot: 0x&lt;hex&gt; */]
</code></pre>
<h4 id="example-71"><a class="header" href="#example-71">Example</a></h4>
<pre><code class="language-text">sstore(v0, v1)
sstore(v2, v3) /* slot: 0x0 */
</code></pre>
<h4 id="operands-71"><a class="header" href="#operands-71">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>key</code></td><td><code>i256</code></td><td>Storage slot. May be a constant slot, a keccak-derived slot for mappings or dynamic arrays, or an arbitrary expression.</td></tr>
<tr><td><code>value</code></td><td><code>i256</code></td><td>The 256-bit word to store.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-71"><a class="header" href="#result-and-purity-71">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-71"><a class="header" href="#annotations-71">Annotations</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Source field</th><th>Printed as</th></tr>
</thead>
<tbody>
<tr><td><code>static_slot: Option&lt;BigUint&gt;</code></td><td><code>/* slot: 0x&lt;hex&gt; */</code> when set; suppressed otherwise</td></tr>
</tbody>
</table>
</div>
<p>The printer renders the annotation whenever the field is <code>Some</code>, and the deduplicator’s canonicalizer and mapping-fusion analyses consume it as part of the signature. No pass currently writes <code>Some(...)</code>, so the annotation is dormant in present-day dumps; when absent, alias and dedup analyses fall back to the conservative “may alias any slot” assumption.</p>
<h3 id="tstore"><a class="header" href="#tstore"><code>tstore</code></a></h3>
<p>(<code>Statement::TStore</code>)</p>
<h4 id="description-72"><a class="header" href="#description-72">Description</a></h4>
<p>Write a 32-byte word to transient storage at <code>key</code>. Transient storage is wiped at the end of the transaction, so <code>tstore</code> is the right primitive for per-transaction bookkeeping (reentrancy guards, cached results) without the gas cost of <code>sstore</code> on EVM. On PolkaVM the transient backing store is provided by pallet-revive.</p>
<h4 id="syntax-72"><a class="header" href="#syntax-72">Syntax</a></h4>
<pre><code class="language-text">tstore($key[: &lt;type&gt;], $value[: &lt;type&gt;])
</code></pre>
<h4 id="example-72"><a class="header" href="#example-72">Example</a></h4>
<pre><code class="language-text">tstore(v0, v1)
</code></pre>
<h4 id="operands-72"><a class="header" href="#operands-72">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>key</code></td><td><code>i256</code></td><td>Transient storage slot.</td></tr>
<tr><td><code>value</code></td><td><code>i256</code></td><td>The 256-bit word to store.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-72"><a class="header" href="#result-and-purity-72">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-72"><a class="header" href="#annotations-72">Annotations</a></h4>
<p>None. Unlike <code>sstore</code>, the IR does not track a static slot for <code>tstore</code>: transient storage’s short-lived lifetime makes the slot-aware optimizations less valuable, and the translator does not produce the annotation.</p>
<h3 id="mapping_sstore"><a class="header" href="#mapping_sstore"><code>mapping_sstore</code></a></h3>
<p>(<code>Statement::MappingSStore</code>)</p>
<h4 id="description-73"><a class="header" href="#description-73">Description</a></h4>
<p>Compound store for a Solidity mapping element. Equivalent to <code>mstore(0, key); mstore(32, slot); sstore(keccak256(0, 64), value)</code> but emitted as a single outlined statement after the <code>mapping_access_outlining</code> pass recognizes the pattern (it fuses a <code>keccak256_pair</code> followed by an <code>sstore</code> whose key has a single consumer). Only valid when the intermediate hash is not observed by any other statement.</p>
<h4 id="syntax-73"><a class="header" href="#syntax-73">Syntax</a></h4>
<pre><code class="language-text">mapping_sstore($key[: &lt;type&gt;], $slot[: &lt;type&gt;], $value[: &lt;type&gt;])
</code></pre>
<h4 id="example-73"><a class="header" href="#example-73">Example</a></h4>
<pre><code class="language-text">mapping_sstore(v0, v1, v2)
</code></pre>
<h4 id="operands-73"><a class="header" href="#operands-73">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>key</code></td><td><code>i256</code></td><td>Mapping key. The outlining pass force-widens it to <code>i256</code>, so it always prints at full width, even for address keys.</td></tr>
<tr><td><code>slot</code></td><td><code>i256</code></td><td>The mapping’s declared storage slot. Typically a small constant.</td></tr>
<tr><td><code>value</code></td><td><code>i256</code></td><td>The value to store at the computed storage location.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-73"><a class="header" href="#result-and-purity-73">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-73"><a class="header" href="#annotations-73">Annotations</a></h4>
<p>None. <code>mapping_sstore</code> deliberately drops the <code>static_slot</code> annotation that the original <code>sstore</code> may have carried, because the fused statement’s effective slot is the keccak hash of the key and the declared slot, which is never a compile-time constant.</p>
<h2 id="bulk-copies-1"><a class="header" href="#bulk-copies-1">Bulk copies</a></h2>
<p>Multi-byte memory copies from the EVM-accessible byte sources (code, external code, returndata, embedded data, and calldata) into emulated EVM linear memory. They all take the same shape: a destination memory offset, a source offset, and a length. They are effectful and act as opaque barriers to the memory passes.</p>
<h3 id="codecopy-1"><a class="header" href="#codecopy-1"><code>codecopy</code></a></h3>
<p>(<code>Statement::CodeCopy</code>)</p>
<h4 id="description-74"><a class="header" href="#description-74">Description</a></h4>
<p>Copy <code>length</code> bytes from the currently executing code at <code>offset</code> into emulated EVM linear memory at <code>dest</code>. Reads past the end of code yield zero bytes.</p>
<h4 id="syntax-74"><a class="header" href="#syntax-74">Syntax</a></h4>
<pre><code class="language-text">codecopy($dest[: &lt;type&gt;], $offset[: &lt;type&gt;], $length[: &lt;type&gt;])
</code></pre>
<h4 id="example-74"><a class="header" href="#example-74">Example</a></h4>
<pre><code class="language-text">codecopy(v0, v1, v2)
</code></pre>
<h4 id="operands-74"><a class="header" href="#operands-74">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>dest</code></td><td><code>i256</code></td><td>Destination byte offset in linear memory.</td></tr>
<tr><td><code>offset</code></td><td><code>i256</code></td><td>Source byte offset in the executing code.</td></tr>
<tr><td><code>length</code></td><td><code>i256</code></td><td>Number of bytes to copy.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-74"><a class="header" href="#result-and-purity-74">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-74"><a class="header" href="#annotations-74">Annotations</a></h4>
<p>None.</p>
<h3 id="extcodecopy"><a class="header" href="#extcodecopy"><code>extcodecopy</code></a></h3>
<p>(<code>Statement::ExtCodeCopy</code>)</p>
<h4 id="description-75"><a class="header" href="#description-75">Description</a></h4>
<p>Copy <code>length</code> bytes from the code at <code>address</code> starting at <code>offset</code> into emulated EVM linear memory at <code>dest</code>. Reads beyond the code yield zero bytes; non-existent accounts yield all zeros.</p>
<h4 id="syntax-75"><a class="header" href="#syntax-75">Syntax</a></h4>
<pre><code class="language-text">extcodecopy($address[: &lt;type&gt;], $dest[: &lt;type&gt;], $offset[: &lt;type&gt;], $length[: &lt;type&gt;])
</code></pre>
<h4 id="example-75"><a class="header" href="#example-75">Example</a></h4>
<pre><code class="language-text">extcodecopy(v0: i160, v1, v2, v3)
</code></pre>
<h4 id="operands-75"><a class="header" href="#operands-75">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>address</code></td><td><code>i256</code></td><td>Account whose code to read; forward analysis widens to at least <code>i160</code>.</td></tr>
<tr><td><code>dest</code></td><td><code>i256</code></td><td>Destination byte offset in linear memory.</td></tr>
<tr><td><code>offset</code></td><td><code>i256</code></td><td>Source byte offset in the external code.</td></tr>
<tr><td><code>length</code></td><td><code>i256</code></td><td>Number of bytes to copy.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-75"><a class="header" href="#result-and-purity-75">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-75"><a class="header" href="#annotations-75">Annotations</a></h4>
<p>None.</p>
<h3 id="returndatacopy"><a class="header" href="#returndatacopy"><code>returndatacopy</code></a></h3>
<p>(<code>Statement::ReturnDataCopy</code>)</p>
<h4 id="description-76"><a class="header" href="#description-76">Description</a></h4>
<p>Copy <code>length</code> bytes from the most recent sub-call’s return data starting at <code>offset</code> into emulated EVM linear memory at <code>dest</code>. Per EVM, reads past the return data’s end revert; the memory passes treat this as a potential trap site.</p>
<h4 id="syntax-76"><a class="header" href="#syntax-76">Syntax</a></h4>
<pre><code class="language-text">returndatacopy($dest[: &lt;type&gt;], $offset[: &lt;type&gt;], $length[: &lt;type&gt;])
</code></pre>
<h4 id="example-76"><a class="header" href="#example-76">Example</a></h4>
<pre><code class="language-text">returndatacopy(v0, v1, v2)
</code></pre>
<h4 id="operands-76"><a class="header" href="#operands-76">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>dest</code></td><td><code>i256</code></td><td>Destination byte offset in linear memory.</td></tr>
<tr><td><code>offset</code></td><td><code>i256</code></td><td>Source byte offset in the return-data buffer.</td></tr>
<tr><td><code>length</code></td><td><code>i256</code></td><td>Number of bytes to copy.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-76"><a class="header" href="#result-and-purity-76">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful (may revert on out-of-range reads, per EVM)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-76"><a class="header" href="#annotations-76">Annotations</a></h4>
<p>None.</p>
<h3 id="datacopy"><a class="header" href="#datacopy"><code>datacopy</code></a></h3>
<p>(<code>Statement::DataCopy</code>)</p>
<h4 id="description-77"><a class="header" href="#description-77">Description</a></h4>
<p>Copy <code>length</code> bytes from an embedded data segment starting at <code>offset</code> into emulated EVM linear memory at <code>dest</code>. The source segment is resolved by the linker, typically used to pull constants compiled into the bytecode into runtime memory.</p>
<h4 id="syntax-77"><a class="header" href="#syntax-77">Syntax</a></h4>
<pre><code class="language-text">datacopy($dest[: &lt;type&gt;], $offset[: &lt;type&gt;], $length[: &lt;type&gt;])
</code></pre>
<h4 id="example-77"><a class="header" href="#example-77">Example</a></h4>
<pre><code class="language-text">datacopy(v0, v1, v2)
</code></pre>
<h4 id="operands-77"><a class="header" href="#operands-77">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>dest</code></td><td><code>i256</code></td><td>Destination byte offset in linear memory.</td></tr>
<tr><td><code>offset</code></td><td><code>i256</code></td><td>Source byte offset in the data segment.</td></tr>
<tr><td><code>length</code></td><td><code>i256</code></td><td>Number of bytes to copy.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-77"><a class="header" href="#result-and-purity-77">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-77"><a class="header" href="#annotations-77">Annotations</a></h4>
<p>None.</p>
<h3 id="calldatacopy"><a class="header" href="#calldatacopy"><code>calldatacopy</code></a></h3>
<p>(<code>Statement::CallDataCopy</code>)</p>
<h4 id="description-78"><a class="header" href="#description-78">Description</a></h4>
<p>Copy <code>length</code> bytes from the current call’s calldata starting at <code>offset</code> into emulated EVM linear memory at <code>dest</code>. Reads past the end of calldata yield zero bytes.</p>
<h4 id="syntax-78"><a class="header" href="#syntax-78">Syntax</a></h4>
<pre><code class="language-text">calldatacopy($dest[: &lt;type&gt;], $offset[: &lt;type&gt;], $length[: &lt;type&gt;])
</code></pre>
<h4 id="example-78"><a class="header" href="#example-78">Example</a></h4>
<pre><code class="language-text">calldatacopy(v0, v1, v2)
</code></pre>
<h4 id="operands-78"><a class="header" href="#operands-78">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>dest</code></td><td><code>i256</code></td><td>Destination byte offset in linear memory.</td></tr>
<tr><td><code>offset</code></td><td><code>i256</code></td><td>Source byte offset in calldata.</td></tr>
<tr><td><code>length</code></td><td><code>i256</code></td><td>Number of bytes to copy.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-78"><a class="header" href="#result-and-purity-78">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-78"><a class="header" href="#annotations-78">Annotations</a></h4>
<p>None.</p>
<h2 id="bindings-and-wrappers-1"><a class="header" href="#bindings-and-wrappers-1">Bindings and wrappers</a></h2>
<p>The statements that bind SSA values, hold loose expressions evaluated for their side effects, and write to immutable storage. Every pure expression in this reference’s earlier sections appears on the right-hand side of one of these statements (almost always <code>let</code>).</p>
<h3 id="let"><a class="header" href="#let"><code>let</code></a></h3>
<p>(<code>Statement::Let</code>)</p>
<h4 id="description-79"><a class="header" href="#description-79">Description</a></h4>
<p>SSA binding: evaluate an expression and bind its result(s) to a list of fresh value ids. The <code>let</code> statement is the only mechanism by which pure expressions enter the value namespace; every <code>v&lt;id&gt;</code> in a dump was produced by a <code>let</code> (or by a value-yielding control-flow statement or by a parameter at function entry).</p>
<h4 id="syntax-79"><a class="header" href="#syntax-79">Syntax</a></h4>
<pre><code class="language-text">let $binding_0[, $binding_1, …] := $expression
</code></pre>
<h4 id="example-79"><a class="header" href="#example-79">Example</a></h4>
<pre><code class="language-text">let v3 := add(v0, v1)
let v4, v5 := if v2 [v0, v1] { … } else { … } // multi-binding from a value-yielding If
</code></pre>
<h4 id="operands-79"><a class="header" href="#operands-79">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>bindings</code></td><td><code>Vec&lt;ValueId&gt;</code></td><td>One or more fresh SSA ids to bind. Most expressions produce one value; control-flow statements may produce several.</td></tr>
<tr><td><code>value</code></td><td><code>Expression</code></td><td>The right-hand side; see any of the Pure expression entries.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-79"><a class="header" href="#result-and-purity-79">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None directly — the bound ids carry the expression’s result(s)</td><td>Effectful (binding establishment); the right-hand side’s purity is independent</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-79"><a class="header" href="#annotations-79">Annotations</a></h4>
<p>None.</p>
<h3 id="expression-statement"><a class="header" href="#expression-statement">Expression statement</a></h3>
<p>(<code>Statement::Expression</code>)</p>
<h4 id="description-80"><a class="header" href="#description-80">Description</a></h4>
<p>Wraps an expression evaluated for its observable consequences but whose value is not bound. Typically a zero-return (void) user-defined function call (<code>Expression::Call</code>) evaluated for its side effects, or the discarded void result of a Yul builtin used as a statement (a value-producing expression is bound by a <code>let</code> instead). EVM external calls (<code>call</code>, <code>delegatecall</code>, etc.) and contract creation (<code>create</code>, <code>create2</code>) translate to dedicated <code>Statement::ExternalCall</code> and <code>Statement::Create</code> variants, not through this wrapper.</p>
<h4 id="syntax-80"><a class="header" href="#syntax-80">Syntax</a></h4>
<pre><code class="language-text">$expression
</code></pre>
<h4 id="example-80"><a class="header" href="#example-80">Example</a></h4>
<pre><code class="language-text">update_balance(v0) // void function called for its side effects
</code></pre>
<h4 id="operands-80"><a class="header" href="#operands-80">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>expression</code></td><td><code>Expression</code></td><td>Any expression; result is discarded.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-80"><a class="header" href="#result-and-purity-80">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful (per its statement position)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-80"><a class="header" href="#annotations-80">Annotations</a></h4>
<p>None.</p>
<h3 id="setimmutable"><a class="header" href="#setimmutable"><code>setimmutable</code></a></h3>
<p>(<code>Statement::SetImmutable</code>)</p>
<h4 id="description-81"><a class="header" href="#description-81">Description</a></h4>
<p>Write an immutable variable during contract construction. Immutables are written once in the constructor and read later via <a href="#loadimmutable"><code>loadimmutable</code></a>. The key is a string identifier resolved by the linker.</p>
<h4 id="syntax-81"><a class="header" href="#syntax-81">Syntax</a></h4>
<pre><code class="language-text">setimmutable("&lt;key&gt;", $value[: &lt;type&gt;])
</code></pre>
<h4 id="example-81"><a class="header" href="#example-81">Example</a></h4>
<pre><code class="language-text">setimmutable("MyContract.owner", v0)
</code></pre>
<h4 id="operands-81"><a class="header" href="#operands-81">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>value</code></td><td><code>i256</code></td><td>The value to store; the key is a quoted string literal in the syntax position.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-81"><a class="header" href="#result-and-purity-81">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-81"><a class="header" href="#annotations-81">Annotations</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Source field</th><th>Printed as</th></tr>
</thead>
<tbody>
<tr><td><code>key: String</code></td><td>The quoted identifier in the syntax position.</td></tr>
</tbody>
</table>
</div>
<h2 id="structured-control-flow-1"><a class="header" href="#structured-control-flow-1">Structured control flow</a></h2>
<p>The IR’s control flow is structured: <code>if</code>, <code>switch</code>, and <code>for</code> are statements with explicit nested regions, each carrying input values and yielding output values. The jump-like statements (<code>break</code>, <code>continue</code>, <code>leave</code>) are scoped to their nearest enclosing construct. Nested blocks create lexical scope without otherwise changing control flow.</p>
<h3 id="if"><a class="header" href="#if"><code>if</code></a></h3>
<p>(<code>Statement::If</code>)</p>
<h4 id="description-82"><a class="header" href="#description-82">Description</a></h4>
<p>Conditional execution with optional value yields. The <code>then</code> region runs when <code>condition</code> is non-zero; the <code>else</code> region runs otherwise. If <code>outputs</code> is non-empty, both regions must yield the same number of values and the statement is bound by a <code>let</code>.</p>
<h4 id="syntax-82"><a class="header" href="#syntax-82">Syntax</a></h4>
<pre><code class="language-text">if $condition[: &lt;type&gt;] [[$input_0, $input_1, …]] { … } [else { … }]
</code></pre>
<h4 id="example-82"><a class="header" href="#example-82">Example</a></h4>
<pre><code class="language-text">if v0: i1 {
sstore(v1, v2)
}
let v5, v6 := if v3: i1 [v1, v2] {
let v7: i64 := 0x1 // add widens its operands to the i64 register width
let v8 := add(v2, v7: i64)
yield v1, v8
} else {
yield v1, v2
}
</code></pre>
<h4 id="operands-82"><a class="header" href="#operands-82">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>condition</code></td><td><code>i256</code></td><td>Branch selector; non-zero takes the <code>then</code> region. Often narrowed to <code>i1</code>.</td></tr>
<tr><td><code>inputs</code></td><td><code>Vec&lt;Value&gt;</code></td><td>Values threaded into both regions, printed in square brackets after the condition.</td></tr>
<tr><td>(regions)</td><td>—</td><td>The <code>then_region</code> is mandatory; the <code>else_region</code> is optional and, when absent, implicitly yields the inputs unchanged.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-82"><a class="header" href="#result-and-purity-82">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None for the statement form; for the value-yielding form, one value per <code>outputs</code> binding, types taken from the yielded values</td><td>Effectful (control flow)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-82"><a class="header" href="#annotations-82">Annotations</a></h4>
<p>None.</p>
<h3 id="switch"><a class="header" href="#switch"><code>switch</code></a></h3>
<p>(<code>Statement::Switch</code>)</p>
<h4 id="description-83"><a class="header" href="#description-83">Description</a></h4>
<p>Multi-way dispatch on a scrutinee value. Each case matches a specific constant and runs its region; an optional <code>default</code> region catches non-matching values. Like <code>if</code>, switch may yield values via <code>outputs</code> and accept thread-through values via <code>inputs</code>.</p>
<h4 id="syntax-83"><a class="header" href="#syntax-83">Syntax</a></h4>
<pre><code class="language-text">switch $scrutinee[: &lt;type&gt;] [[$input_0, …]]
case 0x&lt;hex&gt; {
…
}
[case 0x&lt;hex&gt; {
…
} …]
[default {
…
}]
</code></pre>
<h4 id="example-83"><a class="header" href="#example-83">Example</a></h4>
<pre><code class="language-text">switch v0
case 0x0 {
sstore(v1, v2)
}
case 0x1 {
sstore(v1, v3)
}
default {
invalid()
}
</code></pre>
<h4 id="operands-83"><a class="header" href="#operands-83">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>scrutinee</code></td><td><code>i256</code></td><td>The value to compare against each case.</td></tr>
<tr><td><code>inputs</code></td><td><code>Vec&lt;Value&gt;</code></td><td>Values threaded into every case and default region.</td></tr>
<tr><td><code>cases</code></td><td><code>Vec&lt;SwitchCase&gt;</code></td><td>Each case carries a constant <code>value: BigUint</code> and a region.</td></tr>
<tr><td>(default)</td><td>—</td><td>Optional fall-through region.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-83"><a class="header" href="#result-and-purity-83">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None for the statement form; one value per <code>outputs</code> binding for the value-yielding form</td><td>Effectful (control flow)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-83"><a class="header" href="#annotations-83">Annotations</a></h4>
<p>None.</p>
<h3 id="for"><a class="header" href="#for"><code>for</code></a></h3>
<p>(<code>Statement::For</code>)</p>
<h4 id="description-84"><a class="header" href="#description-84">Description</a></h4>
<p>Structured loop with explicit loop-carried variables. Each iteration evaluates <code>condition_statements</code> followed by <code>condition</code>; if the condition is non-zero, the <code>body</code> region runs, then the <code>post</code> region runs, and the loop iterates. Loop-carried variables are passed as SSA values through each region. <code>break</code> exits the loop and <code>continue</code> jumps to the post region.</p>
<h4 id="syntax-84"><a class="header" href="#syntax-84">Syntax</a></h4>
<pre><code class="language-text">for { $variable_0 := $initial_0[, …] }
[// condition statements:
…]
condition: $condition
post [($post_input_variable_0[, …])] {
…
}
body {
… body …
}
</code></pre>
<h4 id="example-84"><a class="header" href="#example-84">Example</a></h4>
<pre><code class="language-text">let v0: i1 := 0x0
let v6 := for { v1 := v0: i1 }
// condition statements:
let v2: i8 := 0xa
condition: lt(v1, v2: i8)
post (v3) {
let v4: i64 := 0x1
let v5 := add(v3, v4: i64)
yield v5
}
body {
sstore(v1, v1)
0x0: void
yield v1
}
</code></pre>
<h4 id="operands-84"><a class="header" href="#operands-84">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>initial_values</code></td><td><code>Vec&lt;Value&gt;</code></td><td>Starting values for the loop-carried variables.</td></tr>
<tr><td><code>loop_variables</code></td><td><code>Vec&lt;ValueId&gt;</code></td><td>SSA ids visible inside condition, body, and post.</td></tr>
<tr><td><code>condition_statements</code></td><td><code>Vec&lt;Statement&gt;</code></td><td>Statements evaluated each iteration <em>before</em> the condition expression; emitted into the loop header block. Printed only when non-empty, behind a <code>// condition statements:</code> comment.</td></tr>
<tr><td><code>condition</code></td><td><code>Expression</code></td><td>Re-evaluated each iteration; non-zero continues, zero exits.</td></tr>
<tr><td><code>body</code></td><td><code>Region</code></td><td>Loop body; yields current loop-carried values.</td></tr>
<tr><td><code>post_input_variables</code></td><td><code>Vec&lt;ValueId&gt;</code></td><td>Input SSA ids for the post region (one per loop-carried variable); receive the body’s yielded values merged with continue-site values via phi nodes in the LLVM codegen.</td></tr>
<tr><td><code>post</code></td><td><code>Region</code></td><td>Runs after each body iteration (and after <code>continue</code>); yields updated loop-carried values.</td></tr>
<tr><td><code>outputs</code></td><td><code>Vec&lt;ValueId&gt;</code></td><td>Final loop-carried values after exit.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-84"><a class="header" href="#result-and-purity-84">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None for the statement form; one value per <code>outputs</code> binding for the value-yielding form</td><td>Effectful (control flow)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-84"><a class="header" href="#annotations-84">Annotations</a></h4>
<p>None.</p>
<h3 id="break"><a class="header" href="#break"><code>break</code></a></h3>
<p>(<code>Statement::Break</code>)</p>
<h4 id="description-85"><a class="header" href="#description-85">Description</a></h4>
<p>Exit the innermost enclosing <code>for</code> loop. Carries the current values of loop-carried variables at the break point; these become the loop’s outputs.</p>
<h4 id="syntax-85"><a class="header" href="#syntax-85">Syntax</a></h4>
<pre><code class="language-text">break
</code></pre>
<h4 id="example-85"><a class="header" href="#example-85">Example</a></h4>
<pre><code class="language-text">if v0 { break [v1, v2] }
</code></pre>
<h4 id="operands-85"><a class="header" href="#operands-85">Operands</a></h4>
<p>The loop-carried <code>values: Vec&lt;Value&gt;</code> print in brackets when non-empty (e.g. <code>break [v1, v2]</code>).</p>
<h4 id="result-and-purity-85"><a class="header" href="#result-and-purity-85">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful (control flow)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-85"><a class="header" href="#annotations-85">Annotations</a></h4>
<p>None.</p>
<h3 id="continue"><a class="header" href="#continue"><code>continue</code></a></h3>
<p>(<code>Statement::Continue</code>)</p>
<h4 id="description-86"><a class="header" href="#description-86">Description</a></h4>
<p>Skip to the post region of the innermost enclosing <code>for</code> loop. Like <code>break</code>, carries the current values of loop-carried variables internally.</p>
<h4 id="syntax-86"><a class="header" href="#syntax-86">Syntax</a></h4>
<pre><code class="language-text">continue
</code></pre>
<h4 id="example-86"><a class="header" href="#example-86">Example</a></h4>
<pre><code class="language-text">if v0 { continue [v1, v2] }
</code></pre>
<h4 id="operands-86"><a class="header" href="#operands-86">Operands</a></h4>
<p>The loop-carried <code>values</code> print in brackets when non-empty (e.g. <code>continue [v1, v2]</code>).</p>
<h4 id="result-and-purity-86"><a class="header" href="#result-and-purity-86">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful (control flow)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-86"><a class="header" href="#annotations-86">Annotations</a></h4>
<p>None.</p>
<h3 id="leave"><a class="header" href="#leave"><code>leave</code></a></h3>
<p>(<code>Statement::Leave</code>)</p>
<h4 id="description-87"><a class="header" href="#description-87">Description</a></h4>
<p>Exit the current function, returning the listed values as the function’s return values. The Yul-level <code>leave</code> keyword translates directly to this statement; the inlining pass eliminates intra-function <code>leave</code>s where possible via the exit-flag transformation.</p>
<h4 id="syntax-87"><a class="header" href="#syntax-87">Syntax</a></h4>
<pre><code class="language-text">leave [[$value_0[: &lt;type&gt;], $value_1[: &lt;type&gt;], …]]
</code></pre>
<h4 id="example-87"><a class="header" href="#example-87">Example</a></h4>
<pre><code class="language-text">leave [v0, v1] // returns v0 and v1 from the function
leave // returns nothing (void function)
</code></pre>
<h4 id="operands-87"><a class="header" href="#operands-87">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>return_values</code></td><td><code>Vec&lt;Value&gt;</code></td><td>Empty for void functions; otherwise one entry per declared return.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-87"><a class="header" href="#result-and-purity-87">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful (control flow)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-87"><a class="header" href="#annotations-87">Annotations</a></h4>
<p>None.</p>
<h3 id="nested-block"><a class="header" href="#nested-block">Nested block</a></h3>
<p>(<code>Statement::Block</code>)</p>
<h4 id="description-88"><a class="header" href="#description-88">Description</a></h4>
<p>A lexical scope without conditional or iterative behavior. The body is a region; control falls through after the region’s statements complete. Used to bound the visibility of inner bindings.</p>
<h4 id="syntax-88"><a class="header" href="#syntax-88">Syntax</a></h4>
<pre><code class="language-text">{
…
}
</code></pre>
<h4 id="example-88"><a class="header" href="#example-88">Example</a></h4>
<pre><code class="language-text">{
let v0 := add(v1, v2)
sstore(v3, v0)
} // v0 is no longer in scope here
</code></pre>
<h4 id="operands-88"><a class="header" href="#operands-88">Operands</a></h4>
<p>None — the body is a region, not an operand.</p>
<h4 id="result-and-purity-88"><a class="header" href="#result-and-purity-88">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful (per the body’s contents)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-88"><a class="header" href="#annotations-88">Annotations</a></h4>
<p>None.</p>
<h2 id="external-interaction-1"><a class="header" href="#external-interaction-1">External interaction</a></h2>
<p>Statements that cross the contract boundary: external calls, contract creation, and event log emission. All produce or rely on external state and act as barriers to memory and storage analyses.</p>
<h3 id="call"><a class="header" href="#call"><code>call</code></a></h3>
<p>(<code>Statement::ExternalCall</code> with <code>CallKind::Call</code>)</p>
<h4 id="description-89"><a class="header" href="#description-89">Description</a></h4>
<p>Standard external call that may transfer value. Reads <code>args_length</code> bytes from emulated EVM linear memory at <code>args_offset</code> as calldata, executes the target, and writes up to <code>ret_length</code> bytes of return data into linear memory at <code>ret_offset</code>. The boolean result indicates success.</p>
<h4 id="syntax-89"><a class="header" href="#syntax-89">Syntax</a></h4>
<pre><code class="language-text">let $result := call($gas[: &lt;type&gt;], $address[: &lt;type&gt;], $value[: &lt;type&gt;], $args_offset[: &lt;type&gt;], $args_length[: &lt;type&gt;], $ret_offset[: &lt;type&gt;], $ret_length[: &lt;type&gt;])
</code></pre>
<h4 id="example-89"><a class="header" href="#example-89">Example</a></h4>
<pre><code class="language-text">let v8 := call(v0: i64, v1: i160, v2, v3: i64, v4: i64, v5: i64, v6: i64)
</code></pre>
<h4 id="operands-89"><a class="header" href="#operands-89">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>gas</code></td><td><code>i256</code></td><td>Gas to forward to the target; forward analysis widens to at least <code>i64</code>.</td></tr>
<tr><td><code>address</code></td><td><code>i256</code></td><td>Callee address; forward analysis widens to at least <code>i160</code>.</td></tr>
<tr><td><code>value</code></td><td><code>i256</code></td><td>Wei to transfer with the call.</td></tr>
<tr><td><code>args_offset</code></td><td><code>i256</code></td><td>Calldata source offset in linear memory; forward analysis widens to at least <code>i64</code>.</td></tr>
<tr><td><code>args_length</code></td><td><code>i256</code></td><td>Calldata length in bytes; forward analysis widens to at least <code>i64</code>.</td></tr>
<tr><td><code>ret_offset</code></td><td><code>i256</code></td><td>Return-data destination offset in linear memory; forward analysis widens to at least <code>i64</code>.</td></tr>
<tr><td><code>ret_length</code></td><td><code>i256</code></td><td>Maximum return-data length; forward analysis widens to at least <code>i64</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-89"><a class="header" href="#result-and-purity-89">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code> (success flag: <code>1</code> on success, <code>0</code> on revert/error; narrowable to <code>i1</code>)</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-89"><a class="header" href="#annotations-89">Annotations</a></h4>
<p>None.</p>
<h3 id="callcode"><a class="header" href="#callcode"><code>callcode</code></a></h3>
<p>(<code>Statement::ExternalCall</code> with <code>CallKind::CallCode</code>)</p>
<h4 id="description-90"><a class="header" href="#description-90">Description</a></h4>
<p>Deprecated EVM opcode that executes the callee’s code in the caller’s context but with the callee’s storage. Not supported by the newyork backend (codegen rejects it); use <a href="#delegatecall"><code>delegatecall</code></a> instead.</p>
<h4 id="syntax-90"><a class="header" href="#syntax-90">Syntax</a></h4>
<pre><code class="language-text">let $result := callcode($gas[: &lt;type&gt;], $address[: &lt;type&gt;], $value[: &lt;type&gt;], $args_offset[: &lt;type&gt;], $args_length[: &lt;type&gt;], $ret_offset[: &lt;type&gt;], $ret_length[: &lt;type&gt;])
</code></pre>
<h4 id="example-90"><a class="header" href="#example-90">Example</a></h4>
<pre><code class="language-text">let v8 := callcode(v0: i64, v1: i160, v2, v3: i64, v4: i64, v5: i64, v6: i64)
</code></pre>
<h4 id="operands-90"><a class="header" href="#operands-90">Operands</a></h4>
<p>Same shape as <a href="#call"><code>call</code></a>.</p>
<h4 id="result-and-purity-90"><a class="header" href="#result-and-purity-90">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code> (success flag; narrowable to <code>i1</code>)</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-90"><a class="header" href="#annotations-90">Annotations</a></h4>
<p>None.</p>
<h3 id="delegatecall"><a class="header" href="#delegatecall"><code>delegatecall</code></a></h3>
<p>(<code>Statement::ExternalCall</code> with <code>CallKind::DelegateCall</code>)</p>
<h4 id="description-91"><a class="header" href="#description-91">Description</a></h4>
<p>Execute the callee’s code in the caller’s context: same storage, same sender, same call value. The standard mechanism for library calls and proxy patterns. No <code>value</code> operand (the caller’s call value is inherited).</p>
<h4 id="syntax-91"><a class="header" href="#syntax-91">Syntax</a></h4>
<pre><code class="language-text">let $result := delegatecall($gas[: &lt;type&gt;], $address[: &lt;type&gt;], $args_offset[: &lt;type&gt;], $args_length[: &lt;type&gt;], $ret_offset[: &lt;type&gt;], $ret_length[: &lt;type&gt;])
</code></pre>
<h4 id="example-91"><a class="header" href="#example-91">Example</a></h4>
<pre><code class="language-text">let v7 := delegatecall(v0: i64, v1: i160, v2: i64, v3: i64, v4: i64, v5: i64)
</code></pre>
<h4 id="operands-91"><a class="header" href="#operands-91">Operands</a></h4>
<p>Same shape as <a href="#call"><code>call</code></a> minus the <code>value</code> operand.</p>
<h4 id="result-and-purity-91"><a class="header" href="#result-and-purity-91">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code> (success flag; narrowable to <code>i1</code>)</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-91"><a class="header" href="#annotations-91">Annotations</a></h4>
<p>None.</p>
<h3 id="staticcall"><a class="header" href="#staticcall"><code>staticcall</code></a></h3>
<p>(<code>Statement::ExternalCall</code> with <code>CallKind::StaticCall</code>)</p>
<h4 id="description-92"><a class="header" href="#description-92">Description</a></h4>
<p>Read-only external call. Any state modification in the callee (including nested calls) causes the call to revert. No <code>value</code> operand.</p>
<h4 id="syntax-92"><a class="header" href="#syntax-92">Syntax</a></h4>
<pre><code class="language-text">let $result := staticcall($gas[: &lt;type&gt;], $address[: &lt;type&gt;], $args_offset[: &lt;type&gt;], $args_length[: &lt;type&gt;], $ret_offset[: &lt;type&gt;], $ret_length[: &lt;type&gt;])
</code></pre>
<h4 id="example-92"><a class="header" href="#example-92">Example</a></h4>
<pre><code class="language-text">let v7 := staticcall(v0: i64, v1: i160, v2: i64, v3: i64, v4: i64, v5: i64)
</code></pre>
<h4 id="operands-92"><a class="header" href="#operands-92">Operands</a></h4>
<p>Same shape as <a href="#call"><code>call</code></a> minus the <code>value</code> operand.</p>
<h4 id="result-and-purity-92"><a class="header" href="#result-and-purity-92">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code> (success flag; narrowable to <code>i1</code>)</td><td>Effectful (no state writes, but still an external boundary and may revert)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-92"><a class="header" href="#annotations-92">Annotations</a></h4>
<p>None.</p>
<h3 id="create"><a class="header" href="#create"><code>create</code></a></h3>
<p>(<code>Statement::Create</code> with <code>CreateKind::Create</code>)</p>
<h4 id="description-93"><a class="header" href="#description-93">Description</a></h4>
<p>Deploy a new contract with the given init-code bytes, transferring <code>value</code> wei from the caller. The new contract’s address is derived from the caller’s address and nonce; on failure the result is <code>0</code>.</p>
<h4 id="syntax-93"><a class="header" href="#syntax-93">Syntax</a></h4>
<pre><code class="language-text">let $result := create($value[: &lt;type&gt;], $offset[: &lt;type&gt;], $length[: &lt;type&gt;])
</code></pre>
<h4 id="example-93"><a class="header" href="#example-93">Example</a></h4>
<pre><code class="language-text">let v4 := create(v0, v1: i64, v2: i64)
</code></pre>
<h4 id="operands-93"><a class="header" href="#operands-93">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>value</code></td><td><code>i256</code></td><td>Wei to transfer to the new contract.</td></tr>
<tr><td><code>offset</code></td><td><code>i256</code></td><td>Linear-memory offset of the init code; forward analysis widens to at least <code>i64</code>.</td></tr>
<tr><td><code>length</code></td><td><code>i256</code></td><td>Length of the init code in bytes; forward analysis widens to at least <code>i64</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-93"><a class="header" href="#result-and-purity-93">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code> (created address; narrowable to <code>i160</code> on success, <code>0</code> on failure)</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-93"><a class="header" href="#annotations-93">Annotations</a></h4>
<p>None.</p>
<h3 id="create2"><a class="header" href="#create2"><code>create2</code></a></h3>
<p>(<code>Statement::Create</code> with <code>CreateKind::Create2</code>)</p>
<h4 id="description-94"><a class="header" href="#description-94">Description</a></h4>
<p>Deploy a new contract with a deterministic address derived from the caller’s address, the salt, and the init-code hash. Same operand shape as <a href="#create"><code>create</code></a> plus an additional <code>salt</code>.</p>
<h4 id="syntax-94"><a class="header" href="#syntax-94">Syntax</a></h4>
<pre><code class="language-text">let $result := create2($value[: &lt;type&gt;], $offset[: &lt;type&gt;], $length[: &lt;type&gt;], $salt[: &lt;type&gt;])
</code></pre>
<h4 id="example-94"><a class="header" href="#example-94">Example</a></h4>
<pre><code class="language-text">let v5 := create2(v0, v1: i64, v2: i64, v3)
</code></pre>
<h4 id="operands-94"><a class="header" href="#operands-94">Operands</a></h4>
<p>Same as <a href="#create"><code>create</code></a> plus <code>salt: i256</code>.</p>
<h4 id="result-and-purity-94"><a class="header" href="#result-and-purity-94">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td><code>i256</code> (created address; narrowable to <code>i160</code> on success, <code>0</code> on failure)</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-94"><a class="header" href="#annotations-94">Annotations</a></h4>
<p>None.</p>
<h3 id="logn"><a class="header" href="#logn"><code>log&lt;N&gt;</code></a></h3>
<p>(<code>Statement::Log</code>)</p>
<h4 id="description-95"><a class="header" href="#description-95">Description</a></h4>
<p>Emit an event log entry. The mnemonic suffix <code>&lt;N&gt;</code> is the number of indexed topics (<code>0</code> through <code>4</code>), determined by the length of the IR’s <code>topics</code> field. The data portion is read from <code>length</code> bytes of emulated EVM linear memory at <code>offset</code>.</p>
<h4 id="syntax-95"><a class="header" href="#syntax-95">Syntax</a></h4>
<pre><code class="language-text">log&lt;N&gt;($offset[: &lt;type&gt;], $length[: &lt;type&gt;][, $topic_0[: &lt;type&gt;], …])
</code></pre>
<h4 id="example-95"><a class="header" href="#example-95">Example</a></h4>
<pre><code class="language-text">log0(v0: i64, v1: i64) // no topics
log2(v0: i64, v1: i64, v2, v3) // two topics
</code></pre>
<h4 id="operands-95"><a class="header" href="#operands-95">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>offset</code></td><td><code>i256</code></td><td>Data source offset in linear memory; forward analysis widens to at least <code>i64</code>.</td></tr>
<tr><td><code>length</code></td><td><code>i256</code></td><td>Data length in bytes; forward analysis widens to at least <code>i64</code>.</td></tr>
<tr><td><code>topics</code></td><td><code>Vec&lt;Value&gt;</code></td><td>Zero to four indexed topic values; the length determines the mnemonic suffix.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-95"><a class="header" href="#result-and-purity-95">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None</td><td>Effectful</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-95"><a class="header" href="#annotations-95">Annotations</a></h4>
<p>None.</p>
<h2 id="termination-1"><a class="header" href="#termination-1">Termination</a></h2>
<p>Statements that end the current call frame. Plain forms (<code>return</code>, <code>revert</code>, <code>stop</code>), unconditional traps (<code>invalid</code>, <code>selfdestruct</code>), and outlined revert variants (<code>panic_revert</code>, <code>error_string_revert</code>, <code>custom_error_revert</code>) that encode common Solidity error patterns into single nodes that can be deduplicated across call sites.</p>
<h3 id="return"><a class="header" href="#return"><code>return</code></a></h3>
<p>(<code>Statement::Return</code>)</p>
<h4 id="description-96"><a class="header" href="#description-96">Description</a></h4>
<p>End the current call frame successfully, returning <code>length</code> bytes from emulated EVM linear memory at <code>offset</code> as the return data.</p>
<h4 id="syntax-96"><a class="header" href="#syntax-96">Syntax</a></h4>
<pre><code class="language-text">return($offset[: &lt;type&gt;], $length[: &lt;type&gt;])
</code></pre>
<h4 id="example-96"><a class="header" href="#example-96">Example</a></h4>
<pre><code class="language-text">return(v0: i64, v1: i64)
</code></pre>
<h4 id="operands-96"><a class="header" href="#operands-96">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>offset</code></td><td><code>i256</code></td><td>Return-data source offset; forward analysis widens to at least <code>i64</code>.</td></tr>
<tr><td><code>length</code></td><td><code>i256</code></td><td>Return-data length; forward analysis widens to at least <code>i64</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-96"><a class="header" href="#result-and-purity-96">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None — terminates the call frame</td><td>Effectful (terminator)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-96"><a class="header" href="#annotations-96">Annotations</a></h4>
<p>None.</p>
<h3 id="revert"><a class="header" href="#revert"><code>revert</code></a></h3>
<p>(<code>Statement::Revert</code>)</p>
<h4 id="description-97"><a class="header" href="#description-97">Description</a></h4>
<p>End the current call frame with a revert, undoing all state changes made during the call, and returning <code>length</code> bytes of revert data from emulated EVM linear memory at <code>offset</code>.</p>
<h4 id="syntax-97"><a class="header" href="#syntax-97">Syntax</a></h4>
<pre><code class="language-text">revert($offset[: &lt;type&gt;], $length[: &lt;type&gt;])
</code></pre>
<h4 id="example-97"><a class="header" href="#example-97">Example</a></h4>
<pre><code class="language-text">revert(v0: i64, v1: i64)
</code></pre>
<h4 id="operands-97"><a class="header" href="#operands-97">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>offset</code></td><td><code>i256</code></td><td>Revert-data source offset; forward analysis widens to at least <code>i64</code>.</td></tr>
<tr><td><code>length</code></td><td><code>i256</code></td><td>Revert-data length; forward analysis widens to at least <code>i64</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-97"><a class="header" href="#result-and-purity-97">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None — terminates the call frame</td><td>Effectful (terminator)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-97"><a class="header" href="#annotations-97">Annotations</a></h4>
<p>None.</p>
<h3 id="stop"><a class="header" href="#stop"><code>stop</code></a></h3>
<p>(<code>Statement::Stop</code>)</p>
<h4 id="description-98"><a class="header" href="#description-98">Description</a></h4>
<p>End the current call frame successfully with empty return data.</p>
<h4 id="syntax-98"><a class="header" href="#syntax-98">Syntax</a></h4>
<pre><code class="language-text">stop()
</code></pre>
<h4 id="example-98"><a class="header" href="#example-98">Example</a></h4>
<pre><code class="language-text">stop()
</code></pre>
<h4 id="operands-98"><a class="header" href="#operands-98">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-98"><a class="header" href="#result-and-purity-98">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None — terminates the call frame</td><td>Effectful (terminator)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-98"><a class="header" href="#annotations-98">Annotations</a></h4>
<p>None.</p>
<h3 id="invalid"><a class="header" href="#invalid"><code>invalid</code></a></h3>
<p>(<code>Statement::Invalid</code>)</p>
<h4 id="description-99"><a class="header" href="#description-99">Description</a></h4>
<p>Unconditional invalid-opcode trap. Consumes all remaining gas and reverts. Used for unreachable branches and assertion failures.</p>
<h4 id="syntax-99"><a class="header" href="#syntax-99">Syntax</a></h4>
<pre><code class="language-text">invalid()
</code></pre>
<h4 id="example-99"><a class="header" href="#example-99">Example</a></h4>
<pre><code class="language-text">invalid()
</code></pre>
<h4 id="operands-99"><a class="header" href="#operands-99">Operands</a></h4>
<p>None.</p>
<h4 id="result-and-purity-99"><a class="header" href="#result-and-purity-99">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None — terminates the call frame</td><td>Effectful (terminator)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-99"><a class="header" href="#annotations-99">Annotations</a></h4>
<p>None.</p>
<h3 id="selfdestruct"><a class="header" href="#selfdestruct"><code>selfdestruct</code></a></h3>
<p>(<code>Statement::SelfDestruct</code>)</p>
<h4 id="description-100"><a class="header" href="#description-100">Description</a></h4>
<p>End the current call frame and transfer the contract’s remaining balance to <code>address</code>. Post-Cancun, the contract storage is not deleted (selfdestruct is effectively deprecated; the opcode still exists for legacy compatibility).</p>
<h4 id="syntax-100"><a class="header" href="#syntax-100">Syntax</a></h4>
<pre><code class="language-text">selfdestruct($address[: &lt;type&gt;])
</code></pre>
<h4 id="example-100"><a class="header" href="#example-100">Example</a></h4>
<pre><code class="language-text">selfdestruct(v0: i160)
</code></pre>
<h4 id="operands-100"><a class="header" href="#operands-100">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>address</code></td><td><code>i256</code></td><td>Recipient of the contract’s balance; forward analysis widens to at least <code>i160</code>.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-100"><a class="header" href="#result-and-purity-100">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None — terminates the call frame</td><td>Effectful (terminator)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-100"><a class="header" href="#annotations-100">Annotations</a></h4>
<p>None.</p>
<h3 id="panic_revert"><a class="header" href="#panic_revert"><code>panic_revert</code></a></h3>
<p>(<code>Statement::PanicRevert</code>)</p>
<h4 id="description-101"><a class="header" href="#description-101">Description</a></h4>
<p>Outlined Solidity panic revert. Equivalent to writing the <code>Panic(uint256)</code> ABI encoding (selector <code>0x4e487b71</code> plus the panic code) into emulated EVM linear memory and reverting, but emitted as a single statement that lowers to one outlined helper call. Common panic codes: <code>0x01</code> assertion failure, <code>0x11</code> arithmetic overflow, <code>0x12</code> division by zero, <code>0x32</code> array-out-of-bounds, <code>0x41</code> memory overflow.</p>
<h4 id="syntax-101"><a class="header" href="#syntax-101">Syntax</a></h4>
<pre><code class="language-text">panic_revert(0x&lt;hex&gt;)
</code></pre>
<h4 id="example-101"><a class="header" href="#example-101">Example</a></h4>
<pre><code class="language-text">panic_revert(0x11) // arithmetic overflow
</code></pre>
<h4 id="operands-101"><a class="header" href="#operands-101">Operands</a></h4>
<p>None — the panic code is stored as a <code>u8</code> field on the IR, not an SSA operand.</p>
<h4 id="result-and-purity-101"><a class="header" href="#result-and-purity-101">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None — terminates the call frame</td><td>Effectful (terminator)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-101"><a class="header" href="#annotations-101">Annotations</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Source field</th><th>Printed as</th></tr>
</thead>
<tbody>
<tr><td><code>code: u8</code></td><td>The panic code in <code>0x&lt;hex&gt;</code> form (two hex digits, zero-padded).</td></tr>
</tbody>
</table>
</div>
<h3 id="error_string_revert"><a class="header" href="#error_string_revert"><code>error_string_revert</code></a></h3>
<p>(<code>Statement::ErrorStringRevert</code>)</p>
<h4 id="description-102"><a class="header" href="#description-102">Description</a></h4>
<p>Outlined Solidity <code>Error(string)</code> revert. Equivalent to writing the <code>Error</code> selector (<code>0x08c379a0</code>), the string offset and length, and up to four 32-byte data words into emulated EVM linear memory and reverting. The string length and the data words are stored as compile-time fields; no SSA operands.</p>
<h4 id="syntax-102"><a class="header" href="#syntax-102">Syntax</a></h4>
<pre><code class="language-text">error_string_revert(&lt;length&gt;, &lt;N&gt;_words)
</code></pre>
<h4 id="example-102"><a class="header" href="#example-102">Example</a></h4>
<pre><code class="language-text">error_string_revert(12, 1_words) // 12-byte string in one 32-byte word
</code></pre>
<h4 id="operands-102"><a class="header" href="#operands-102">Operands</a></h4>
<p>None — the string length and data are compile-time fields, not SSA operands.</p>
<h4 id="result-and-purity-102"><a class="header" href="#result-and-purity-102">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None — terminates the call frame</td><td>Effectful (terminator)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-102"><a class="header" href="#annotations-102">Annotations</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Source field</th><th>Printed as</th></tr>
</thead>
<tbody>
<tr><td><code>length: u8</code></td><td>The string length in bytes, in the first syntax position.</td></tr>
<tr><td><code>data: Vec&lt;BigUint&gt;</code></td><td>The number of 32-byte data words (1–4), printed as <code>&lt;N&gt;_words</code> in the second syntax position. The actual data is stored separately and not shown in the printed form.</td></tr>
</tbody>
</table>
</div>
<h3 id="custom_error_revert"><a class="header" href="#custom_error_revert"><code>custom_error_revert</code></a></h3>
<p>(<code>Statement::CustomErrorRevert</code>)</p>
<h4 id="description-103"><a class="header" href="#description-103">Description</a></h4>
<p>Outlined Solidity custom-error revert. Encodes the error selector (left-shifted by 224 bits) and zero or more argument values into scratch memory and reverts. No FMP load is needed; the encoding uses the scratch region at offset <code>0</code>.</p>
<h4 id="syntax-103"><a class="header" href="#syntax-103">Syntax</a></h4>
<pre><code class="language-text">custom_error_revert(0x&lt;hex&gt;, [$arg_0, $arg_1, …])
</code></pre>
<h4 id="example-103"><a class="header" href="#example-103">Example</a></h4>
<pre><code class="language-text">custom_error_revert(0xa28c4c11, [v0, v1])
</code></pre>
<h4 id="operands-103"><a class="header" href="#operands-103">Operands</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td><code>arguments</code></td><td><code>Vec&lt;Value&gt;</code></td><td>Zero or more argument values; the selector is a compile-time field.</td></tr>
</tbody>
</table>
</div>
<h4 id="result-and-purity-103"><a class="header" href="#result-and-purity-103">Result and purity</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Result</th><th>Purity</th></tr>
</thead>
<tbody>
<tr><td>None — terminates the call frame</td><td>Effectful (terminator)</td></tr>
</tbody>
</table>
</div>
<h4 id="annotations-103"><a class="header" href="#annotations-103">Annotations</a></h4>
<div class="table-wrapper">
<table>
<thead>
<tr><th>Source field</th><th>Printed as</th></tr>
</thead>
<tbody>
<tr><td><code>selector: BigUint</code></td><td>The 4-byte error selector in hex, in the first syntax position. The selector is stored left-shifted by 224 bits; the printer right-shifts it back and prints the bare 4-byte value.</td></tr>
</tbody>
</table>
</div>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="pvm-and-the-pallet-revive-runtime-target"><a class="header" href="#pvm-and-the-pallet-revive-runtime-target">PVM and the pallet-revive runtime target</a></h1>
<p>The <code>revive</code> compiler targets <a href="https://github.com/paritytech/polkavm">PolkaVM (PVM)</a> via <a href="https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/revive">pallet-revive</a> on Polkadot.</p>
<h2 id="target-cpu-configuration"><a class="header" href="#target-cpu-configuration">Target CPU configuration</a></h2>
<p>The exact target CPU configuration can be found <a href="https://github.com/paritytech/revive/blob/8cd10a613625428956eb33c39c9022a91bfbf103/crates/llvm-context/src/target_machine/mod.rs#L22-L32">here</a>.</p>
<blockquote class="blockquote-tag blockquote-tag-note">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
<p>The PVM linker requires fully relocatable ELF objects.</p>
</blockquote>
<h2 id="why-pvm"><a class="header" href="#why-pvm">Why PVM</a></h2>
<p>PVM is a RISC-V based VM designed to overcome the flaws of <a href="https://webassembly.org/">WebAssebmly (Wasm)</a>. Wasm was believed to be a more efficient successor to the rather slow EVM. However, Wasm is far from an ideal target for smart contracts as some of its design decisions are unfavorable for short-lived workloads. The main problem is on-chain Wasm bytecode compilation or interpretation overhead. Prior benchmarks consistently ignoring this overhead seeded the blockchain industry with flawed assumptions: <em>Only when ignoring the startup overhead</em> Wasm is much faster than the slow computing EVM. In practice however, gains are nullified entirely and Wasm loses completely even against very slow VMs like the EVM. Executing Wasm contracts is in fact so inefficient that typical contract workloads are <em>orders of magnitude</em> more expensive than the equivalent EVM variant.</p>
<p>On the other hand, since RISC-V is similar to CPUs found in validator hardware (x86 and ARM), bytecode translation mostly boils down to a linear mapping from one instruction to another. The <em>embedded</em> ISA specification reduces the number of general purpose registers, in turn removing the need for expensive register allocation. This guarantees single-pass <code>O(n)</code> JIT compilation of contract bytecode. The close proximity of PVM bytecode with actual validator CPU bytecode effectively allows to move all expensive compilation workload off-chain. Benchmarks (<a href="https://hackmd.io/@XXX9CM1uSSCWVNFRYaSB5g/HJarTUhJA#Results-execute">1</a>, <a href="https://github.com/paritytech/polkavm/blob/master/BENCHMARKS.md">2</a>) show that with the PVM JIT, sandboxed PVM code executes at around half the speed of native code, which falls into the same ballpark of the state-of-the-art <code>wasmtime</code> Wasm implementation (while EVM sits somewhere around 1/10 to less than 1/100 of native speed). However, the PVM JIT compiler only uses a fraction of the time <code>wasmtime</code> requires to compile the code.</p>
<blockquote class="blockquote-tag blockquote-tag-note">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
<p>The PVM JIT isn’t available yet in <code>pallet-revive</code>. At the time of writing, the contract code is interpreted, which is orders of magnitude slower than the JIT.</p>
</blockquote>
<h2 id="host-environment-pallet-revive"><a class="header" href="#host-environment-pallet-revive">Host environment: <code>pallet-revive</code></a></h2>
<p>The <code>revive</code> compiler targets the <a href="https://docs.rs/pallet-revive/"><code>pallet-revive</code> runtime environment</a>.</p>
<p><code>pallet-revive</code> exposes a <a href="https://docs.rs/pallet-revive/latest/pallet_revive/trait.SyscallDoc.html">syscall like interface</a> for contract interactions with the host environment. This is provided by the <a href="https://crates.io/crates/revive-runtime-api">revive-runtime-api</a> library.</p>
<p>After the initial launch on the Polkadot Asset Hub blockchain, the runtime API is considered stable and backwards compatible indefinitively.</p>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="testing-strategy"><a class="header" href="#testing-strategy">Testing strategy</a></h1>
<p>Contributors are encouraged to implement some appropriate unit and integration tests together with any bug fixes or new feature implementations. However, when it comes to testing the code generation logic, our testing strategy goes way beyond simple unit and integration tests. This chapter explains how the <code>revive</code> compiler implementation is tested for correctness and how we define correctness.</p>
<blockquote class="blockquote-tag blockquote-tag-tip">
<p class="blockquote-tag-title"><svg viewbox="0 0 16 16" width="18" height="18"><path d="M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"></path></svg>Tip</p>
<p>Running the integration tests require the <code>evm</code> tool from <code>go-ethereum</code> in your <code>$PATH</code>.</p>
<p>Either install it using your package manager or to build it from source:</p>
<pre><code class="language-bash">git clone https://github.com/ethereum/go-ethereum/
cd go-ethereum
make all
export PATH=/path/to/go-ethereum/build/bin/:$PATH
</code></pre>
</blockquote>
<h2 id="bug-compatibility-with-ethereum-solidity"><a class="header" href="#bug-compatibility-with-ethereum-solidity">Bug compatibility with Ethereum Solidity</a></h2>
<p>As a Solidity compiler, we aim to preserve contract code semantics as close as possible to Solidity compiled to EVM with the <code>solc</code> reference implementation. As highlighted in the user guide, due to the underlying target difference, this isn’t always possible. However, wherever it is possible, we follow the philosophy of <a href="https://en.wikipedia.org/wiki/Bug_compatibility"><strong>bug compatibility</strong></a> with the Ethereum contracts stack.</p>
<h2 id="differential-integration-tests"><a class="header" href="#differential-integration-tests">Differential integration tests</a></h2>
<p>A high level of bug compatibility with Ethereum is ensured through <a href="https://en.wikipedia.org/wiki/Differential_testing"><strong>differential testing</strong></a> with the Ethereum <code>solc</code> and EVM contracts stack. The <a href="https://crates.io/crates/revive-integration">revive-integration</a> library is the central integration test utility, providing a set of Solidity integration test cases. Further, it implements differential tests against the reference implementation by combining the <a href="https://crates.io/crates/revive-runner">revive-runner</a> sandbox, the <a href="https://github.com/ethereum/go-ethereum/tree/master/cmd/evm">go-ethereum EVM tool</a> and the <a href="https://crates.io/crates/revive-differential">revive-differential</a>.</p>
<p>The <code>revive-runner</code> library provides a <a href="https://en.wikipedia.org/wiki/Declarative_programming"><strong>declarative</strong></a> test <a href="https://github.com/paritytech/revive/blob/main/crates/runner/src/specs.rs">specification format</a>. This vastly simplifies writing differential test cases and removes a lot of room for errors in test logic. Example:</p>
<pre><code class="language-json">{
"differential": true,
"actions": [
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "Bitwise"
}
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "3fa4f245"
}
}
]
}
</code></pre>
<p>Above example instantiates the <code>Bitwise</code> contract and calls it with some defined calldata. The <code>revive-runner</code> library implements a helper wrapper to execute test specs on the go-ethereum standalone <code>evm</code> tool. This allows the <code>revive-runner</code> to execute specs against the EVM and the <code>pallet-revive</code> runtime. Key to differential testing is setting <code>"differential": true</code>, resulting in the following:</p>
<ol>
<li>The <code>Bitwise</code> contract is compiled to EVM and PVM code.</li>
<li>The runner executes the defined <code>actions</code> on the EVM and collects all state changes (storage, balance) and execution results.</li>
<li>The runner executes each action on the PVM. Observed state changes <em>after each step</em> as well as the final execution result is asserted to match the EVM counterparts <strong>exactly</strong>.</li>
</ol>
<p><strong>Note how we never defined any expected outcome manually.</strong> Instead, we simply observe and collect the data defining the “correct” outcome.</p>
<p>Differential testing in combination with declarative test specifications proved to be simple, yet very effective, in ensuring expected Ethereum Solidity semantics on <code>pallet-revive</code>.</p>
<h2 id="the-differential-testing-utility"><a class="header" href="#the-differential-testing-utility">The differential testing utility</a></h2>
<p>A lot of nuanced bugs caused by tiny implementation details inside the <code>revive</code> compiler <em>and</em> the <code>pallet-revive</code> runtime could be identified and eliminated early on thanks to the differential testing strategy. Thus, we decided to take this approach further and created a comprehensive test runner and a large suite of more complex test cases.</p>
<p>The <a href="https://github.com/paritytech/revive-differential-tests/">Revive Differential Tests</a> follow the exact same strategy but implement a much more powerful test spec format, spec runner and reports. This allows differentially testing much more complex test cases (for example testing Uniswap pair creations and swaps), executed via transactions sent to actual blockchain nodes.</p>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="cross-compilation"><a class="header" href="#cross-compilation">Cross compilation</a></h1>
<p>We cross-compile the <code>resolc.js</code> frontend executable to Wasm for running it in a Node.js or browser environment.</p>
<p>The <a href="https://www.musl-libc.org/">musl</a> target is used to obtain statically linked ELF binaries for Linux.</p>
<h2 id="wasm-via-emscripten"><a class="header" href="#wasm-via-emscripten">Wasm via emscripten</a></h2>
<p>The <code>REVIVE_LLVM_TARGET_PREFIX</code> environment variable is used to control the target environment LLVM dependency. This requires a compatible LLVM build, obtainable via the <code>revive-llvm</code> build script. Example:</p>
<pre><code class="language-sh"># Build the host LLVM dependency with PolkaVM target support
make install-llvm
export LLVM_SYS_221_PREFIX=${PWD}/target-llvm/gnu/target-final
# Build the target LLVM dependency with PolkaVM target support
revive-llvm emsdk
source emsdk/emsdk_env.sh
revive-llvm --target-env emscripten build --llvm-projects lld
export REVIVE_LLVM_TARGET_PREFIX=${PWD}/target-llvm/emscripten/target-final
# Build the resolc frontend executable
make install-wasm
make test-wasm
</code></pre>
<h2 id="musl-libc"><a class="header" href="#musl-libc">musl libc</a></h2>
<p><a href="https://github.com/rust-cross/rust-musl-cross">rust-musl-cross</a> is a straightforward way to cross compile Rust to musl. The <a href="https://github.com/paritytech/revive/blob/main/Dockerfile">Dockerfile</a> is an executable example of how to do that.</p>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="faq"><a class="header" href="#faq">FAQ</a></h1>
<h2 id="what-evm-version-do-you-support"><a class="header" href="#what-evm-version-do-you-support">What EVM version do you support?</a></h2>
<p>We neither do nor don’t support any EVM version. We support Solidity versions, starting from <code>solc</code> version 0.8.0 onwards.</p>
<h2 id="is-inline-assembly-supported"><a class="header" href="#is-inline-assembly-supported">Is inline assembly supported</a></h2>
<p>Yes, almost all inline assembly features are supported (<a href="#differences-to-evm">see the differences in Yul translation chapter</a>).</p>
<h2 id="do-you-support-opcode-xy"><a class="header" href="#do-you-support-opcode-xy">Do you support opcode <code>XY</code>?</a></h2>
<p>See above, the same applies.</p>
<h2 id="in-what-solidity-version-should-i-write-my-dapp"><a class="header" href="#in-what-solidity-version-should-i-write-my-dapp">In what Solidity version should I write my dApp?</a></h2>
<p>We generally recommend to always use the latest supported version to profit from latest bugfixes, features and performance improvements.</p>
<p>Find out about the latest supported version by running <code>resolc --supported-solc-versions</code> or checking <a href="https://paritytech.github.io/resolc-bin/">here</a>.</p>
<h2 id="tool-xy-says-the-contract-size-is-larger-than-24kb-and-will-fail-to-deploy"><a class="header" href="#tool-xy-says-the-contract-size-is-larger-than-24kb-and-will-fail-to-deploy">Tool <code>XY</code> says the contract size is larger than 24kb and will fail to deploy?</a></h2>
<p>The 24kb code size restriction only exist for the EVM. Our limit is currently around 1mb and may increase further in the future.</p>
<h2 id="is-resolc-a-drop-in-replacement-for-solc"><a class="header" href="#is-resolc-a-drop-in-replacement-for-solc">Is <code>resolc</code> a drop-in replacement for <code>solc</code>?</a></h2>
<p>No. <code>resolc</code> aims to work similarly to <code>solc</code>, but it’s not considered a drop-in replacement.</p>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="vision-and-roadmap"><a class="header" href="#vision-and-roadmap">Vision and Roadmap</a></h1>
<p>The <code>revive</code> compiler speeds up Solidity contracts significantly. <code>revive</code> provides a decisive edge over other contract platforms. Notably, the compiler eliminates the need of rewriting Solidity dApps in Rust or even as single dApp parachains for scaling reasons. Retaining as high compatibility with Ethereum Solidity as possible keeps entry barriers low.</p>
<p>We believe in Dr. Gavin Wood’s <a href="https://gavwood.com/dappsweb3.html">ĐApps: What Web 3.0 Looks Like</a> manifesto and the ecosystem of the Solidity programming language. Our motivation lies in the realization that for a <em>true</em> web3 revolution, significant scaling efforts, like the ones provided by the PVM and this project, are necessary to unfold.</p>
<h2 id="roadmap"><a class="header" href="#roadmap">Roadmap</a></h2>
<p>The first major release, <code>resolc</code> v1.0.0, emits functional PVM code from given Solidity sources. It relies on <code>solc</code> and LLVM for optimizations. The main priority of this release was delivering a mostly feature complete and safe Solidity v0.8.0 compiler.</p>
<p>Focus for the second major release is on the custom optimization pipeline, which aims to significantly improve emitted code blob sizes.</p>
<p>The below roadmap gives a rough overview of the project’s development timeline.</p>
<p><img src="images/roadmap.svg" alt="Roadmap"></p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
</nav>
</div>
<template id=fa-eye><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"/></svg></span></template>
<template id=fa-eye-slash><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"/></svg></span></template>
<template id=fa-copy><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M502.6 70.63l-61.25-61.25C435.4 3.371 427.2 0 418.7 0H255.1c-35.35 0-64 28.66-64 64l.0195 256C192 355.4 220.7 384 256 384h192c35.2 0 64-28.8 64-64V93.25C512 84.77 508.6 76.63 502.6 70.63zM464 320c0 8.836-7.164 16-16 16H255.1c-8.838 0-16-7.164-16-16L239.1 64.13c0-8.836 7.164-16 16-16h128L384 96c0 17.67 14.33 32 32 32h47.1V320zM272 448c0 8.836-7.164 16-16 16H63.1c-8.838 0-16-7.164-16-16L47.98 192.1c0-8.836 7.164-16 16-16H160V128H63.99c-35.35 0-64 28.65-64 64l.0098 256C.002 483.3 28.66 512 64 512h192c35.2 0 64-28.8 64-64v-32h-47.1L272 448z"/></svg></span></template>
<template id=fa-play><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg></span></template>
<template id=fa-clock-rotate-left><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M75 75L41 41C25.9 25.9 0 36.6 0 57.9V168c0 13.3 10.7 24 24 24H134.1c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24V256c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65V152c0-13.3-10.7-24-24-24z"/></svg></span></template>
<!-- Livereload script (if served using the cli tool) -->
<script>
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload";
const socket = new WebSocket(wsAddress);
socket.onmessage = function (event) {
if (event.data === "reload") {
socket.close();
location.reload();
}
};
window.onbeforeunload = function() {
socket.close();
}
</script>
<script>
window.playground_copyable = true;
</script>
<script src="elasticlunr-ef4e11c1.min.js"></script>
<script src="mark-09e88c2c.min.js"></script>
<script src="searcher-c2a407aa.js"></script>
<script src="clipboard-1626706a.min.js"></script>
<script src="highlight-abc7f01d.js"></script>
<script src="book-a0b12cfe.js"></script>
<!-- Custom JS scripts -->
<script>
window.addEventListener('load', function() {
window.setTimeout(window.print, 100);
});
</script>
</div>
</body>
</html>