<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/assets/rss.xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Max Bernstein&apos;s Blog</title>
        <description></description>
        <link>https://bernsteinbear.com</link>
        <atom:link href="https://bernsteinbear.com/feed.xml" rel="self" type="application/rss+xml" />
        <item shouldShow="false">
            <title>Sorry for marking all the posts as unread</title>
            <description>
              I noticed that the URLs were all a little off (had two slashes
              instead of one) and went in and fixed it. I did not think
              everyone's RSS software was going to freak out the way it did.

              PS: this is a special RSS-only post that is not visible on the
              site. Enjoy.
            </description>
            <pubDate>Wed, 31 Jan 2024 00:00:00 +0000</pubDate>
            <guid isPermaLink="false">rss-only-post-1</guid>
        </item>
        
        <item>
            <title>A quick look at zero-knowledge proofs</title>
            <description>&lt;p&gt;&lt;em&gt;NB: This isn’t about crypto. I don’t care about crypto.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.chrisgregory.me/&quot;&gt;Chris&lt;/a&gt; messaged me the other week asking
if I wanted to implement zero-knowledge proofs. I initially was not interested,
but then he said:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What if I told you there’s a version of them that has nothing to do with
cryptocurrencies? What if I told you it involves graph theory? What if I told
you there’s a 30 line implementation?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now &lt;em&gt;that&lt;/em&gt; was interesting.&lt;/p&gt;

&lt;p&gt;The idea of a zero-knowledge proof (ZKP) is that there are two parties: the
prover and the verifier. The prover asserts that it has a solution to a
(generally NP-complete) problem. The prover can convince the verifier of this
without sharing the actual solution to the problem.&lt;/p&gt;

&lt;p&gt;The canonical example is 3-coloring a graph. That is, the prover asserts that,
for a given (shared) graph, it has a valid 3-coloring. It wants to convince the
verifier of this without revealing the actual color assignment.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;As a quick recap, graph coloring is the problem where given a graph,
we find a way to assign each node a color such that no two adjacent nodes
have the same color. 3-coloring is coloring with at most 3 colors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;!--
graph {
    0 -- 1;
    1 -- 2;
    2 -- 3;
    3 -- 4;
    4 -- 0;
    0 -- 2;
}
--&gt;
&lt;object&gt;
&lt;svg width=&quot;127pt&quot; height=&quot;332pt&quot; viewBox=&quot;0.00 0.00 127.00 332.00&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot;&gt;
&lt;g id=&quot;graph0&quot; class=&quot;graph&quot; transform=&quot;scale(1 1) rotate(0) translate(4 328)&quot;&gt;
&lt;title&gt;%0&lt;/title&gt;
&lt;polygon fill=&quot;#ffffff&quot; stroke=&quot;transparent&quot; points=&quot;-4,4 -4,-328 123,-328 123,4 -4,4&quot; /&gt;
&lt;!-- 0 --&gt;
&lt;g id=&quot;node1&quot; class=&quot;node&quot;&gt;
&lt;title&gt;0&lt;/title&gt;
&lt;ellipse fill=&quot;none&quot; stroke=&quot;#000000&quot; cx=&quot;82&quot; cy=&quot;-306&quot; rx=&quot;27&quot; ry=&quot;18&quot; /&gt;
&lt;text text-anchor=&quot;middle&quot; x=&quot;82&quot; y=&quot;-301.8&quot; font-family=&quot;Times,serif&quot; font-size=&quot;14.00&quot; fill=&quot;#000000&quot;&gt;0&lt;/text&gt;
&lt;/g&gt;
&lt;!-- 1 --&gt;
&lt;g id=&quot;node2&quot; class=&quot;node&quot;&gt;
&lt;title&gt;1&lt;/title&gt;
&lt;ellipse fill=&quot;none&quot; stroke=&quot;#000000&quot; cx=&quot;27&quot; cy=&quot;-234&quot; rx=&quot;27&quot; ry=&quot;18&quot; /&gt;
&lt;text text-anchor=&quot;middle&quot; x=&quot;27&quot; y=&quot;-229.8&quot; font-family=&quot;Times,serif&quot; font-size=&quot;14.00&quot; fill=&quot;#000000&quot;&gt;1&lt;/text&gt;
&lt;/g&gt;
&lt;!-- 0&amp;#45;&amp;#45;1 --&gt;
&lt;g id=&quot;edge1&quot; class=&quot;edge&quot;&gt;
&lt;title&gt;0&amp;#45;&amp;#45;1&lt;/title&gt;
&lt;path fill=&quot;none&quot; stroke=&quot;#000000&quot; d=&quot;M69.5196,-289.6621C60.5186,-277.8788 48.4207,-262.0416 39.431,-250.2733&quot; /&gt;
&lt;/g&gt;
&lt;!-- 2 --&gt;
&lt;g id=&quot;node3&quot; class=&quot;node&quot;&gt;
&lt;title&gt;2&lt;/title&gt;
&lt;ellipse fill=&quot;none&quot; stroke=&quot;#000000&quot; cx=&quot;65&quot; cy=&quot;-162&quot; rx=&quot;27&quot; ry=&quot;18&quot; /&gt;
&lt;text text-anchor=&quot;middle&quot; x=&quot;65&quot; y=&quot;-157.8&quot; font-family=&quot;Times,serif&quot; font-size=&quot;14.00&quot; fill=&quot;#000000&quot;&gt;2&lt;/text&gt;
&lt;/g&gt;
&lt;!-- 0&amp;#45;&amp;#45;2 --&gt;
&lt;g id=&quot;edge6&quot; class=&quot;edge&quot;&gt;
&lt;title&gt;0&amp;#45;&amp;#45;2&lt;/title&gt;
&lt;path fill=&quot;none&quot; stroke=&quot;#000000&quot; d=&quot;M79.8813,-288.0535C76.6237,-260.4592 70.4191,-207.9026 67.1454,-180.1727&quot; /&gt;
&lt;/g&gt;
&lt;!-- 1&amp;#45;&amp;#45;2 --&gt;
&lt;g id=&quot;edge2&quot; class=&quot;edge&quot;&gt;
&lt;title&gt;1&amp;#45;&amp;#45;2&lt;/title&gt;
&lt;path fill=&quot;none&quot; stroke=&quot;#000000&quot; d=&quot;M36.0055,-216.937C41.9752,-205.626 49.7851,-190.8282 55.7952,-179.4407&quot; /&gt;
&lt;/g&gt;
&lt;!-- 3 --&gt;
&lt;g id=&quot;node4&quot; class=&quot;node&quot;&gt;
&lt;title&gt;3&lt;/title&gt;
&lt;ellipse fill=&quot;none&quot; stroke=&quot;#000000&quot; cx=&quot;65&quot; cy=&quot;-90&quot; rx=&quot;27&quot; ry=&quot;18&quot; /&gt;
&lt;text text-anchor=&quot;middle&quot; x=&quot;65&quot; y=&quot;-85.8&quot; font-family=&quot;Times,serif&quot; font-size=&quot;14.00&quot; fill=&quot;#000000&quot;&gt;3&lt;/text&gt;
&lt;/g&gt;
&lt;!-- 2&amp;#45;&amp;#45;3 --&gt;
&lt;g id=&quot;edge3&quot; class=&quot;edge&quot;&gt;
&lt;title&gt;2&amp;#45;&amp;#45;3&lt;/title&gt;
&lt;path fill=&quot;none&quot; stroke=&quot;#000000&quot; d=&quot;M65,-143.8314C65,-133 65,-119.2876 65,-108.4133&quot; /&gt;
&lt;/g&gt;
&lt;!-- 4 --&gt;
&lt;g id=&quot;node5&quot; class=&quot;node&quot;&gt;
&lt;title&gt;4&lt;/title&gt;
&lt;ellipse fill=&quot;none&quot; stroke=&quot;#000000&quot; cx=&quot;92&quot; cy=&quot;-18&quot; rx=&quot;27&quot; ry=&quot;18&quot; /&gt;
&lt;text text-anchor=&quot;middle&quot; x=&quot;92&quot; y=&quot;-13.8&quot; font-family=&quot;Times,serif&quot; font-size=&quot;14.00&quot; fill=&quot;#000000&quot;&gt;4&lt;/text&gt;
&lt;/g&gt;
&lt;!-- 3&amp;#45;&amp;#45;4 --&gt;
&lt;g id=&quot;edge4&quot; class=&quot;edge&quot;&gt;
&lt;title&gt;3&amp;#45;&amp;#45;4&lt;/title&gt;
&lt;path fill=&quot;none&quot; stroke=&quot;#000000&quot; d=&quot;M71.6742,-72.2022C75.8593,-61.0419 81.2321,-46.7143 85.4026,-35.593&quot; /&gt;
&lt;/g&gt;
&lt;!-- 4&amp;#45;&amp;#45;0 --&gt;
&lt;g id=&quot;edge5&quot; class=&quot;edge&quot;&gt;
&lt;title&gt;4&amp;#45;&amp;#45;0&lt;/title&gt;
&lt;path fill=&quot;none&quot; stroke=&quot;#000000&quot; d=&quot;M95.7474,-35.9069C97.7466,-46.3759 99.9966,-59.8759 101,-72 104.9591,-119.8364 104.5886,-132.1343 101,-180 98.0891,-218.8263 90.2002,-263.8211 85.5539,-288.1513&quot; /&gt;
&lt;/g&gt;
&lt;/g&gt;
&lt;/svg&gt;
&lt;/object&gt;

&lt;!--
graph {
    0 [style=filled; color=navy; fontcolor=white];
    1 [style=filled; color=darkgreen; fontcolor=white];
    2 [style=filled; color=crimson; fontcolor=white];
    3 [style=filled; color=navy; fontcolor=white];
    4 [style=filled; color=darkgreen; fontcolor=white];
    0 -- 1;
    1 -- 2;
    2 -- 3;
    3 -- 4;
    4 -- 0;
    0 -- 2;
}
--&gt;
&lt;object&gt;
&lt;svg width=&quot;127pt&quot; height=&quot;332pt&quot; viewBox=&quot;0.00 0.00 127.00 332.00&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot;&gt;
&lt;g id=&quot;graph0&quot; class=&quot;graph&quot; transform=&quot;scale(1 1) rotate(0) translate(4 328)&quot;&gt;
&lt;title&gt;%0&lt;/title&gt;
&lt;polygon fill=&quot;#ffffff&quot; stroke=&quot;transparent&quot; points=&quot;-4,4 -4,-328 123,-328 123,4 -4,4&quot; /&gt;
&lt;!-- 0 --&gt;
&lt;g id=&quot;node1&quot; class=&quot;node&quot;&gt;
&lt;title&gt;0&lt;/title&gt;
&lt;ellipse fill=&quot;#000080&quot; stroke=&quot;#000080&quot; cx=&quot;82&quot; cy=&quot;-306&quot; rx=&quot;27&quot; ry=&quot;18&quot; /&gt;
&lt;text text-anchor=&quot;middle&quot; x=&quot;82&quot; y=&quot;-301.8&quot; font-family=&quot;Times,serif&quot; font-size=&quot;14.00&quot; fill=&quot;#ffffff&quot;&gt;0&lt;/text&gt;
&lt;/g&gt;
&lt;!-- 1 --&gt;
&lt;g id=&quot;node2&quot; class=&quot;node&quot;&gt;
&lt;title&gt;1&lt;/title&gt;
&lt;ellipse fill=&quot;#006400&quot; stroke=&quot;#006400&quot; cx=&quot;27&quot; cy=&quot;-234&quot; rx=&quot;27&quot; ry=&quot;18&quot; /&gt;
&lt;text text-anchor=&quot;middle&quot; x=&quot;27&quot; y=&quot;-229.8&quot; font-family=&quot;Times,serif&quot; font-size=&quot;14.00&quot; fill=&quot;#ffffff&quot;&gt;1&lt;/text&gt;
&lt;/g&gt;
&lt;!-- 0&amp;#45;&amp;#45;1 --&gt;
&lt;g id=&quot;edge1&quot; class=&quot;edge&quot;&gt;
&lt;title&gt;0&amp;#45;&amp;#45;1&lt;/title&gt;
&lt;path fill=&quot;none&quot; stroke=&quot;#000000&quot; d=&quot;M69.5196,-289.6621C60.5186,-277.8788 48.4207,-262.0416 39.431,-250.2733&quot; /&gt;
&lt;/g&gt;
&lt;!-- 2 --&gt;
&lt;g id=&quot;node3&quot; class=&quot;node&quot;&gt;
&lt;title&gt;2&lt;/title&gt;
&lt;ellipse fill=&quot;#dc143c&quot; stroke=&quot;#dc143c&quot; cx=&quot;65&quot; cy=&quot;-162&quot; rx=&quot;27&quot; ry=&quot;18&quot; /&gt;
&lt;text text-anchor=&quot;middle&quot; x=&quot;65&quot; y=&quot;-157.8&quot; font-family=&quot;Times,serif&quot; font-size=&quot;14.00&quot; fill=&quot;#ffffff&quot;&gt;2&lt;/text&gt;
&lt;/g&gt;
&lt;!-- 0&amp;#45;&amp;#45;2 --&gt;
&lt;g id=&quot;edge6&quot; class=&quot;edge&quot;&gt;
&lt;title&gt;0&amp;#45;&amp;#45;2&lt;/title&gt;
&lt;path fill=&quot;none&quot; stroke=&quot;#000000&quot; d=&quot;M79.8813,-288.0535C76.6237,-260.4592 70.4191,-207.9026 67.1454,-180.1727&quot; /&gt;
&lt;/g&gt;
&lt;!-- 1&amp;#45;&amp;#45;2 --&gt;
&lt;g id=&quot;edge2&quot; class=&quot;edge&quot;&gt;
&lt;title&gt;1&amp;#45;&amp;#45;2&lt;/title&gt;
&lt;path fill=&quot;none&quot; stroke=&quot;#000000&quot; d=&quot;M36.0055,-216.937C41.9752,-205.626 49.7851,-190.8282 55.7952,-179.4407&quot; /&gt;
&lt;/g&gt;
&lt;!-- 3 --&gt;
&lt;g id=&quot;node4&quot; class=&quot;node&quot;&gt;
&lt;title&gt;3&lt;/title&gt;
&lt;ellipse fill=&quot;#000080&quot; stroke=&quot;#000080&quot; cx=&quot;65&quot; cy=&quot;-90&quot; rx=&quot;27&quot; ry=&quot;18&quot; /&gt;
&lt;text text-anchor=&quot;middle&quot; x=&quot;65&quot; y=&quot;-85.8&quot; font-family=&quot;Times,serif&quot; font-size=&quot;14.00&quot; fill=&quot;#ffffff&quot;&gt;3&lt;/text&gt;
&lt;/g&gt;
&lt;!-- 2&amp;#45;&amp;#45;3 --&gt;
&lt;g id=&quot;edge3&quot; class=&quot;edge&quot;&gt;
&lt;title&gt;2&amp;#45;&amp;#45;3&lt;/title&gt;
&lt;path fill=&quot;none&quot; stroke=&quot;#000000&quot; d=&quot;M65,-143.8314C65,-133 65,-119.2876 65,-108.4133&quot; /&gt;
&lt;/g&gt;
&lt;!-- 4 --&gt;
&lt;g id=&quot;node5&quot; class=&quot;node&quot;&gt;
&lt;title&gt;4&lt;/title&gt;
&lt;ellipse fill=&quot;#006400&quot; stroke=&quot;#006400&quot; cx=&quot;92&quot; cy=&quot;-18&quot; rx=&quot;27&quot; ry=&quot;18&quot; /&gt;
&lt;text text-anchor=&quot;middle&quot; x=&quot;92&quot; y=&quot;-13.8&quot; font-family=&quot;Times,serif&quot; font-size=&quot;14.00&quot; fill=&quot;#ffffff&quot;&gt;4&lt;/text&gt;
&lt;/g&gt;
&lt;!-- 3&amp;#45;&amp;#45;4 --&gt;
&lt;g id=&quot;edge4&quot; class=&quot;edge&quot;&gt;
&lt;title&gt;3&amp;#45;&amp;#45;4&lt;/title&gt;
&lt;path fill=&quot;none&quot; stroke=&quot;#000000&quot; d=&quot;M71.6742,-72.2022C75.8593,-61.0419 81.2321,-46.7143 85.4026,-35.593&quot; /&gt;
&lt;/g&gt;
&lt;!-- 4&amp;#45;&amp;#45;0 --&gt;
&lt;g id=&quot;edge5&quot; class=&quot;edge&quot;&gt;
&lt;title&gt;4&amp;#45;&amp;#45;0&lt;/title&gt;
&lt;path fill=&quot;none&quot; stroke=&quot;#000000&quot; d=&quot;M95.7474,-35.9069C97.7466,-46.3759 99.9966,-59.8759 101,-72 104.9591,-119.8364 104.5886,-132.1343 101,-180 98.0891,-218.8263 90.2002,-263.8211 85.5539,-288.1513&quot; /&gt;
&lt;/g&gt;
&lt;/g&gt;
&lt;/svg&gt;
&lt;/object&gt;

&lt;p&gt;How do you do this? Assorted blog posts and fancy-looking demonstrations were
interesting but did not help us understand much.&lt;/p&gt;

&lt;p&gt;Chris and I went around in circles for a bit until we decided to take a look at
&lt;a href=&quot;https://dl.acm.org/doi/pdf/10.1145/116825.116852&quot;&gt;one of the original papers&lt;/a&gt;
(PDF) by Goldreich, Micali, and Widgerson. We only really read page 23 (labeled
page 713 in the PDF) but that was enough to get things going.&lt;/p&gt;

&lt;h2 id=&quot;the-papers-protocol&quot;&gt;The paper’s protocol&lt;/h2&gt;

&lt;p&gt;Protocol 4 from the paper describes an interactive 3-color proof session
between the prover (P, with numbered steps) and the verifier (V, with numbered
steps), reproduced here:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;common input&lt;/em&gt; A graph &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G(V, E)&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n = |V|&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m = |E|&lt;/code&gt;).&lt;/p&gt;

  &lt;p&gt;The following four steps are executed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m²&lt;/code&gt; times, each time using independent
coin tosses.&lt;/p&gt;

  &lt;p&gt;(P1) The prover chooses at random an assignment of three colors to the
three independent sets induced by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;φ&lt;/code&gt;, colors the graph using this 3-coloring,
and places these colors in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n&lt;/code&gt; locked boxes each bearing the number of the
corresponding vertex. More specifically, the prover chooses a permutation
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;π ∈R S₃&lt;/code&gt;, places &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;π(φ(i))&lt;/code&gt; in a box marked &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt; (∀ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i ∈ V&lt;/code&gt;), locks all boxes
and sends them (without the keys) to the verifier.&lt;/p&gt;

  &lt;p&gt;(V1) The verifier chooses at random an edge &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e ∈R E&lt;/code&gt; and sends it to the
prover. (Intuitively, the verifier asks to examine the colors of the endpoints
of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e ∈ E&lt;/code&gt;.)&lt;/p&gt;

  &lt;p&gt;(P2) If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e = (u, v) ∈ E&lt;/code&gt;, then the prover reveals the colors of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v&lt;/code&gt; by sending the verifier the keys to boxes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v&lt;/code&gt;. Otherwise, the
prover does nothing.&lt;/p&gt;

  &lt;p&gt;(V2) The verifier opens boxes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v&lt;/code&gt; using the keys received and
checks whether they contain two different elements of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{1, 2, 3}&lt;/code&gt;. If the keys
do not match the boxes, or the contents violate the condition then the
verifier &lt;em&gt;rejects&lt;/em&gt; and stops. Otherwise, the verifier continues to the next
iteration.&lt;/p&gt;

  &lt;p&gt;If the verifier has completed all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m²&lt;/code&gt; iterations then it &lt;em&gt;accepts&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We’ll come back to the number of iterations. For now let’s try to just do &lt;em&gt;one&lt;/em&gt;
iteration. For each step, I’ll annotate the code with “Only prover” or “Only
verifier” so that it’s clear who can see what data.&lt;/p&gt;

&lt;h2 id=&quot;one-iteration&quot;&gt;One iteration&lt;/h2&gt;

&lt;p&gt;We’ll start by sketching out what it means to have a graph. For the example
graphviz graph above, we have the following edge list data structure:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Shared between prover, verifier
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each tuple in the list represents a connection between two numbered nodes.
Fancy stuff. Because it’s an undirected graph, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(0, 1)&lt;/code&gt; means the same as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(1,
0)&lt;/code&gt; so we don’t have to include both. We can also color it:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Only prover
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coloring&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;navy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;darkgreen&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;crimson&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;navy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;darkgreen&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each key is a node number and each value is a color.&lt;/p&gt;

&lt;p&gt;Though finding a 3-coloring of a graph is slow, verifying one is fast—linear
in the number of edges. Let’s verify that we have a valid sample coloring:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# For the reader
# Check each edge to make sure no edge has the same color on each node
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Check that the total number of colors used is 3
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’ll now go through the paper’s steps one by one, writing some code to
accompany each step.&lt;/p&gt;

&lt;h3 id=&quot;tips-and-tricks&quot;&gt;Tips and tricks&lt;/h3&gt;

&lt;p&gt;If you are building alongside the blog post, I recommend using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;random.seed(0)&lt;/code&gt;
so your randomness doesn’t change between runs of your program. I also
recommend setting the environment variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PYTHONHASHSEED&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt; if you are
using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hash&lt;/code&gt; for the same stability reasons.&lt;/p&gt;

&lt;h3 id=&quot;step-p1&quot;&gt;Step P1&lt;/h3&gt;

&lt;p&gt;The first thing we need to do is permute the coloring we have. That is, we
should swap around the color values while maintaining the 3-color property.&lt;/p&gt;

&lt;p&gt;Thankfully, this is easier than it might sound: the names of colors are
meaningless to the 3-coloring; they need only be different along the edges. So
if we do a bijective (A maps to one B, and B came from one A) mapping from old
to new name, this will hold.&lt;/p&gt;

&lt;p&gt;I came up with this function that shuffles the colors, lines them up
side-by-side, makes a table, and then uses that to make a new coloring:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;random&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Only prover
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;permute_three_coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;all_colors&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;new_colors&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sample&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all_colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all_colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;permutation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all_colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;permutation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# For example,
# {0: &quot;crimson&quot;, 1: &quot;navy&quot;, 2: &quot;darkgreen&quot;, 3: &quot;crimson&quot;, 4: &quot;navy&quot;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then we have to place the colors in “locked boxes”. One way to proverbially
lock a box is to apply a one-way function to it: for example, a hash function.
If we hash each color and then pass only the hashes to the verifier, the
verifier cannot open them.&lt;/p&gt;

&lt;p&gt;This example uses the Python standard library hash function for brevity but it
might be better to use a cryptographic hash function like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hashlib.sha256&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Only prover. Wrong!
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hash_coloring_wrong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# For example:
# {0: -6789624683659967261, 1: 7846608853949633950, 2: 6009240650600289446,
#  3: -6789624683659967261, 4: 7846608853949633950}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There’s just one problem with handing these locked boxes to the verifier: two
boxes locked with the same colors will have the same hashes. The verifier would
know the coloring. Even if the exact colors are now hidden, it’s really the
structure of the coloring for which we want to give up zero knowledge.&lt;/p&gt;

&lt;p&gt;To get around this, we can add what’s called a &lt;em&gt;nonce&lt;/em&gt; to each node and its
coloring. That is, each node gets a little bit of random data packed into the
hash so that different nodes’ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;darkgreen&quot;&lt;/code&gt; hash values look different.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Only prover
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;nonce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randrange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;box_coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nonce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hash_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;permuted_coloring&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;permute_three_coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# For example:
# {0: &quot;crimson&quot;, 1: &quot;navy&quot;, 2: &quot;darkgreen&quot;, 3: &quot;crimson&quot;, 4: &quot;navy&quot;}
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;boxed_coloring&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box_coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;permuted_coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# For example:
# {0: (&quot;crimson&quot;, 33), 1: (&quot;navy&quot;, 65), 2: (&quot;darkgreen&quot;, 62),
#  3: (&quot;crimson&quot;, 51), 4: (&quot;navy&quot;, 38)}
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hashed_coloring&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hash_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;boxed_coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# For example:
# {0: -2275004828450249492, 1: 2227921633151400991, 2: -5024343381376265886,
#  3: -5381005702768533635, 4: 1164729608819214729}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Again, you probably don’t want to use the standard library random number
generator for your nonces. You should consider something like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secrets.token_hex()&lt;/code&gt; from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secrets&lt;/code&gt; module (Python 3.6+). Maybe even
consider using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hmac&lt;/code&gt; module.&lt;/p&gt;

&lt;p&gt;Finally, we can send the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hashed_coloring&lt;/code&gt; to the verifier and begin step V1.&lt;/p&gt;

&lt;h3 id=&quot;step-v1&quot;&gt;Step V1&lt;/h3&gt;

&lt;p&gt;Since the verifier knows the graph (but not its colors), it can pick an
arbitrary edge to inspect. It wants to verify that the arbitrary edge it picked
satisfies the 3-color conditions. It sends off a request for an arbitrary edge
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Only verifier
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;revealed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prover_please_reveal_colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is implicitly relying on some global state (the prover knowing what
“session” is active with the verifier). If you have multiple verifiers or
concurrent sessions or something, you may need to thread through some context
identifier in the communication.&lt;/p&gt;

&lt;h3 id=&quot;step-p2&quot;&gt;Step P2&lt;/h3&gt;

&lt;p&gt;The prover, having received this request, sends over the colors and nonces for
each of the nodes in the edge.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Only prover
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;prover_please_reveal_colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edge&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boxed_coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boxed_coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# For example:
# {3: (&apos;crimson&apos;, 51), 4: (&apos;navy&apos;, 38)}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You may be suspicious at this point because we’re leaking some information
about the coloring.&lt;/p&gt;

&lt;p&gt;Note that it’s ok for the prover to reveal the color for one edge, because 1)
the colors have been shuffled once per round and 2) we’re going to apply our
box locking protocol &lt;em&gt;each time we reveal an edge&lt;/em&gt; (also once per round), so
the verifier accumulates no information about our colors between iterations.&lt;/p&gt;

&lt;h3 id=&quot;step-v2&quot;&gt;Step V2&lt;/h3&gt;

&lt;p&gt;The verifier can check that the color+nonce hashes to the hash value given for
each node in step P1. This ensures that the prover is not changing colors
around mid-round. This relies on the verifier and the prover using the same
hash function (and the same hash seed if using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hash&lt;/code&gt;).&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Only verifier
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nonce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;revealed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hashed_coloring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nonce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hash mismatch!&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The verifier can then inspect that the two color values are different. This
gives a small amount of credence (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1/|E|&lt;/code&gt; because you know something about one
edge now) that the graph is 3-colored appropriately because the prover had no
way of knowing which edge the verifier would want to inspect.&lt;/p&gt;

&lt;p&gt;If either of these two conditions doesn’t check out, the verifier &lt;em&gt;rejects&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;probabilities&quot;&gt;Probabilities&lt;/h2&gt;

&lt;p&gt;You have to do at least a couple of rounds of this for the verifier to believe
the prover about the 3-coloring.&lt;/p&gt;

&lt;p&gt;The paper goes on to assert that “the probability that the verifier will
accept (i.e., complete all the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m²&lt;/code&gt; rounds without detecting that “something is
wrong”) is bounded above by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(1 - m⁻¹)^(m²)&lt;/code&gt;” (where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m = |E|&lt;/code&gt;). Which is
pretty good. For a large graph (say, 1000 edges), this falls off reasonably
quickly:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(1 - m⁻¹)^round = &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At 4600 rounds, you’re at 1% possibility of “cheating”. At 10,000 rounds,
you’re at 0.0045% possibility of “cheating”. At &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m² = 1,000,000&lt;/code&gt; rounds, it’s
&lt;em&gt;very low&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;a-networked-demo&quot;&gt;A networked demo&lt;/h2&gt;

&lt;p&gt;Writing Python code that runs in a single process with comments denoting
“prover” and “verifier” is not very satisfying. It does not preclude accidental
data leaking in the slightest. It would be much more satisfying if there was
some sort of barrier, like a process barrier or a network barrier, between the
prover and the verifier.&lt;/p&gt;

&lt;p&gt;For this reason, Chris and I have prepared a server (prover) and client
(verifier) demo. You can click “Run Round” to run a round (and show the
permuted colors). You can visit &lt;a href=&quot;https://zkp-3color-demo.fly.dev/docs&quot;&gt;the docs
page&lt;/a&gt; to see the API docs and build a
client yourself!&lt;/p&gt;

&lt;div id=&quot;output&quot;&gt;
    &lt;button id=&quot;round&quot;&gt;Run Round&lt;/button&gt;
    &lt;noscript&gt;&lt;p&gt;JavaScript is required to run this demo.&lt;/p&gt;&lt;/noscript&gt;
    &lt;p&gt;&lt;i&gt;(If you don&apos;t see a graph right below this message, please wait a
    moment for the server to wake up.)&lt;/i&gt;&lt;/p&gt;
    &lt;div id=&quot;demoGraph&quot;&gt;&lt;/div&gt;
    &lt;ol reversed=&quot;&quot; style=&quot;max-height: 100px; overflow-y: auto;&quot;&gt;&lt;/ol&gt;
&lt;/div&gt;

&lt;script type=&quot;module&quot;&gt;
import * as Viz from &quot;/assets/js/graphviz.js&quot;;

const BASE_URL = &apos;https://zkp-3color-demo.fly.dev&apos;;

async function requestGraph() {
    const responseText = await fetch(`${BASE_URL}/graph`);
    return responseText.json();
}

var sha256;
if (typeof window === &apos;undefined&apos;) {
    const { createHash } = require(&apos;node:crypto&apos;);
    sha256 = async function(content) {
        return createHash(&apos;sha256&apos;).update(content).digest(&apos;hex&apos;);
    }
} else {
    sha256 = async function(message) {
        const msgBuffer = new TextEncoder().encode(message);
        const hashBuffer = await window.crypto.subtle.digest(&apos;SHA-256&apos;, msgBuffer);
        const hashArray = Array.from(new Uint8Array(hashBuffer));
        return hashArray.map(b =&gt; b.toString(16).padStart(2, &apos;0&apos;)).join(&apos;&apos;);
    }
}

async function requestBoxedEdges() {
    const responseText = await fetch(`${BASE_URL}/boxes`);
    return responseText.json();
}

async function requestUnboxedEdge(roundId, edgeIndex) {
    const responseText = await fetch(`${BASE_URL}/edge?round_id=${roundId}&amp;edge_index=${edgeIndex}`);
    const response = await responseText.json();
    if (typeof response !== &apos;object&apos; || Array.isArray(response)) {
        throw new Error(&apos;Invalid response format&apos;);
    }
    return response;
}

async function round(edges) {
    const boxedEdges = await requestBoxedEdges();
    const hashes = boxedEdges.boxes;
    const roundId = boxedEdges.round_id;

    const randomIndex = Math.floor(Math.random() * edges.length);
    const response = await requestUnboxedEdge(roundId, randomIndex);
    const openedBoxes = response.openings;
    const edge = edges[randomIndex];
    const edge0 = `${edge[0]}`;
    const edge1 = `${edge[1]}`;
    if (!openedBoxes.hasOwnProperty(edge0) || !openedBoxes.hasOwnProperty(edge1)) {
        throw new Error(&apos;Response does not contain expected edge keys&apos;);
    }
    const {color: color0, nonce: nonce0} = openedBoxes[edge0];
    const {color: color1, nonce: nonce1} = openedBoxes[edge1];
    const hash0 = await sha256(`${color0}-${nonce0}`);
    const hash1 = await sha256(`${color1}-${nonce1}`);
    if (hash0 !== hashes[edge0] || hash1 !== hashes[edge1]) {
        throw new Error(&apos;Hash mismatch for edge colors&apos;);
    }
    if (color0 === color1) {
        throw new Error(&apos;Adjacent vertices have the same color&apos;);
    }
    const result = {};
    result[`node${edge0}`] = color0;
    result[`node${edge1}`] = color1;
    console.log(result);
    return result;
}

(async function() {
    const viz = await Viz.instance();
    const outputGraph = document.querySelector(&apos;#demoGraph&apos;);
    const graph = await requestGraph();
    const edges = graph.edges;
    // Graphviz numbers nodes node1, node2, ... in layout order, so give each
    // one an explicit id matching its vertex name.
    const nodes = [...new Set(edges.flat())];
    const dotString = `graph {
        ${nodes.map(node =&gt; `${node} [id=&quot;demo-node${node}&quot;]`).join(&apos;; &apos;)};
        ${edges.map(edge =&gt; `${edge[0]} -- ${edge[1]}`).join(&apos;; &apos;)}
    }`;
    const svgElement = viz.renderSVGElement(dotString);
    outputGraph.appendChild(svgElement);

    const outputList = document.querySelector(&apos;#output ol&apos;);
    const roundButton = document.querySelector(&apos;#round&apos;);

    function paint(nodeId, color) {
        const node = document.querySelector(`#demo-${nodeId}`);
        node.querySelector(&apos;ellipse&apos;).style.fill = color ?? &apos;none&apos;;
        node.querySelector(&apos;text&apos;).style.fill = color ? &apos;white&apos; : &apos;black&apos;;
    }

    function logRound(msg) {
        const item = document.createElement(&apos;li&apos;);
        item.textContent = msg;
        outputList.prepend(item);
    }

    function clearColors() {
        for (const node of nodes) {
            paint(`node${node}`, null);
        }
    }

    let i = 0;
    roundButton.addEventListener(&apos;click&apos;, async () =&gt; {
        roundButton.disabled = true;
        clearColors();
        try {
            const edgeColors = await round(edges);
            for (const [node, color] of Object.entries(edgeColors)) {
                paint(node, color);
            }
            const probability = 1 - Math.pow(1 - 1 / edges.length, i + 1);
            const msg = `Success. Confident ${(probability * 100).toFixed(2)}%`;
            logRound(msg);
            console.log(`Round ${i + 1}:`, msg);
            i += 1;
        } catch (error) {
            const msg = `Failure - ${error.message}`;
            logRound(msg);
            console.error(`Round ${i + 1}:`, msg);
            throw error;
        } finally {
            roundButton.disabled = false;
        }
    });
})();
&lt;/script&gt;

&lt;h2 id=&quot;encoding-other-np-complete-problems&quot;&gt;Encoding other NP-complete problems&lt;/h2&gt;

&lt;p&gt;At this point, we’ve shown how you can use a zero-knowledge interactive proof
to verify that someone has a valid 3-coloring of a graph without learning &lt;em&gt;any&lt;/em&gt;
information about the 3-coloring. So what? Is there anything else we can prove
with zero knowledge? Is the interactive proof of 3-coloring just a contrived
party trick without real applications?&lt;/p&gt;

&lt;p&gt;Well…&lt;/p&gt;

&lt;h3 id=&quot;sudoku&quot;&gt;Sudoku&lt;/h3&gt;

&lt;p&gt;Sudoku puzzles are another example of a problem that’s hard to solve and easy
to verify. It is algorithmically hard to fill in 81 squares to satisfy the
constraints of all rows, columns, and boxes containing the digits 1-9, but the
verifier is extremely quick.&lt;/p&gt;

&lt;p&gt;Let’s say we wanted to prove we’ve finished a Sudoku, but we don’t want to give
up a morsel of information about the solution. We can execute an interactive
proof very analogous to 3-coloring! Instead of shuffling colors, we shuffle
digits. Instead of revealing edges, we reveal rows, columns, and boxes.&lt;/p&gt;

&lt;p&gt;Next time your fellow bus rider leans over asking to see your Sudoku solution,
just ask them to go through 90,000 easy steps first!&lt;/p&gt;

&lt;h3 id=&quot;reduction&quot;&gt;Reduction&lt;/h3&gt;

&lt;p&gt;Let’s say we have a hard problem and we’ve computed a solution it, but we don’t
have an obvious algorithm on hand to execute an interactive proof for it.
Thanks to the authors of the paper above, we know that if the problem is
NP-complete there’s an interactive proof for it!&lt;/p&gt;

&lt;p&gt;We use the power of a polynomial time reduction. We (somehow) convert our
solution to a graph and its 3-coloring, then just follow the steps and code
above! The “somehow” is the tricky part, but much research exists on converting
between different NP-complete problems.&lt;/p&gt;

&lt;p&gt;For example, you may want to create a zero-knowledge proof that you know the
prime factors for a very large composite number. Unfortunately, for only double
digit numbers your graph is thousands of nodes, so reducing to 3-coloring has
its limits in practice and you’re better off with more sophisticated proof
techniques than a reduction to 3-coloring.&lt;/p&gt;

&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h2&gt;

&lt;p&gt;After doing a bit of research, we decided that the most common real world use
cases of zero-knowledge proofs (age verification, crypto, etc) aren’t
particularly interesting to us. We enjoyed the graphs and theory of computation
and networked computing though. We hope you had fun playing around with
interactive proofs too.&lt;/p&gt;
</description>
            <pubDate>Fri, 07 Aug 2026 00:00:00 +0000</pubDate>
            <niceDate>August 7, 2026</niceDate>
            <link>https://bernsteinbear.com/blog/zkp/?utm_source=rss</link>
            <guid isPermaLink="true">https://bernsteinbear.com/blog/zkp/</guid>
        </item>
        
        <item>
            <title>The inliner is yielding benefits for ZJIT</title>
            <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href=&quot;https://railsatscale.com/2026-07-28-the-inliner-is-yielding-benefits/&quot;&gt;Rails At Scale&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We recently enabled a really cool feature in ZJIT that makes it feel like a
Real Compiler™: the inliner! We’ll write more about it soon. In this
post, we’ll talk about one excellent concrete benefit we are already seeing and
how it optimizes blocks in pretty much every Ruby program.&lt;/p&gt;

&lt;p&gt;I’ll start off with a refresher on how blocks work in the Ruby interpreter,
then show you how ZJIT understands and optimizes that bytecode, and then show
you the impact of the inliner.&lt;/p&gt;

&lt;h2 id=&quot;ruby-a-refresher&quot;&gt;Ruby: a refresher&lt;/h2&gt;

&lt;p&gt;In the beginning, there were loops.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;length&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;People used them to navigate and manipulate variable-length structures, like
arrays and strings. This was fine.&lt;/p&gt;

&lt;p&gt;Then, in the 1970s, a small group of computer scientists at Palo Alto Research
Center invented a programming language called Smalltalk. One of the core
features of Smalltalk was that everything was an object and computation was
done by sending messages to objects.&lt;/p&gt;

&lt;p&gt;This meant that iteration wouldn’t do at all. Instead, we would have to send
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;do:&lt;/code&gt; message to the array object and pass it a block object.&lt;/p&gt;

&lt;div class=&quot;language-smalltalk highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;ss&quot;&gt;#(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;do:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, in the 1990s, Matz, inspired by Smalltalk and Perl, created Ruby. We
still have “normal” loops but we also have a very Smalltalk-y way of doing it,
too:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When this program gets compiled to Ruby bytecode, it ends up looking like a
mostly normal method call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;each&lt;/code&gt; except that we pass a special kind of
argument to it: a block argument.&lt;/p&gt;

&lt;p&gt;To see how this works inside CRuby, we’re going to look at a listing of YARV
bytecode—CRuby bytecode. For more on YARV, I recommend Kevin Newton’s
excellent &lt;a href=&quot;https://kddnewton.com/2022/11/30/advent-of-yarv-part-0.html&quot;&gt;Advent of
YARV&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ignore most of the bytecode dump below except for the instruction at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0009&lt;/code&gt;,
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send&lt;/code&gt; instruction. We are &lt;em&gt;send&lt;/em&gt;-ing (see? a message!) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;each&lt;/code&gt; with the
block argument &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;block in &amp;lt;main&amp;gt;&lt;/code&gt; (passed a different way than “normal”
arguments, hence the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;argc:0&lt;/code&gt;).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ruby --dump=insns /tmp/arrayeach.rb
== disasm: #&amp;lt;ISeq:&amp;lt;main&amp;gt;@/tmp/arrayeach.rb:1 (1,0)-(5,3)&amp;gt;
local table (...)
[ 2] arr@0      [ 1] sum@1
0000 duparray                   [1, 2, 3]                 (   1)[Li]
0002 setlocal_WC_0              arr@0
0004 putobject_INT2FIX_0_                                 (   2)[Li]
0005 setlocal_WC_0              sum@1
0007 getlocal_WC_0              arr@0                     (   3)[Li]
0009 send                       &amp;lt;calldata!mid:each, argc:0&amp;gt;, block in &amp;lt;main&amp;gt;
0012 leave
...continued below...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;block in &amp;lt;main&amp;gt;&lt;/code&gt; from the bytecode listing above is the generated name of
the &lt;em&gt;instruction sequence&lt;/em&gt; (bytecode) corresponding to the block we passed to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;each&lt;/code&gt;. Its code, shown below, is the next thing in the bytecode dump. You can
see the usage of local variables &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sum&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getlocal&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setlocal&lt;/code&gt;
variants and also &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;opt_plus&lt;/code&gt;, which represents addition (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt;). Again, the
details are not terribly important:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...continued from above...
== disasm: #&amp;lt;ISeq:block in &amp;lt;main&amp;gt;@/tmp/arrayeach.rb:3 (3,9)-(5,3)&amp;gt;
local table (...)
[ 1] a@0&amp;lt;AmbiguousArg&amp;gt;
0000 getlocal_WC_1              sum@1                     (   4)[LiBc]
0002 getlocal_WC_0              a@0
0004 opt_plus                   &amp;lt;calldata!mid:+, argc:1, ARGS_SIMPLE&amp;gt;[CcCr]
0006 dup
0007 setlocal_WC_1              sum@1
0009 leave                                                (   5)[Br]
$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To make this work, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt; has an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;each&lt;/code&gt; method. This &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;each&lt;/code&gt; method takes in
its optional block argument and calls it once for every element in the array.&lt;/p&gt;

&lt;p&gt;As with most of the core data structures, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt;’s methods tend to be written
in C and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;each&lt;/code&gt; is no different. Here is its nice and short definition with
comments added by me:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;VALUE&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;rb_ary_each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VALUE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Some debug-mode-only checks&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ary_verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// If we didn&apos;t pass a block in, return an enumerator object&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RETURN_SIZED_ENUMERATOR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ary_enum_length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// We passed a block in, so call it for every element&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RARRAY_LEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rb_yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RARRAY_AREF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can’t really see the block parameter to the C code because it’s passed in a
special location on the Ruby VM’s own stack called the &lt;em&gt;block handler&lt;/em&gt;. All you
need to know is that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rb_yield&lt;/code&gt; knows where to find that and how to call it.&lt;/p&gt;

&lt;p&gt;There’s just one more thing, which is… hang on, weren’t we building a JIT to
optimize Ruby code? How are we going to optimize this C code?&lt;/p&gt;

&lt;h2 id=&quot;jits-a-refresher&quot;&gt;JITs: a refresher&lt;/h2&gt;

&lt;p&gt;Rewriting code from C to Ruby means that the JIT compiler gets a chance to
introspect the run-time behavior and code. This means that, over time, as the
compiler and the runtime system grow together, more and more code might get
rewritten in Ruby. This started happening a couple of years ago with YJIT.&lt;/p&gt;

&lt;p&gt;YJIT precipitated some interesting changes to Ruby VM internals. For example,
in 2022, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#each&lt;/code&gt; being written in C started to hurt: it was an opaque blob
that the JIT couldn’t reason about. So Kokubun submitted &lt;a href=&quot;https://github.com/ruby/ruby/pull/6687&quot;&gt;a
PR&lt;/a&gt; to rewrite it in Ruby. After some
back and forth, in 2024, Kokubun landed &lt;a href=&quot;https://github.com/ruby/ruby/pull/11955&quot;&gt;a different
PR&lt;/a&gt; that everyone was happy with.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Array&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# :nodoc:&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Primitive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;attr!&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:inline_block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:c_trace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:without_interrupts&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;defined?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Primitive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cexpr!&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)&apos;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;until&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Primitive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rb_jit_ary_at_end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Primitive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rb_jit_ary_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Primitive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rb_jit_fixnum_inc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ah, finally. A version that JITs can reason about.&lt;/p&gt;

&lt;p&gt;I keep saying “reason about” and what that means concretely here is a) that
it’s written in a format that the JIT can ingest and optimize: Ruby, and b) mostly a
brief rehashing of the key lessons in the venerable Smalltalk (!) paper
&lt;a href=&quot;https://dl.acm.org/doi/pdf/10.1145/800017.800542&quot;&gt;Efficient Implementation of the Smalltalk-80
System&lt;/a&gt; (PDF):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Though systems may offer very dynamic behavior, people don’t frequently make
use of wild features all over the place
    &lt;ul&gt;
      &lt;li&gt;Most people pass fewer than 4 types of objects through a given method&lt;/li&gt;
      &lt;li&gt;As a corollary, even if there are many classes in a system, code locality
is super important, and we can take advantage of that&lt;/li&gt;
      &lt;li&gt;Also, most people do not define, re-define, and otherwise continuously
modify method definitions&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(And if you don’t believe me that the lessons still apply, check out the
excellent paper &lt;a href=&quot;https://dl.acm.org/doi/pdf/10.1145/3563834.3567538&quot;&gt;Who You Gonna
Call&lt;/a&gt; (PDF) by Sophie
Kaleba, Octave Larose, Richard Jones, and Stefan Marr.)&lt;/p&gt;

&lt;p&gt;So JITs like to watch what types of objects flow through methods before
compiling them. And JITs like to, since they know the types of objects, cache
method lookups and specialize method invocations on those objects.&lt;/p&gt;

&lt;p&gt;For example, take a look at this code:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;next_capacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; could be anything. There is no way of knowing its type by looking at the
code. This means that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;length&lt;/code&gt; method could be anything. Furthermore, there
is no way of knowing what the return type of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x.length&lt;/code&gt; is, so we can’t
specialize the method lookups or invocations of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; either.&lt;/p&gt;

&lt;p&gt;But! Per our lessons above, likely only a few types flow through this code. Say
the JIT’s profiler notices that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; has historically been an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt;. Then we
might reasonably assume that it will continue to be an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt;, so when we
compile the method, we add a run-time type check: if the type is no longer an
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt;, jump back into the interpreter.&lt;/p&gt;

&lt;p&gt;Let’s see what optimized code ZJIT can construct by combining profiling
information with the above bytecode. ZJIT operates on its own &lt;em&gt;high-level
intermediate representation&lt;/em&gt; called, uncreatively, HIR. In the following HIR
snippet, we can see this very run-time type check (“guard”) for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt;
class:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;fn next_capacity@file.rb:8:
...
bb3(v9:BasicObject, v10:BasicObject):  # v9 is self and v10 is x
  PatchPoint NoSingletonClass(Array@0xdecaf1)
  PatchPoint MethodRedefined(Array@0xdecaf2, length@0xdecaf3, cme:0xdecaf4)
  v35:ArrayExact = GuardType v10, ArrayExact recompile
  v36:CInt64 = ArrayLength v35
  v37:Fixnum = BoxFixnum v36
  # ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(with real pointers replaced by fake ones for readability)&lt;/p&gt;

&lt;p&gt;Because classes are, among other things, collections of methods, this type
information tells us what the call target of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x.length&lt;/code&gt; is: it’s
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#length&lt;/code&gt;! We have a special fast code snippet to read an array’s length
so we do that instead of a method call.&lt;/p&gt;

&lt;p&gt;And in case the methods ever get changed out from underneath us, we leave
behind these markers called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PatchPoint&lt;/code&gt; that invalidate the code.&lt;/p&gt;

&lt;p&gt;Finally, because we know that the result of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#length&lt;/code&gt; is always a small
integer (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fixnum&lt;/code&gt;), we can special case the method lookups for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; as
well.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;fn next_capacity@file.rb:8:
...
bb3(v9:BasicObject, v10:BasicObject):  # v9 is self and v10 is x
  PatchPoint NoSingletonClass(Array@0xdecaf)
  PatchPoint MethodRedefined(Array@0xdecaf, length@0xdecaf, cme:0xdecaf)
  v35:ArrayExact = GuardType v10, ArrayExact recompile
  v36:CInt64 = ArrayLength v35
  v37:Fixnum = BoxFixnum v36
  v18:Fixnum[2] = Const Value(2)
  PatchPoint MethodRedefined(Integer@0xdecaf, *@0xdecaf, cme:0xdecaf)
  v41:Fixnum = FixnumMult v37, v18
  v23:Fixnum[1] = Const Value(1)
  PatchPoint MethodRedefined(Integer@0xdecaf, +@0xdecaf, cme:0xdecaf)
  v45:Fixnum = FixnumAdd v41, v23
  CheckInterrupts
  Return v45
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There you have it. This is how JITs work: observe, assume, specialize. So why
am I telling you all this? How does this circle back to blocks?&lt;/p&gt;

&lt;p&gt;Well, blocks work not quite the same way, but similarly. Instead of having an
object that we call a method on, we just have the target &lt;em&gt;instruction
sequence&lt;/em&gt;&lt;sup id=&quot;fnref:procs-and-such&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:procs-and-such&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. But if we observe that the block argument, in its
special location, has been consistently one object, we can specialize the call
to it.&lt;/p&gt;

&lt;p&gt;This is more or less fine for some cases of code. For example, in the following
code snippet, we only have one caller to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;custom_iterator&lt;/code&gt;, so its profiled
block will be &lt;em&gt;monomorphic&lt;/em&gt; (one observed shape).&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;custom_iterator&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;custom_iterator&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This reinforces what we know and love about the Smalltalk-80 paper: code
locality wins! Yes!&lt;/p&gt;

&lt;p&gt;But unfortunately this falls apart when we start thinking about all of the
core library methods (and potentially the methods and classes you have
stashed away in the grab-bag &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;utils.rb&lt;/code&gt; in your application). Those methods are
probably &lt;em&gt;megamorphic&lt;/em&gt; (many many observed shapes). They probably see all sorts
of stuff because they are general-purpose utilities that everybody needs, all
the time.&lt;/p&gt;

&lt;p&gt;One such example in the Ruby core library is the venerable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#each&lt;/code&gt;
that we saw earlier. Because there are a million different call sites to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#each&lt;/code&gt; across your application and each probably passes a totally
different block, we’re in an unhappy situation. How can we possibly optimize
for so many different blocks? What happened to our code locality? How do we fix
this?&lt;/p&gt;

&lt;h2 id=&quot;the-code-locality-is-in-another-tower&quot;&gt;The code locality is in another tower&lt;/h2&gt;

&lt;p&gt;It’s okay. Code locality still rules. Look at all the various callers of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#each&lt;/code&gt;. They all pass a different, but constant&lt;sup id=&quot;fnref:not-always-constant&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:not-always-constant&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;,
block at the call-site:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;method_a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;some_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;some_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;method_b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;some_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;some_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;method_c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;some_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;some_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So the code locality that we need in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#each&lt;/code&gt; to optimize the code is one
level up at the caller. If we can use that &lt;em&gt;call context&lt;/em&gt;, we can specialize
the code.&lt;/p&gt;

&lt;p&gt;YJIT accomplishes this by &lt;em&gt;splitting&lt;/em&gt;, which is a very natural transformation
for basic block versioning and tracing. Such compilers are good at following
code paths as they would be executed and putting together context across method
calls. However, YJIT’s heuristic for splitting blocks is based on
manual annotations: it will only kick in for certain Ruby library functions
specially annotated with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Primitive.attr! :inline_block&lt;/code&gt; (and the name is a bit
of a misnomer).&lt;/p&gt;

&lt;p&gt;The team that builds ZJIT, a method JIT, decided not to add splitting
facilities. We could split methods (and blocks), but we have an easier time
reasoning about larger code units than YJIT does because we optimize an entire
method at once. So instead, ZJIT chooses to get call context by &lt;em&gt;inlining&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;inlining&quot;&gt;Inlining&lt;/h2&gt;

&lt;p&gt;Method inlining refers to copying the body of the callee into the caller. In
the above example, it means copying the body of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#each&lt;/code&gt; into each of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;method_a&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;method_b&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;method_c&lt;/code&gt;. I don’t mean the Ruby code and I don’t
mean the bytecode: I mean the HIR. ZJIT does this by building the HIR of the
callee (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#each&lt;/code&gt;) into the existing HIR of the caller (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;method_a&lt;/code&gt;, …).&lt;/p&gt;

&lt;p&gt;The illustrious &lt;a href=&quot;/authors/kevin-menard/&quot;&gt;Kevin Menard&lt;/a&gt; wrote ZJIT’s inliner and
he’ll write a post about all the details soon. It’s pretty interesting stuff.&lt;/p&gt;

&lt;p&gt;For now, we can take a look at the (lightly edited) result of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#each&lt;/code&gt;
being inlined into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;method_a&lt;/code&gt;. The details don’t matter, but there are two
things I want to call out:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;What used to be a dynamic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;invokeblock&lt;/code&gt; is now what we call
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InvokeBlockIseqDirect&lt;/code&gt; because we know from the call context what block
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;method_a&lt;/code&gt; is passing to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#each&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;What used to be a method call is now a loop: the condition check is in
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bb10&lt;/code&gt;, the body is in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bb9&lt;/code&gt;, and the stuff after the loop is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bb13&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bb4&lt;/code&gt;.
If you’re interested, try to find the iteration variable and where it gets
incremented. See the footnote&lt;sup id=&quot;fnref:iteration-variable&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:iteration-variable&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; for the answer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a massive improvement over the previous very generic operations.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;fn method_a@/tmp/arrayeach.rb:2:
# ...
bb3(v9:BasicObject, v10:BasicObject):
  # ...
  v55:Fixnum[0] = Const Value(0)
  Jump bb10(v25, v55)
bb10(v68:ArrayExact, v69:Fixnum):
  v73:CInt64 = ArrayLength v68
  v74:Fixnum = BoxFixnum v73
  v75:BoolExact = FixnumGe v69, v74
  v77:CBool = Test v75
  CondBranch v77, bb13(), bb9(v68, v69)
bb13():
  CheckInterrupts
  Jump bb4(v68)
bb9(v90:ArrayExact, v91:Fixnum):
  v96:CInt64 = UnboxFixnum v91
  v97:BasicObject = ArrayAref v90, v96
  v99:CPtr = GetEP 0
  v100:CInt64 = LoadField v99, :VM_ENV_DATA_INDEX_SPECVAL@-0x8
  v101:CInt64[-4] = Const CInt64(-4)
  v102:CInt64 = IntAnd v100, v101
  v103:BasicObject = InvokeBlockIseqDirect (0x16f3d5968), v102, v97
  v107:Fixnum[1] = Const Value(1)
  v108:Fixnum = FixnumAdd v91, v107
  PatchPoint NoEPEscape(each)
  Jump bb10(v90, v108)
bb4(v114:BasicObject):
  PopInlineFrame
  PatchPoint NoEPEscape(method_a)
  CheckInterrupts
  Return v114
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You may notice that there is still one call in the loop: the call to the block
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InvokeBlockIseqDirect&lt;/code&gt;). That’s next on our list to tackle. We are optimistic
that we will soon also be able to inline block calls. Then the whole thing will
&lt;em&gt;really&lt;/em&gt; be just a loop!&lt;/p&gt;

&lt;p&gt;The code that enables us to reason about which block got passed into the
inlined callee (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#each&lt;/code&gt;) only landed &lt;a href=&quot;https://github.com/ruby/ruby/commit/c35fbd6fb27a13842299597d46b49a10f018c5b0&quot;&gt;a couple of days
ago&lt;/a&gt;
(July 10, 2026), written by &lt;a href=&quot;https://github.com/luke-gruber&quot;&gt;Luke Gruber&lt;/a&gt;. This
was one of Luke’s first changes to ZJIT.&lt;/p&gt;

&lt;h2 id=&quot;so-what-does-performance-look-like&quot;&gt;So what does performance look like?&lt;/h2&gt;

&lt;p&gt;Well, for starters, the microbenchmarks that we use as “performance unit tests”
for specific aspects of Ruby went wild. Some benchmarks that were using
block-based looping got &lt;em&gt;much&lt;/em&gt; faster; they were previously bounded by ZJIT’s
block invocation performance.&lt;/p&gt;

&lt;p&gt;Take a look at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cfunc_itself&lt;/code&gt; benchmark, which tests that we can fold away
the call to Ruby’s built-in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;itself&lt;/code&gt; method. ZJIT ends up optimizing the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;times&lt;/code&gt; method to invoke the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;do&lt;/code&gt; block directly, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;do&lt;/code&gt; block gets
optimized to nothing but a guard on the self’s class to make sure it hasn’t
changed.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;run_benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;mi&quot;&gt;500000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;itself&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;itself&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;itself&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;itself&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;itself&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;itself&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;itself&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;itself&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;itself&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;itself&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Because we had previously optimized the body away, the result of the direct
block invocation is a massive speedup:&lt;/p&gt;

&lt;figure style=&quot;margin: 0 auto; max-width: 80%;&quot;&gt;
  &lt;img src=&quot;/assets/img/inliner_invokeblock_cfunc_itself_benchmark.png&quot; /&gt;
  &lt;figcaption&gt;The yellow line represents ZJIT speedup over the interpreter. Higher is better. 35x!&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Other benchmarks also kind of stop making sense because of the amount of
inlining. Our bmethod benchmark, which benchmarked how fast we can call methods
defined with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;define_method&lt;/code&gt;, also stopped measuring anything of use. We’re
going to have to rework the benchmark to be more fair…&lt;/p&gt;

&lt;figure style=&quot;margin: 0 auto; max-width: 80%;&quot;&gt;
  &lt;img src=&quot;/assets/img/inliner_invokeblock_send_bmethod_benchmark.png&quot; /&gt;
  &lt;figcaption&gt;The yellow line represents ZJIT speedup over the interpreter. Higher is better. 139x!&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;As expected, larger Rails benchmarks don’t see a ton of change; they exercise a
diffuse set of features so optimizing any one feature bumps the big benchmarks
only a little bit.&lt;/p&gt;

&lt;p&gt;I am excited to see what happens when we can fully turn &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;each&lt;/code&gt; and friends into
call-less loops!&lt;/p&gt;

&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h2&gt;

&lt;p&gt;Code locality rules. The inliner helps inject more of it and reason across
method calls. ZJIT, a little over one year old, is growing up! :’)&lt;/p&gt;

&lt;p&gt;We’re still tuning the inliner knobs. Some of the code in this post required
tweaking &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--zjit-inline-threshold&lt;/code&gt; to convince the compiler to inline
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#each&lt;/code&gt; into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;method_a&lt;/code&gt; because of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#each&lt;/code&gt;’s size. It will take some
time for the ZJIT developers to figure out reasonable defaults.&lt;/p&gt;

&lt;p&gt;Try out our HIR explorer at &lt;a href=&quot;https://tryzjit.fly.dev/&quot;&gt;tryzjit.fly.dev&lt;/a&gt;. Try
out ZJIT in your application by adding the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--zjit&lt;/code&gt; flag to a Ruby over 4.0.
Thanks for reading and see you next time.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:procs-and-such&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Mostly. I am glossing over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt;, procs, ifuncs, etc. But
the common path is by and away iseq blocks. &lt;a href=&quot;#fnref:procs-and-such&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:not-always-constant&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;It’s not always the case that these methods are called
with a constant block iseq. Sometimes they are called with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;:symbol&lt;/code&gt;
form, for example. Or with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;some_block_variable&lt;/code&gt; form. In that case,
we can use an inline cache to (with a guard) make it constant once more.
But we have not implemented that yet, because it is rarer. &lt;a href=&quot;#fnref:not-always-constant&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:iteration-variable&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;The iteration variable is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v91&lt;/code&gt;! You can see it get used
as an array index when it gets unboxed as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v96&lt;/code&gt; and passed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ArrayAref&lt;/code&gt;.
You can also see it get incremented with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FixnumAdd&lt;/code&gt; and the constant &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;.
In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bb10&lt;/code&gt; it has a different name, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v69&lt;/code&gt;, which gets checked against the
array length. &lt;a href=&quot;#fnref:iteration-variable&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
            <pubDate>Tue, 28 Jul 2026 00:00:00 +0000</pubDate>
            <niceDate>July 28, 2026</niceDate>
            <link>https://bernsteinbear.com/blog/zjit-inliner-invokeblock/?utm_source=rss</link>
            <guid isPermaLink="true">https://bernsteinbear.com/blog/zjit-inliner-invokeblock/</guid>
        </item>
        
        <item>
            <title>Travel notes: PLDI Boulder</title>
            <description>&lt;p&gt;I had another excellent &lt;a href=&quot;https://www.sigplan.org/Conferences/PLDI/&quot;&gt;PLDI&lt;/a&gt; this
past June. It was my fourth&lt;sup id=&quot;fnref:fifth&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:fifth&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. I continued to meet new people and learn
new things!&lt;/p&gt;

&lt;p&gt;Overall: I got to meet a lot of new people, which was exciting. I had some good
chats about research. I asked a question at a talk! I got to show Aaron and
Jacob PLDI and see them enjoy it. I missed hanging out with CF Bolz-Tereick and
Chris Fallin, the usual suspects at conferences I attend. I’m looking forward
to next year.&lt;/p&gt;

&lt;p&gt;This post is more about the conference than the town of Boulder (unlike the
last PLDI post about Seoul) because I didn’t do much Boulder exploring.&lt;/p&gt;

&lt;h2 id=&quot;sunday&quot;&gt;Sunday&lt;/h2&gt;

&lt;p&gt;I got in late on Sunday. Then I had to take a long car ride from Denver airport
to Boulder. I don’t think I had ever flown into Denver with intent to go to
Boulder before so it was a bit of a surprise.&lt;/p&gt;

&lt;p&gt;Jacob offered to have a late dinner with me so we had a tasty meal at Gaia
Masala and Burger. Shout out to Harry, our server.&lt;/p&gt;

&lt;h2 id=&quot;monday&quot;&gt;Monday&lt;/h2&gt;

&lt;p&gt;Monday was a workshop day. I signed up for EGRAPHS and mostly stayed in that
workshop. People kept throwing around the term “Knuth-Bendix”, as they have
been for several years, and only in one of these workshop talks did someone
explain it in a way that made any sense at all. It seems kind of like equality
saturation but for the rewrite rules themselves—no actual expression graphs
involved. I DMed Phil this sketchy explanation during a talk to get his
response and I got to watch him cock his head and think about it in real time.&lt;/p&gt;

&lt;p&gt;At lunch I met Qiantan Hong and we got to talking about Common Lisp and its
object system, CLOS. Seems like a combination of ahead-of-time compilation and
multiple dispatch is really tricky.&lt;/p&gt;

&lt;p&gt;I had dinner with Aaron at Postino and then wandered into a bunch of people
staying at the conference hotel chatting in the lobby. Ben Titzer said “fix my
subtyping bug”, which I interpreted as him saying hi. I ended up just planting
myself at the table as a bunch of interesting people cycled through: Jared
Roesch, Mae Milano, Hila Peleg, Russel Arbore. It was a late evening.&lt;/p&gt;

&lt;h2 id=&quot;tuesday&quot;&gt;Tuesday&lt;/h2&gt;

&lt;p&gt;Back to the workshops! But late because of aforementioned late evening. I saw
Vadym’s talk about Remora. I only understood about 40% of it but it was good to
catch up. I hadn’t seen him since leaving Northeastern.&lt;/p&gt;

&lt;p&gt;Around a break time I joined a little cluster of people talking about e-graphs
and I guessed asked enough basic questions that Pavel convinced Max Willsey to
run a “BYOEG” (build your own e-graph) tutorial. The structure was as follows:
Max would instruct Jacob as to what &lt;em&gt;kind&lt;/em&gt; of thing to build next but not be
prescriptive about exactly how to build it and not look at Jacob’s screen. The
rest of us would sit around a table and try to follow along as best we could. I
hear Pavel has a blog post about this experience coming soon…&lt;/p&gt;

&lt;p&gt;I saw Slava Pestov walking around and introduced myself because we keep liking
one others’ bad jokes on Mastodon. We ended up getting dinner with Aaron and
Jacob that night at Leaf. We learned a lot about monoids, Knuth-Bendix (!),
Factor, and Swift. Slava volunteered to do a similar follow-along “BYOKBC”
(build your own Knuth-Bendix completion) tutorial the next day.&lt;/p&gt;

&lt;h2 id=&quot;wednesday&quot;&gt;Wednesday&lt;/h2&gt;

&lt;p&gt;First day of the conference! I was walking into the hotel in the morning and I
had made it about three feet onto the property when Alexa VanHattum, who was
going the opposite direction, convinced me to instead get coffee elsewhere. We
had a nice catch up and I got to hear about what teaching is like these days.&lt;/p&gt;

&lt;p&gt;Lunch was fun. I got to do another round of “ambush person whose research I
admire” and plopped down with Ben and Christian Wimmer. I’d spent a lot of time
struggling with Christian’s papers on linear scan, then convinced him to chat
about register allocation with us on a video call a couple of months ago. We
continued some of that at lunch but then I (kind of accidentally kind of on
purpose) got Ben started talking about Sea of Nodes and how it is and is not
different between Java and (for example stand-in for dynamic languages)
JavaScript. Apparently he is thinking about a similar thing that he is calling
Sea of Variables.&lt;/p&gt;

&lt;p&gt;We talked about inlining challenges and how to infuse profiles with call
context, which can be a challenge. I feel more inspired to get type-based alias
analysis working in ZJIT.&lt;/p&gt;

&lt;p&gt;I tracked down Christian later in the courtyard and got to hear about what he’s
working on these days. I know very little about ML compilers and ML hardware
and things like that so hearing about the challenges was neat. Yannis
Smaragdakis joined our little standing table chat and we got to learn about
Datalog. Because I had previously written about linear scan register allocation
and about liveness analysis with Datalog, I goaded him into pairing with me on
writing a full linear scan implementation in Datalog.&lt;/p&gt;

&lt;p&gt;This ended up taking the rest of the evening and several beers and then a lot
of the next day! And after the first bit of code I did not manage to contribute
very much at all.&lt;/p&gt;

&lt;h2 id=&quot;thursday&quot;&gt;Thursday&lt;/h2&gt;

&lt;p&gt;I met Hannah Gommerstadt and we got to chatting about bikes and formal methods
(separately). Slava walked by and I got to introduce them. Then Jacob too.&lt;/p&gt;

&lt;p&gt;I continued pairing with Yannis but remained really lost. The only thing I
think I contributed was some familiarity with the core algorithm, which he had
only really seen in passing before. Eventually he got it fully working, but it
needed some deep trickery. More on this soon in its own post.&lt;/p&gt;

&lt;p&gt;I saw a talk about versioned e-graphs and that got me wondering if their
implementation can be used as a persistent e-graph or even just persistent
union-find. Sometimes you want to do backtracking, or have undo-redo in your
compiler.&lt;/p&gt;

&lt;p&gt;Then I went to a talk about streaming byte-pair encoding (BPE). BPE is hard to
do streaming because it definitionally requires looking over the whole input
string. They did some neat trickery to find boundaries in the string that
demarcate regions that don’t interfere with one another and thereby tokenize
on-the-fly. I didn’t understand it fully but I asked my only question of the
conference, which was if this could also be used to implement BPE in parallel.
Seems the answer is “maybe” so I should probably reach out and ask further.&lt;/p&gt;

&lt;p&gt;Slava started showing me and Jacob and Aaron how to implement Knuth-Bendix
completion for strings. I had a lot of tiny little bugs which slowed progress.
Such is life. The banquet and awards ceremony started so we called it a night
and went off to eat dinner. They had good lentils.&lt;/p&gt;

&lt;h2 id=&quot;friday&quot;&gt;Friday&lt;/h2&gt;

&lt;p&gt;I ran into Thalia Archibald and John Regehr and we talked about (really, they talked
about and I tried to learn something) what it might mean to either port Alive2
to another compiler than LLVM, or build “Alive3”, or “Mini-Alive” for some
other IR. John suggested fuzzing the hell out of the thing first, then doing
something more formal later… especially if it’s a dynamic language IR where a
lot of the opcodes end up being “function call that can do anything”.&lt;/p&gt;

&lt;p&gt;I had a nice chat with Steven Holtzen and Zach Tatlock about research and grad
student life. I got some good advice. I meant to talk to Zach about this thing
we keep occasionally chatting about that I call “the big e-graph in the sky”. I
talked a bit about it to Max Willsey and he had some good probing questions
about what would be slow, challenging, or somehow undefined given my problem
statement.&lt;/p&gt;

&lt;p&gt;I continued struggling to implement Knuth-Bendix with significant assistance
from Slava and I think eventually got &lt;em&gt;something&lt;/em&gt; working. I had a really nasty
bug due to string slicing semantics&lt;sup id=&quot;fnref:slicing&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:slicing&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; in Ruby.&lt;/p&gt;

&lt;p&gt;Aaron went off to learn about deep immutability in Python and then got to
chatting with the authors of the paper. We got to compare notes about language
and language implementation challenges. It’s been a long time since I was in
Python-land.&lt;/p&gt;

&lt;p&gt;Aaron and Slava and I got enchiladas for dinner.&lt;/p&gt;

&lt;h2 id=&quot;saturday&quot;&gt;Saturday&lt;/h2&gt;

&lt;p&gt;I had no reading material for the flight so I went downtown, intending to buy
one book, but got too many books. They barely fit into my bag for the flight
home. I started reading Anathem for the second time. It holds up. It’s a damn
good book.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:fifth&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Actually, it might be my fifth. I just remembered that I attended
PLMW in 2020 and also watched a few online talks at wild hours from my
living room. &lt;a href=&quot;#fnref:fifth&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:slicing&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;The semantics in Ruby are probably globally reasonable but did not
fit the thing I was trying to do: if we have two strings &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b&lt;/code&gt;, we
want to find the index at which they start to overlap, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;overlap&lt;/code&gt;. Then we
want to grab the bit of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; that is to the left of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;overlap&lt;/code&gt;. I had
initially written that as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a[..overlap-1]&lt;/code&gt;.&lt;/p&gt;

      &lt;p&gt;However, if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b&lt;/code&gt; overlap at the start of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;overlap&lt;/code&gt; is 0. This
generates the range &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;..-1&lt;/code&gt;, which means we’ll slice until the end of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt;.&lt;/p&gt;

      &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;irb(main):001&amp;gt; &quot;abc&quot;[..-1]
=&amp;gt; &quot;abc&quot;
irb(main):002&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;      &lt;/div&gt;

      &lt;p&gt;Not what we want. Instead, the fix in &lt;a href=&quot;https://github.com/tekknolagi/knuth-bendix/commit/e46b0069767de44622dc116b0b7b51a4392c400d&quot;&gt;the
commit&lt;/a&gt;
shows how I had to add a special slice function called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string_upto&lt;/code&gt; that
handles the 0 case. &lt;a href=&quot;#fnref:slicing&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
            <pubDate>Sun, 05 Jul 2026 00:00:00 +0000</pubDate>
            <niceDate>July 5, 2026</niceDate>
            <link>https://bernsteinbear.com/blog/travel-notes-pldi-boulder/?utm_source=rss</link>
            <guid isPermaLink="true">https://bernsteinbear.com/blog/travel-notes-pldi-boulder/</guid>
        </item>
        
        <item>
            <title>A survey of inlining heuristics</title>
            <description>&lt;p&gt;Compilers, especially method just-in-time compilers, operate on one function at
a time. It is a natural code unit size, especially for a dynamic language JIT:
at a given point in time, what more information can you gather about other
parts of a running, changing system?&lt;/p&gt;

&lt;p&gt;I don’t have any data to back this up—maybe I should go gather some—but on
average, methods are small. Especially in languages such as Ruby that use
method dispatch for everything, even instance variable (attribute, field, …)
lookups, they are &lt;em&gt;small&lt;/em&gt;. And everywhere.&lt;/p&gt;

&lt;p&gt;This makes the compiler sad. If we are to continue to anthropomorphize them,
compilers like having more context so they can optimize better. Consider the
following silly-looking example that is actually representative of a surprising
amount of real-world code:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Point&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:y&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;distance_from_origin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Right now, in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;distance_from_origin&lt;/code&gt; method, I count 8 different method calls:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Point.new&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Point#initialize&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Point.new&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Point#initialize&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Point#distance&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Float#**&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Float#**&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Math.sqrt&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Technically more, but the ivar lookups (including &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;attr_reader&lt;/code&gt;!), addition,
and subtraction are generally specialized and don’t push a frame, even in the
interpreter.)&lt;/p&gt;

&lt;p&gt;Furthermore, there are at least two heap allocations: one for each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Point&lt;/code&gt;
instance.&lt;/p&gt;

&lt;p&gt;Last, there is a bunch of memory traffic to and from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Point&lt;/code&gt; instances.&lt;/p&gt;

&lt;p&gt;This all is a huge bummer! What should be a simple math operation is now
overwhelmed with a bunch of other stuff. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Point&lt;/code&gt; is certainly not a zero-cost
abstraction.&lt;/p&gt;

&lt;p&gt;Even if we had a bunch of other optimizations such as load-store elimination or
escape analysis, they would not be able to do much: pretty much everything
escapes and is effectful. That is, unless we &lt;em&gt;inline&lt;/em&gt;. Inlining is the lever
that enables a bunch of other optimization passes to kick in.&lt;/p&gt;

&lt;h2 id=&quot;inlining-the-easy-part&quot;&gt;Inlining: the “easy” part&lt;/h2&gt;

&lt;p&gt;I wrote about the design and implementation of Cinder’s inliner (&lt;a href=&quot;https://engineering.fb.com/2022/05/02/open-source/cinder-jits-instagram/&quot;&gt;FB
link&lt;/a&gt;,
&lt;a href=&quot;/blog/cinder-jit-inliner/&quot;&gt;personal blog link&lt;/a&gt;) a couple of years ago. I wrote
about arguably the simplest part, which is copying the callee body into the
caller. It took me at least a week to get working. Probably closer to months if
you consider all the plumbing through the rest of the JIT. In February during a
small hackathon, I watched my colleague &lt;a href=&quot;https://github.com/k0kubun&quot;&gt;k0kubun&lt;/a&gt;
prototype that bit of the inliner inside ZJIT in about 30 minutes.&lt;/p&gt;

&lt;p&gt;There is more to do when pretty much every part of the VM is observable from
the guest language: both Python and Ruby allow inspecting the state of the
locals, the call stack, etc from user code. Sampling profilers also expect some
amount of breadcrumbs to work with to inspect the stack. So there’s some more
machinery still required to pretend like the callee function was not inlined. I
talk about this a little bit in the Cinder blog post.&lt;/p&gt;

&lt;p&gt;Even so, all of that can probably be designed and wired together in a couple
of months. Then you will find yourself tuning the inliner for the next 10
years. This is much harder.&lt;/p&gt;

&lt;h2 id=&quot;when-the-harder-part&quot;&gt;When: the harder part&lt;/h2&gt;

&lt;p&gt;The thing that makes inlining difficult, especially in a method JIT, is that
you are trying to make an entire (dynamic!) system faster but you are only
looking through a microscope and only capable of local reasoning&lt;sup id=&quot;fnref:aot-split&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:aot-split&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.
Whereas other optimizations such as strength reduction, inline caches, and
value numbering are an un-alloyed good for the generated code, inlining can
have &lt;em&gt;negative effects&lt;/em&gt;. It is also perhaps the first optimization people add
that has non-local impact.&lt;/p&gt;

&lt;p&gt;If you inline wrong, your code size might blow up. This might thrash your CPU’s
caches. Bummer, but happens to the best of us.&lt;/p&gt;

&lt;p&gt;But also, if you inline wrong, you might get in the way of other helpful
optimizations: if you hit some size limit after inlining method A, you might
never get to inline B, which is the key to unlocking the performance of the
method you are trying to optimize.&lt;/p&gt;

&lt;p&gt;Last, inlining might hurt compile time. In situations where latency is
paramount (think: interactive client JavaScript), adding tons more code into
the fray might add noticeable hiccups, even if the long-term throughput
improves. As always, in-band compilation is a trade-off because any time you
spend compiling, you are &lt;em&gt;not executing code&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You have to write your compiler to reason about all of this stuff. So you have
heuristics. For example, here is Michael Pollan’s inliner heuristic:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Inline methods. Mostly small. Not too many.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I did a survey of a bunch of compilers, mostly JIT compilers, to see what their
inlining heuristics look like. I also read (skimmed) some papers to see what
those folks had to say. I wonder if they agree.&lt;/p&gt;

&lt;p&gt;This post was a long time coming. I started working on it about five years ago
but then when I quit working at Facebook I accidentally left behind all of the
inliner research I did for Cinder’s inliner. So then I kind of just thought
about it aimlessly for a while before redoing it this year. Anyway, here’s
wonderwall.&lt;/p&gt;

&lt;h2 id=&quot;the-heuristics&quot;&gt;The heuristics&lt;/h2&gt;

&lt;p&gt;Spoiler alert: all in all, people tend to look at:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Profiles of call target&lt;/li&gt;
  &lt;li&gt;Cumulative caller size (increasing as callees get inlined)&lt;/li&gt;
  &lt;li&gt;Callee size&lt;/li&gt;
  &lt;li&gt;Inline depth&lt;/li&gt;
  &lt;li&gt;Number of inlined calls at a certain depth&lt;/li&gt;
  &lt;li&gt;If recursion is present&lt;/li&gt;
  &lt;li&gt;Callee/caller call count ratio (if callee only called less than K% of calls
to caller, don’t inline callee)&lt;/li&gt;
  &lt;li&gt;Callee stack usage&lt;/li&gt;
  &lt;li&gt;Polymorphism in callee&lt;/li&gt;
  &lt;li&gt;What mode the compiler is in (baseline vs more aggressive)&lt;/li&gt;
  &lt;li&gt;If the callee looks like it always raises/throws&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And also have different interesting ways to pipe in profile information.&lt;/p&gt;

&lt;p&gt;Last, some newer papers do some wild stuff:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Train neural networks to make inlining decisions&lt;/li&gt;
  &lt;li&gt;Let inlining drive the entire optimization pipeline, treating it as a search
heuristic over a BFS walk of the call graph&lt;/li&gt;
  &lt;li&gt;Use AOT-gathered information to aid in JIT heuristics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another thing to consider in inlining is how you gather and interpret profiles.&lt;/p&gt;

&lt;h2 id=&quot;call-context-and-profiles-the-other-harder-part&quot;&gt;Call context and profiles: the other harder part&lt;/h2&gt;

&lt;p&gt;When you compile a function, you tend to specialize it based on the input it
has historically been given. For a monomorphic input, maybe you guard that the
type is still the same and otherwise jump into the interpreter. For a
polymorphic input, maybe you check the top K (~4) common cases and otherwise
jump into the interpreter. Fine.&lt;/p&gt;

&lt;p&gt;But sometimes you can be compiling a polymorphic method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bar&lt;/code&gt; that is actually
monomorphic in its caller &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt;. That is, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; might only ever pass one kind
of input to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bar&lt;/code&gt;, but other callers pass all kinds of stuff. Here is a bit of
a silly example to show what I mean:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HashWithIndifferentAccess&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Allow reading from the Hash with either a String or a Symbol&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_sym&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# some method...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;some_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;HashWithIndifferentAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;some_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# some other method...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;another_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;HashWithIndifferentAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;another_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:xyz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Just kidding, not so silly at all. It’s a super common pattern &lt;a href=&quot;https://github.com/rails/rails/blob/6c75e6d5663afa4278ee593c2d6c20c1ee396e32/activesupport/lib/active_support/hash_with_indifferent_access.rb#L55&quot;&gt;in
Rails&lt;/a&gt;. It makes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key&lt;/code&gt; polymorphic in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HashWithIndifferentAccess#[]&lt;/code&gt; even
though for many of its callers, it may well be monomorphic (or even a
constant).&lt;/p&gt;

&lt;p&gt;In order to plumb this information through to the compiler, you have to figure
out this call context relationship. There are a couple of common ways to do it.&lt;/p&gt;

&lt;h3 id=&quot;splitting&quot;&gt;Splitting&lt;/h3&gt;

&lt;p&gt;YJIT, for example, though it does not inline, splits methods based on the types
of the arguments going in. This means that it clones the compiled code,
generating a new version for each context. This does not give &lt;em&gt;call&lt;/em&gt; context
(“A calls B”) but gives type context (“B is called with integers, B’ is called
with strings”).&lt;/p&gt;

&lt;p&gt;A compiler could do type-based splitting in the interpreter or a baseline tier.&lt;/p&gt;

&lt;h3 id=&quot;profile-splitting&quot;&gt;Profile splitting&lt;/h3&gt;

&lt;p&gt;If you don’t fancy duplicating the code, you can instead duplicate the
profiles. You could either do this using type context (as above) or using call
context. SpiderMonkey, for example, does “trial inlining” that allows callers
to pass down a bit of memory for potential inline candidate callees to record
their inline caches. Instead of each function holding its own ICScript, the
caller allocates a unique ICScript for that potential-inline call-site. This
gives each callee function (at least?) one level of call context.&lt;/p&gt;

&lt;p&gt;Later, when inlining the callee into the caller, we don’t have other callers’
type information polluting the IR builder (or whatever reads the profiles).&lt;/p&gt;

&lt;h3 id=&quot;bytecode-inlining&quot;&gt;Bytecode inlining&lt;/h3&gt;

&lt;p&gt;&lt;del&gt;JavaScriptCore handles this by inlining bytecode into other bytecode. This is a
gnarly transformation but gives the interpreter, even (!) access to call
context. On tier-up to the compiler, all the inlining decisions have been made
already.&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;EDIT: Upon reflection, I think think this is wrong and based on a misremembered
conversation.&lt;/p&gt;

&lt;h3 id=&quot;early-tier-with-counters&quot;&gt;Early tier with counters&lt;/h3&gt;

&lt;p&gt;HotSpot handles this with multiple tiers. The interpreter tiers up to the
client compiler, C1. C1 profiles branch and call targets in compiled code. C1
may eventually recompile based on this new information. C1 may eventually tier
up to C2, which copies C1 inlining decisions. This way, we get call context in
profiles via inlining.&lt;/p&gt;

&lt;h3 id=&quot;inline-and-analyze-and-hope&quot;&gt;Inline and analyze and hope&lt;/h3&gt;

&lt;p&gt;One last thing you could do is just trust your type inference and branch
folding in the optimizer. You could inline and do polymorphic specialization in
the callee when building the IR, then hope that your branch pruning
monomorphizes the inlined callee. It’s a little wasteful because the
polymorphic code is built “for nothing”, but it might work fine?&lt;/p&gt;

&lt;!--
### Inline and merge profiles
--&gt;

&lt;p&gt;Okay, onto the collected notes and half-baked commentary. Here’s a survey of a
bunch of JIT compilers and how they reason about inlining heuristics.&lt;/p&gt;

&lt;h3 id=&quot;thanks&quot;&gt;Thanks&lt;/h3&gt;

&lt;p&gt;But before we get into that, thanks to Iain Ireland, CF Bolz-Tereick, and Ian
Rogers for feedback on this blog post!&lt;/p&gt;

&lt;h2 id=&quot;the-survey-bits-and-bobbles&quot;&gt;The survey: bits and bobbles&lt;/h2&gt;

&lt;p&gt;What follows is mostly a “bits and bobbles” section a la &lt;a href=&quot;https://www.philipzucker.com/&quot;&gt;Phil
Zucker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We’ll start with &lt;a href=&quot;https://github.com/facebookincubator/cinderx&quot;&gt;Cinder&lt;/a&gt;, because when I wrote Cinder’s inliner I
added only the simplest heuristics, mostly “don’t inline” signals. Over time,
after I left, people tuned more.&lt;/p&gt;

&lt;h3 id=&quot;cinder-new&quot;&gt;Cinder: new&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Update:&lt;/em&gt; Shortly after writing this post, and completely unrelated to my
writing this post, Alex Malyshev reworked the inlining heuristincs in Cinder. I
left the old section below as &lt;strong&gt;Cinder: old&lt;/strong&gt; and this section covers the newer
design as of July 2026.&lt;/p&gt;

&lt;p&gt;The two PRs: &lt;a href=&quot;https://github.com/facebookincubator/cinderx/commit/069ed87f0771a2dac751afdae8993685792a3e5a&quot;&gt;the
first&lt;/a&gt;
and &lt;a href=&quot;https://github.com/facebookincubator/cinderx/commit/5c918e9d58b6350f6da2095324099a3293220079&quot;&gt;the
second&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first PR ranks potential callees to inline by call count and avoids
inlining callees that have 5% or less of the call count of the caller
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inliner_cold_call_threshold&lt;/code&gt;). It also orders the callees by perceived
importance using this metric: hotter callees get inlined first.&lt;/p&gt;

&lt;p&gt;The second PR starts inlining transitively. That is, if we inline function B
into function A, the calls in function B themselves become considered inlining
candidates. Furthermore, it begins ranking call-sites by cost (measured by
bytecode count) and using a worklist to drive the inlining decision making. It
prohibits recursive inlines, preventing an inline if the code is already found
in the compile-time inline stack. Last, it adds a depth limit for the recursion
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inliner_depth_limit&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The old explicit sorting goes away; the new implementation uses
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::priority_queue&lt;/code&gt; to order by cost and tie-break by the order in which a
call was discovered.&lt;/p&gt;

&lt;h3 id=&quot;cinder-old&quot;&gt;Cinder: old&lt;/h3&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/facebookincubator/cinderx/blob/88189ebf4bfd196ac7578c5076efa39bfa11f211/cinderx/Jit/hir/inliner.cpp#L341&quot;&gt;inliner&lt;/a&gt; starts from the caller CFG, walking it to find
suitable inlining candidates. Inlining candidates are only for call targets
that are known—in Cinder’s case, only for monomorphic call targets—and pass
some checks. The callee is only known by it’s function object, which includes
its bytecode. There is no IR available for the callee until we decide to inline.&lt;/p&gt;

&lt;p&gt;Most of the “can’t handle this” checks are related to argument handling. Python
has a pretty complex calling convention, so if the caller/callee have not
agreed on how the arguments should be passed through, the inliner doesn’t care
to try and figure it out on its own. That is the responsibility of &lt;a href=&quot;https://github.com/facebookincubator/cinderx/blob/88189ebf4bfd196ac7578c5076efa39bfa11f211/cinderx/Jit/hir/simplify.cpp#L1765&quot;&gt;other parts
of the compiler&lt;/a&gt;. Things in this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canInline&lt;/code&gt;
function could be considered “TODO”.&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;canInline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;caller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractCall&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call_instr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;BorrowedRef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PyFunctionObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call_instr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fail&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InlineFailureType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;failure_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dlogAndCollectFailureStats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;caller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call_instr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;failure_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func_kwdefaults&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InlineFailureType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kHasKwdefaults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;BorrowedRef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PyCodeObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;JIT_CHECK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PyCode_Check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Expected PyCodeObject&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;co_kwonlyargcount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InlineFailureType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kHasKwOnlyArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Failures are logged so they can be analyzed. If the Cinder team determines that
there is some very frequent case they should handle, they will find out from
the logs.&lt;/p&gt;

&lt;p&gt;The inliner collects all candidate call instructions in one pass over the CFG.
It loads the configurable “cost limit” from the options struct. Then it does
one pass over the inlining candidates vector, inlining until it (maybe) hits
the cost limit.&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cost_limit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inliner_cost_limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;codeCost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irfunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Inline as many calls as possible, starting from the top of the function and&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// working down.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to_inline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;BorrowedRef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PyCodeObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;codeCost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cost_limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LOG_INLINER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;Inliner reached cost limit of {} when trying to inline {} into {}, &quot;&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;inlining stopping early&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;new_cost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;funcFullname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;irfunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fullname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_cost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;inlineFunctionCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irfunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// We need to reflow types after every inline to propagate new type&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// information from the callee.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;reflowTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irfunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It does some graph maintenance work after inlining these calls, but that’s it.&lt;/p&gt;

&lt;p&gt;This approach gets a surprising amount of utility for being so simple: it
inlines constants (quite a few methods look like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;def foo(): return 5&lt;/code&gt;), small
methods, and (at least, as far as I can remember) shrinks the compiled code
size. All for very little compile time overhead.&lt;/p&gt;

&lt;p&gt;There’s one other “standalone” Python JIT out there, PyPy. So we should look at
that too.&lt;/p&gt;

&lt;h3 id=&quot;pypy&quot;&gt;PyPy&lt;/h3&gt;

&lt;p&gt;There are two inliners in PyPy. One is inside the RPython to C translation
pipeline, which acts more like an ahead-of-time compiler&lt;sup id=&quot;fnref:rpython-inliner&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:rpython-inliner&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.
Then there is the tracing JIT bit, which has its own optimizer and heuristics.
We’re going to look at the latter.&lt;/p&gt;

&lt;p&gt;I talked to &lt;a href=&quot;https://cfbolz.de/&quot;&gt;CF Bolz-Tereick&lt;/a&gt; about the inliner and their
comment was that PyPy’s inlining heuristic is “yes”. There are a couple of
exceptions, such as not inlining recursive functions or functions with loops.
But the basic idea of tracing includes tracing through call instructions, which
naturally means that you are “inlining”.&lt;/p&gt;

&lt;p&gt;PyPy also does this neat thing where they treat frame pushes like normal
allocation. Frame pushes, frame reads, and frame writes get written to the
trace like normal object memory traffic and can get optimized away like other
field reads and writes. This means that they can “just” use DCE to eliminate
frame pushes and pops, whereas Cinder has some complicated mechanism to do it
(which is my fault).&lt;/p&gt;

&lt;p&gt;TODO get more details here&lt;/p&gt;

&lt;h3 id=&quot;v8&quot;&gt;V8&lt;/h3&gt;

&lt;p&gt;V8 is a JS engine and it has over the years had many execution approaches.
We’ll look at three of them since they all have or had their place in the
history:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Hydrogen was the first real SSA IR and it looks very familiar to me, having
worked on Cinder and now ZJIT. It is now defunct.&lt;/li&gt;
  &lt;li&gt;Turbofan was the replacement, going full Sea of Nodes. In the grand scheme of
things it is a pretty fast compiler, but it does not hold back from doing some
expensive rewrites. This was recently rewritten from Sea of Nodes to a mode
traditional CFG and nicknamed Turboshaft.&lt;/li&gt;
  &lt;li&gt;Maglev is meant to coexist alongside Turbofan, preferring to speculate a little
more eagerly and do fewer incremental rewrites in the name of compile
time.&lt;sup id=&quot;fnref:turbolev&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:turbolev&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They also each inline at different times in the pipeline, which made for a fun
time trying to understand the different codebases.&lt;/p&gt;

&lt;h4 id=&quot;v8-hydrogen&quot;&gt;V8 Hydrogen&lt;/h4&gt;

&lt;p&gt;Inlining happens during Hydrogen graph building&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/tekknolagi/v8/blob/a969ab67f8e1e7475d9b26468225c3a772890c64/src/crankshaft/hydrogen.cc#L9236&quot;&gt;https://github.com/tekknolagi/v8/blob/a969ab67f8e1e7475d9b26468225c3a772890c64/src/crankshaft/hydrogen.cc#L9236&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don’t store function bytecode of all functions; need to re-parse callee &lt;em&gt;text
source&lt;/em&gt; to inline&lt;/p&gt;

&lt;p&gt;Heuristics &lt;a href=&quot;https://github.com/tekknolagi/v8/blob/a969ab67f8e1e7475d9b26468225c3a772890c64/src/crankshaft/hydrogen.cc#L7807&quot;&gt;https://github.com/tekknolagi/v8/blob/a969ab67f8e1e7475d9b26468225c3a772890c64/src/crankshaft/hydrogen.cc#L7807&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;something about native context&lt;/li&gt;
  &lt;li&gt;check callee AST size against configurable limit&lt;/li&gt;
  &lt;li&gt;check inlining depth against configurable limit&lt;/li&gt;
  &lt;li&gt;don’t inline recursive functions&lt;/li&gt;
  &lt;li&gt;check current cumulative method size (as tracked by AST node count) against
configurable limit&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;v8-turbofan&quot;&gt;V8 TurboFan&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.google.com/document/d/1VoYBhpDhJC4VlqMXCKvae-8IGuheBGxy32EOgC2LnT8/edit&quot;&gt;https://docs.google.com/document/d/1VoYBhpDhJC4VlqMXCKvae-8IGuheBGxy32EOgC2LnT8/edit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/v8/v8/blob/036842f4841326130a40adfcff38f85a9b4cd30a/src/compiler/js-inlining-heuristic.h#L14&quot;&gt;https://github.com/v8/v8/blob/036842f4841326130a40adfcff38f85a9b4cd30a/src/compiler/js-inlining-heuristic.h#L14&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Find candidates &lt;a href=&quot;https://github.com/v8/v8/blob/036842f4841326130a40adfcff38f85a9b4cd30a/src/compiler/js-inlining-heuristic.cc#L134&quot;&gt;https://github.com/v8/v8/blob/036842f4841326130a40adfcff38f85a9b4cd30a/src/compiler/js-inlining-heuristic.cc#L134&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Can inline &lt;a href=&quot;https://github.com/v8/v8/blob/036842f4841326130a40adfcff38f85a9b4cd30a/src/compiler/js-inlining-heuristic.cc#L75&quot;&gt;https://github.com/v8/v8/blob/036842f4841326130a40adfcff38f85a9b4cd30a/src/compiler/js-inlining-heuristic.cc#L75&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Force inline small functions &lt;a href=&quot;https://github.com/v8/v8/blob/036842f4841326130a40adfcff38f85a9b4cd30a/src/compiler/js-inlining-heuristic.cc#L309&quot;&gt;https://github.com/v8/v8/blob/036842f4841326130a40adfcff38f85a9b4cd30a/src/compiler/js-inlining-heuristic.cc#L309&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Loop over sorted (by comparator) list &lt;a href=&quot;https://github.com/v8/v8/blob/036842f4841326130a40adfcff38f85a9b4cd30a/src/compiler/js-inlining-heuristic.cc#L847&quot;&gt;https://github.com/v8/v8/blob/036842f4841326130a40adfcff38f85a9b4cd30a/src/compiler/js-inlining-heuristic.cc#L847&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;v8-maglev&quot;&gt;V8 Maglev&lt;/h4&gt;

&lt;p&gt;When optimizing, add call instructions to the inline candidates list: &lt;a href=&quot;https://github.com/v8/v8/blob/1a391f98cc7a9196369f2d6cab7df35ffbe92c08/src/maglev/maglev-graph-optimizer.cc#L1271&quot;&gt;https://github.com/v8/v8/blob/1a391f98cc7a9196369f2d6cab7df35ffbe92c08/src/maglev/maglev-graph-optimizer.cc#L1271&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;ProcessResult&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MaglevGraphOptimizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VisitCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                              &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProcessingState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytecode_length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shared&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBytecodeArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;broker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;score&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call_frequency&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytecode_length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loop_depth_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_small_function&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;bytecode_length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;reducer_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;graph&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compilation_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_eager_inlined_bytecode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;MaglevCallSiteInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call_site&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reducer_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MaglevCallSiteInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;MaglevCallerDetails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;is_small_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call_frequency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytecode_length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;reducer_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PushInlineCandidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/v8/v8/blob/036842f4841326130a40adfcff38f85a9b4cd30a/src/maglev/maglev-inlining.h#L36&quot;&gt;https://github.com/v8/v8/blob/036842f4841326130a40adfcff38f85a9b4cd30a/src/maglev/maglev-inlining.h#L36&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unlike for example Cinder, Maglev looks like it does not have a lot of
restrictions about what can get inlined into what, so its “can inline” signal
is about budget. Actually two budgets: small budget and normal budget.&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MaglevInliner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CanInlineCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// We stop inlining entirely if the small budget is exhausted.&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// Inlining decisions after that become bad if we stop inlining small&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// functions, but keep inlining large ones.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;graph_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inlineable_calls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;graph_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_inlined_bytecode_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;max_inlined_bytecode_size_cumulative&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;graph_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_inlined_bytecode_size_small&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;max_inlined_bytecode_size_small_total&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then its inlining loop is a greedy walk of the to-inline queue checking
candidate sizes.&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MaglevInliner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InlineCallSites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;DCHECK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CanInlineCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;graph_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inlineable_calls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// pop from inlineable_calls&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;MaglevCallSiteInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call_site&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ChooseNextCallSite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_small_with_heapnum_input_outputs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;IsSmallWithHeapNumberInputsOutputs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;graph_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_inlined_bytecode_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_inlined_bytecode_size_cumulative&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// We ran out of budget. Checking if this is a small-ish function that we&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// can still inline.&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;graph_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_inlined_bytecode_size_small&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;max_inlined_bytecode_size_small_total&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;graph_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compilation_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_could_not_inline_all_candidates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_small_with_heapnum_input_outputs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;graph_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compilation_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_could_not_inline_all_candidates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Not that we don&apos;t break just rather just continue: next candidates&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// might be inlineable.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;InliningResult&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;BuildInlineFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_small_with_heapnum_input_outputs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It runs this loop (which drains the queue) interleaved with the optimizer
(which populates the queue).&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MaglevInliner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;graph_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inlineable_calls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CanInlineCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InlineCallSites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RunOptimizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Confusingly, though, the optimizer also calls another function called
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CanInlineCall&lt;/code&gt; which checks if it legally can inline:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;skip recursion&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/v8/v8/blob/1a391f98cc7a9196369f2d6cab7df35ffbe92c08/src/objects/shared-function-info-inl.h#L421&quot;&gt;https://github.com/v8/v8/blob/1a391f98cc7a9196369f2d6cab7df35ffbe92c08/src/objects/shared-function-info-inl.h#L421&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;not called enough (min call frequency)&lt;/li&gt;
  &lt;li&gt;bytecode too big&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bool MaglevGraphBuilder::ShouldEagerInlineCall(&lt;/code&gt; &lt;del&gt;appears unused? / dead
declaration?&lt;/del&gt; maybe src/maglev/maglev-graph-builder.cc is just not working on
github search&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MaybeReduceResult MaglevGraphBuilder::TryBuildCallKnownJSFunction(&lt;/code&gt; &lt;del&gt;also
unused / dead declaration&lt;/del&gt; same&lt;/p&gt;

&lt;h3 id=&quot;javascriptcore&quot;&gt;JavaScriptCore&lt;/h3&gt;

&lt;p&gt;JavaScriptCore is funky! Unlike these other compilers that do inlining in their
neat little SSA IRs, JSC inlines &lt;em&gt;at the bytecode level&lt;/em&gt;&lt;sup id=&quot;fnref:fil&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:fil&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. This is their way of
making sure that they get at least one level of call context into their
interpreter inline caches, which will eventually give better information to the
compiler.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Bytecode inlining
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/WebKit/WebKit/blob/709c3895afd71e0836f8c8be7393e44d41fab7e1/Source/JavaScriptCore/bytecode/CodeBlock.cpp#L2453&quot;&gt;https://github.com/WebKit/WebKit/blob/709c3895afd71e0836f8c8be7393e44d41fab7e1/Source/JavaScriptCore/bytecode/CodeBlock.cpp#L2453&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;DFG
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/WebKit/WebKit/blob/709c3895afd71e0836f8c8be7393e44d41fab7e1/Source/JavaScriptCore/dfg/DFGCapabilities.cpp#L76&quot;&gt;https://github.com/WebKit/WebKit/blob/709c3895afd71e0836f8c8be7393e44d41fab7e1/Source/JavaScriptCore/dfg/DFGCapabilities.cpp#L76&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/WebKit/WebKit/blob/917854a9c245b87b333e23ed4b195505d574a333/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp#L1703&quot;&gt;https://github.com/WebKit/WebKit/blob/917854a9c245b87b333e23ed4b195505d574a333/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp#L1703&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/WebKit/WebKit/blob/917854a9c245b87b333e23ed4b195505d574a333/Source/JavaScriptCore/bytecode/CallLinkStatus.cpp#L294&quot;&gt;https://github.com/WebKit/WebKit/blob/917854a9c245b87b333e23ed4b195505d574a333/Source/JavaScriptCore/bytecode/CallLinkStatus.cpp#L294&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/WebKit/WebKit/blob/d919344236c47b610930636d3310f00380624d43/Source/JavaScriptCore/bytecode/InlineCallFrame.h&quot;&gt;https://github.com/WebKit/WebKit/blob/d919344236c47b610930636d3310f00380624d43/Source/JavaScriptCore/bytecode/InlineCallFrame.h&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;JSC only inlines based on bytecode profile information, and only inlines
bytecode??&lt;/p&gt;

&lt;p&gt;TODO find better sources for bytecode inlining&lt;/p&gt;

&lt;!--
Compile plan
https://github.com/WebKit/WebKit/blob/709c3895afd71e0836f8c8be7393e44d41fab7e1/Source/JavaScriptCore/dfg/DFGPlan.cpp#L186
--&gt;

&lt;h3 id=&quot;spidermonkey&quot;&gt;SpiderMonkey&lt;/h3&gt;

&lt;p&gt;SpiderMonkey has another way of getting that call context without doing bytecode
inlining: they add call context to their inline caches. Methods can pass down
an &lt;em&gt;ICScript&lt;/em&gt; to their callees where the callee writes its inline cache
information. Then, when compiling, the callee is more likely to be
monomorphized.&lt;/p&gt;

&lt;p&gt;Wasm&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/mozilla-firefox/firefox/blob/438a3ce10eb77fb50d968463b7741117aec5bb4a/js/src/wasm/WasmHeuristics.h#L213&quot;&gt;https://github.com/mozilla-firefox/firefox/blob/438a3ce10eb77fb50d968463b7741117aec5bb4a/js/src/wasm/WasmHeuristics.h#L213&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SpiderMonkey ICScript&lt;/p&gt;

&lt;h3 id=&quot;wasmtime-and-cranelift&quot;&gt;Wasmtime and Cranelift&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://fitzgen.com/2025/11/19/inliner.html&quot;&gt;https://fitzgen.com/2025/11/19/inliner.html&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;hotspot&quot;&gt;HotSpot&lt;/h3&gt;

&lt;p&gt;Plan: run in interpreter; tier up to C1; profile call targets; inline in C1;
profile branch counts; tier up to C2, which copies C1 inlining decisions in
bytecode parser&lt;/p&gt;

&lt;p&gt;HotSpot C2&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/openjdk/jdk/blob/a05d5d2514c835f2bfeaf7a8c7df0ac241f0177f/src/hotspot/share/opto/bytecodeInfo.cpp#L116&quot;&gt;https://github.com/openjdk/jdk/blob/a05d5d2514c835f2bfeaf7a8c7df0ac241f0177f/src/hotspot/share/opto/bytecodeInfo.cpp#L116&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/openjdk/jdk/blob/497dca2549a9829530670576115bf4b8fab386b3/src/hotspot/share/opto/bytecodeInfo.cpp#L197&quot;&gt;https://github.com/openjdk/jdk/blob/497dca2549a9829530670576115bf4b8fab386b3/src/hotspot/share/opto/bytecodeInfo.cpp#L197&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/openjdk/jdk/blob/497dca2549a9829530670576115bf4b8fab386b3/src/hotspot/share/opto/parse.hpp#L42&quot;&gt;https://github.com/openjdk/jdk/blob/497dca2549a9829530670576115bf4b8fab386b3/src/hotspot/share/opto/parse.hpp#L42&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/openjdk/jdk/blob/497dca2549a9829530670576115bf4b8fab386b3/src/hotspot/share/opto/doCall.cpp#L185&quot;&gt;https://github.com/openjdk/jdk/blob/497dca2549a9829530670576115bf4b8fab386b3/src/hotspot/share/opto/doCall.cpp#L185&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not too small&lt;/p&gt;

&lt;p&gt;Walk up the call stack to figure out what to compile&lt;/p&gt;

&lt;p&gt;Handling the right thing to inline: def foo(a) = a.each {|x| x }
want to compile &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt;, inline each, inline block, not compile block separately
(probably)&lt;/p&gt;

&lt;p&gt;HotSpot C1&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://bernsteinbear.com/assets/img/design-hotspot-client-compiler.pdf&quot;&gt;https://bernsteinbear.com/assets/img/design-hotspot-client-compiler.pdf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/openjdk/jdk/blob/d854a04231a437a6af36ae65780961f40f336343/src/hotspot/share/c1/c1_GraphBuilder.cpp#L755&quot;&gt;https://github.com/openjdk/jdk/blob/d854a04231a437a6af36ae65780961f40f336343/src/hotspot/share/c1/c1_GraphBuilder.cpp#L755&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/openjdk/jdk/blob/d854a04231a437a6af36ae65780961f40f336343/src/hotspot/share/c1/c1_GraphBuilder.cpp#L3854&quot;&gt;https://github.com/openjdk/jdk/blob/d854a04231a437a6af36ae65780961f40f336343/src/hotspot/share/c1/c1_GraphBuilder.cpp#L3854&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;skip callees with exception handlers (unless explicitly allowed with a CLI flag)&lt;/li&gt;
  &lt;li&gt;skip synchronized callees (unless explicitly allowed with a CLI flag)&lt;/li&gt;
  &lt;li&gt;skip classes with unlinked callees&lt;/li&gt;
  &lt;li&gt;skip uninitialized classes&lt;/li&gt;
  &lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;heuristics:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;max inline level (default 9)&lt;/li&gt;
  &lt;li&gt;max recursive inline level (default 1)&lt;/li&gt;
  &lt;li&gt;callee bytecode size (max for top level is 35 bytecodes, but falls off by 10% per inline level)&lt;/li&gt;
  &lt;li&gt;callee stack usage (max of 10 slots)
    &lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;      &lt;span class=&quot;c1&quot;&gt;// Additional condition to limit stack usage for non-recursive calls.&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;callee_recursive_level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;callee&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_stack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callee&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_locals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callee&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size_of_parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;C1InlineStackLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;INLINE_BAILOUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;callee uses too much stack&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;max total method size (default 8000 bytecodes)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;truffleruby&quot;&gt;TruffleRuby&lt;/h3&gt;

&lt;p&gt;TruffleRuby uses weighted compile queue&lt;/p&gt;

&lt;p&gt;Graal
&lt;a href=&quot;https://ieeexplore.ieee.org/document/8661171&quot;&gt;https://ieeexplore.ieee.org/document/8661171&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;net&quot;&gt;.NET&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/dotnet/runtime/blob/2d638dc1179164a08d9387cbe6354fe2b7e4d823/docs/design/coreclr/jit/inlining-plans.md&quot;&gt;https://github.com/dotnet/runtime/blob/2d638dc1179164a08d9387cbe6354fe2b7e4d823/docs/design/coreclr/jit/inlining-plans.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/dotnet/runtime/blob/0b3f3ab1ecf4de06459e5f0e2b7cb3baf70ef981/src/coreclr/jit/inline.def#L94&quot;&gt;https://github.com/dotnet/runtime/blob/0b3f3ab1ecf4de06459e5f0e2b7cb3baf70ef981/src/coreclr/jit/inline.def#L94&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/dotnet/runtime/blob/0b3f3ab1ecf4de06459e5f0e2b7cb3baf70ef981/src/coreclr/jit/inlinepolicy.cpp&quot;&gt;https://github.com/dotnet/runtime/blob/0b3f3ab1ecf4de06459e5f0e2b7cb3baf70ef981/src/coreclr/jit/inlinepolicy.cpp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/dotnet/runtime/blob/0b3f3ab1ecf4de06459e5f0e2b7cb3baf70ef981/docs/design/coreclr/jit/inline-size-estimates.md?plain=1#L5&quot;&gt;https://github.com/dotnet/runtime/blob/0b3f3ab1ecf4de06459e5f0e2b7cb3baf70ef981/docs/design/coreclr/jit/inline-size-estimates.md?plain=1#L5&lt;/a&gt;
&lt;a href=&quot;https://github.com/dotnet/runtime/blob/0b3f3ab1ecf4de06459e5f0e2b7cb3baf70ef981/src/coreclr/jit/fginline.cpp&quot;&gt;https://github.com/dotnet/runtime/blob/0b3f3ab1ecf4de06459e5f0e2b7cb3baf70ef981/src/coreclr/jit/fginline.cpp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/dotnet/runtime/issues/10303&quot;&gt;https://github.com/dotnet/runtime/issues/10303&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/AndyAyersMS/PerformanceExplorer/blob/master/notes/notes-aug-2016.md&quot;&gt;https://github.com/AndyAyersMS/PerformanceExplorer/blob/master/notes/notes-aug-2016.md&lt;/a&gt;
&lt;!--
LSRA heuristics
https://github.com/dotnet/runtime/blob/2d638dc1179164a08d9387cbe6354fe2b7e4d823/docs/design/coreclr/jit/lsra-heuristic-tuning.md
--&gt;&lt;/p&gt;

&lt;h3 id=&quot;dart&quot;&gt;Dart&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/dart-lang/sdk/blob/391212f3da8cc0790fc532d367549042216bd5ca/runtime/vm/compiler/backend/inliner.cc#L49&quot;&gt;https://github.com/dart-lang/sdk/blob/391212f3da8cc0790fc532d367549042216bd5ca/runtime/vm/compiler/backend/inliner.cc#L49&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/dart-lang/sdk/blob/391212f3da8cc0790fc532d367549042216bd5ca/runtime/vm/compiler/backend/inliner.cc#L1023&quot;&gt;https://github.com/dart-lang/sdk/blob/391212f3da8cc0790fc532d367549042216bd5ca/runtime/vm/compiler/backend/inliner.cc#L1023&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20170830093403id_/https://link.springer.com/content/pdf/10.1007/978-3-540-78791-4_5.pdf&quot;&gt;https://web.archive.org/web/20170830093403id_/https://link.springer.com/content/pdf/10.1007/978-3-540-78791-4_5.pdf&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;DEFINE_FLAG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;deoptimization_counter_inlining_threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;How many times we allow deoptimization before we stop inlining.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DEFINE_FLAG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trace_inlining&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Trace inlining&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DEFINE_FLAG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;charp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inlining_filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Inline only in named function&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Flags for inlining heuristics.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DEFINE_FLAG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;inline_getters_setters_smaller_than&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;Always inline getters and setters that have fewer instructions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DEFINE_FLAG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;inlining_depth_threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;Inline function calls up to threshold nesting depth&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DEFINE_FLAG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;inlining_size_threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;Always inline functions that have threshold or fewer instructions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DEFINE_FLAG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;inlining_callee_call_sites_threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;Always inline functions containing threshold or fewer calls.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DEFINE_FLAG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;inlining_callee_size_threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;mi&quot;&gt;160&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;Do not inline callees larger than threshold&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DEFINE_FLAG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;inlining_small_leaf_size_threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;Do not inline leaf callees larger than threshold&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DEFINE_FLAG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;inlining_caller_size_threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;mi&quot;&gt;50000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;Stop inlining once caller reaches the threshold.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DEFINE_FLAG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;inlining_hotness&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;Inline only hotter calls, in percents (0 .. 100); &quot;&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;default 10%: calls above-equal 10% of max-count are inlined.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DEFINE_FLAG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;inlining_recursion_depth_threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;Inline recursive function calls up to threshold recursion depth.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DEFINE_FLAG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;max_inlined_per_depth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;Max. number of inlined calls per depth&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/assets/img/adaptive-inline.pdf&quot;&gt;An adaptive strategy for inline substitution&lt;/a&gt; (PDF)&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;c1&quot;&gt;// Inlining heuristics based on Cooper et al. 2008.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;InliningDecision&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ShouldWeInline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                  &lt;span class=&quot;kt&quot;&gt;intptr_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instr_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                  &lt;span class=&quot;kt&quot;&gt;intptr_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call_site_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Pragma or size heuristics.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inliner_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AlwaysInline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;callee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InliningDecision&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Yes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AlwaysInline&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inlined_size_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FLAG_inlining_caller_size_threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// Prevent caller methods becoming humongous and thus slow to compile.&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InliningDecision&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;No&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;--inlining-caller-size-threshold&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instr_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FLAG_inlining_callee_size_threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// Prevent inlining of callee methods that exceed certain size.&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InliningDecision&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;No&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;--inlining-callee-size-threshold&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Inlining depth.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callee_inlining_depth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inlining_depth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;callee_inlining_depth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;callee_inlining_depth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inlining_depth_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;FLAG_inlining_depth_threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InliningDecision&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;No&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;--inlining-depth-threshold&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Situation instr_count == 0 denotes no counts have been computed yet.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// In that case, we say ok to the early heuristic and come back with the&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// late heuristic.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instr_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InliningDecision&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Yes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;need to count first&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instr_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FLAG_inlining_size_threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InliningDecision&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Yes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;--inlining-size-threshold&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call_site_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FLAG_inlining_callee_call_sites_threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InliningDecision&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Yes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;--inlining-callee-call-sites-threshold&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InliningDecision&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;No&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;!--
CompileType
https://github.com/dart-lang/sdk/blob/d3c0a3768bd4be4a92886e136811b5f748b63ddd/runtime/vm/compiler/backend/compile_type.h#L43
--&gt;

&lt;!--
intrinsics
https://github.com/dart-lang/sdk/blob/d3c0a3768bd4be4a92886e136811b5f748b63ddd/runtime/vm/compiler/call_specializer.cc#L3229
--&gt;

&lt;h3 id=&quot;hhvm&quot;&gt;HHVM&lt;/h3&gt;

&lt;p&gt;tracelet based&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/facebook/hhvm/blob/eeba7ad1ffa372a9b8cc9d1ec7f5295d45627009/hphp/runtime/vm/jit/inlining-decider.h#L89&quot;&gt;https://github.com/facebook/hhvm/blob/eeba7ad1ffa372a9b8cc9d1ec7f5295d45627009/hphp/runtime/vm/jit/inlining-decider.h#L89&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;c1&quot;&gt;// Refuse if the cost exceeds our thresholds.&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// We measure the cost of inlining each callstack and stop when it exceeds a&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// certain threshold.  (Note that we do not measure the total cost of all the&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// inlined calls for a given caller---just the cost of each nested stack.)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;costOfInlining&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;callerSk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regionAndUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;annotationsPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cfg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HHIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AlwaysInlineVasmCostLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;folly&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sformat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cost={} within always-inline limit&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instrSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;irgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;budgetBCInstrs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;refuse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;folly&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sformat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;exhausted bytecode budget: budgetBCInstrs={}, regionSize={}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;irgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;budgetBCInstrs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instrSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()));&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxTotalCost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adjustedMaxVasmCost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inlineDepth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxCost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxTotalCost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Cfg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HHIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InliningUseStackedCost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;maxCost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;irgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inlineState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;baseProfCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s_baseProfCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callerProfCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;irgen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;curProfCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calleeProfCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;irgen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calleeProfCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxCost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inlineDepth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;irgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;refuse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;folly&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sformat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;too expensive: cost={} : maxCost={} : &quot;&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;baseProfCount={} : callerProfCount={} : calleeProfCount={} : depth={}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;cost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxCost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;baseProfCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callerProfCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calleeProfCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;folly&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sformat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;small region with return: cost={} : &quot;&lt;/span&gt;
                               &lt;span class=&quot;s&quot;&gt;&quot;maxTotalCost={} : maxCost={} : baseProfCount={}&quot;&lt;/span&gt;
                               &lt;span class=&quot;s&quot;&gt;&quot; : callerProfCount={} : calleeProfCount={}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                               &lt;span class=&quot;n&quot;&gt;cost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxTotalCost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxCost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;baseProfCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                               &lt;span class=&quot;n&quot;&gt;callerProfCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calleeProfCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;art&quot;&gt;ART&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/LineageOS/android_art/blob/8ce603e0c68899bdfbc9cd4c50dcc65bbf777982/compiler/optimizing/inliner.h&quot;&gt;https://github.com/LineageOS/android_art/blob/8ce603e0c68899bdfbc9cd4c50dcc65bbf777982/compiler/optimizing/inliner.h&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Instruction limit to control memory.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kMaximumNumberOfTotalInstructions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Maximum number of instructions for considering a method small,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// which we will always try to inline if the other non-instruction limits&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// are not reached.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kMaximumNumberOfInstructionsForSmallMethod&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Limit the number of dex registers that we accumulate while inlining&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// to avoid creating large amount of nested environments.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kMaximumNumberOfCumulatedDexRegisters&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Limit recursive call inlining, which do not benefit from too&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// much inlining compared to code locality.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kMaximumNumberOfRecursiveCalls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Limit recursive polymorphic call inlining to prevent code bloat, since it can quickly get out of&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// hand in the presence of multiple Wrapper classes. We set this to 0 to disallow polymorphic&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// recursive calls at all.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kMaximumNumberOfPolymorphicRecursiveCalls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Controls the use of inline caches in AOT mode.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kUseAOTInlineCaches&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Controls the use of inlining try catches.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kInlineTryCatches&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HInliner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdateInliningBudget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_number_of_instructions_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kMaximumNumberOfTotalInstructions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Always try to inline small methods.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;inlining_budget_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kMaximumNumberOfInstructionsForSmallMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;inlining_budget_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;kMaximumNumberOfInstructionsForSmallMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;kMaximumNumberOfTotalInstructions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total_number_of_instructions_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HInliner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsInliningEncouraged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HInvoke&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invoke_instruction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                    &lt;span class=&quot;n&quot;&gt;ArtMethod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CodeItemDataAccessor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CountRecursiveCallsOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kMaximumNumberOfRecursiveCalls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LOG_FAIL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stats_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MethodCompilationStat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kNotInlinedRecursiveBudget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Method &quot;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrettyMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; is not inlined because it has reached its recursive call budget.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inline_max_code_units&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;codegen_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetCompilerOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetInlineMaxCodeUnits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsnsSizeInCodeUnits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inline_max_code_units&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LOG_FAIL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stats_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MethodCompilationStat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kNotInlinedCodeItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Method &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrettyMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; is not inlined because its code item is too big: &quot;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsnsSizeInCodeUnits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; &amp;gt; &quot;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inline_max_code_units&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;graph_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsCompilingBaseline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;accessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsnsSizeInCodeUnits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CompilerOptions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kBaselineInlineMaxCodeUnits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LOG_FAIL_NO_STAT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Reached baseline maximum code unit for inlining  &quot;&lt;/span&gt;
                       &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrettyMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;outermost_graph_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetUsefulOptimizing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invoke_instruction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetLastInstruction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsThrow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LOG_FAIL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stats_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MethodCompilationStat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kNotInlinedEndsWithThrow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Method &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrettyMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; is not inlined because its block ends with a throw&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;outermost_graph_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsCompilingBaseline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsInvokeVirtual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsInvokeInterface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ProfilingInfoBuilder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsInlineCacheUseful&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AsInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;codegen_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maximum_inlining_depth_for_baseline&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;InlineCache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MaxDexPcEncodingDepth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;outermost_graph_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetArtMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;codegen_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetCompilerOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetInlineMaxCodeUnits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;depth_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maximum_inlining_depth_for_baseline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LOG_FAIL_NO_STAT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Reached maximum depth for inlining in baseline compilation: &quot;&lt;/span&gt;
                       &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depth_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; for &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callee_graph&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetArtMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrettyMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;outermost_graph_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetUsefulOptimizing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;jikesrvm&quot;&gt;JikesRVM&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/JikesRVM/JikesRVM/blob/5072f19761115d987b6ee162f49a03522d36c697/rvm/src/org/jikesrvm/compilers/opt/inlining/DefaultInlineOracle.java#L55&quot;&gt;https://github.com/JikesRVM/JikesRVM/blob/5072f19761115d987b6ee162f49a03522d36c697/rvm/src/org/jikesrvm/compilers/opt/inlining/DefaultInlineOracle.java#L55&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;otherresearch&quot;&gt;Other/research&lt;/h3&gt;

&lt;p&gt;Partial inlining&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://ethz.ch/content/dam/ethz/special-interest/infk/ast-dam/documents/Theodoridis-ASPLOS22-Inlining-Paper.pdf&quot;&gt;Understanding and Exploiting Optimal Function Inlining&lt;/a&gt; (PDF)&lt;/p&gt;

&lt;p&gt;machine learning&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://ieeexplore.ieee.org/document/6495004&quot;&gt;Automatic construction of inlining heuristics using machine learning&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://ssw.jku.at/Teaching/PhDTheses/Mosaner/Dissertation%20Mosaner.pdf&quot;&gt;Machine-Learning-Based Optimization Heuristics in Dynamic Compilers&lt;/a&gt; (PDF)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://webdocs.cs.ualberta.ca/~amaral/thesis/ErickOchoaMSc.pdf&quot;&gt;Guiding Inlining Decisions Using Post-Inlining Transformations&lt;/a&gt; (PDF)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://karimali.ca/resources/papers/ourinliner.pdf&quot;&gt;U Can’t Inline This!&lt;/a&gt; (PDF)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dl.acm.org/doi/10.1145/182409.182489&quot;&gt;Towards better inlining decisions using inlining trials&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/chrisseaton/rhizome/blob/main/doc/inlining.md&quot;&gt;RhizomeRuby inlining&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://aleksandar-prokopec.com/resources/docs/prio-inliner-final.pdf&quot;&gt;An Optimization-Driven Incremental Inline Substitution Algorithm for Just-in-Time Compilers&lt;/a&gt; (PDF)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cresco.enea.it/SC05/schedule/pdf/pap274.pdf&quot;&gt;Automatic Tuning of Inlining Heuristics&lt;/a&gt; (PDF)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dl.acm.org/doi/pdf/10.1145/3563838.3567677&quot;&gt;Inlining-Benefit Prediction with Interprocedural Partial Escape Analysis&lt;/a&gt; (PDF)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/img/virtual-inlining.pdf&quot;&gt;Inlining of Virtual Methods&lt;/a&gt; (PDF)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/img/sable-inlining.pdf&quot;&gt;A Study of Type Analysis for Speculative Method Inlining in a JIT Environment&lt;/a&gt; (PDF)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dl.acm.org/doi/epdf/10.1145/351403.351416&quot;&gt;A Comparative Study of Static and Profile-Based Heuristics for Inlining&lt;/a&gt; (PDF)&lt;/p&gt;

&lt;p&gt;clusters from &lt;a href=&quot;https://llvm.org/devmtg/2022-05/slides/2022EuroLLVM-CustomBenefitDrivenInliner-in-FalconJIT.pdf&quot;&gt;Custom benefit-driven inliner in Falcon JIT&lt;/a&gt; (PDF)&lt;/p&gt;

&lt;h3 id=&quot;graal&quot;&gt;Graal&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/oracle/graal/blob/5dde777cba22a99ebe3f19745d03ddfbc35c563c/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/inlining/policy/GreedyInliningPolicy.java&quot;&gt;https://github.com/oracle/graal/blob/5dde777cba22a99ebe3f19745d03ddfbc35c563c/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/inlining/policy/GreedyInliningPolicy.java&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/oracle/graal/blob/5dde777cba22a99ebe3f19745d03ddfbc35c563c/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/inlining/InliningPhase.java&quot;&gt;https://github.com/oracle/graal/blob/5dde777cba22a99ebe3f19745d03ddfbc35c563c/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/inlining/InliningPhase.java&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/oracle/graal/blob/5dde777cba22a99ebe3f19745d03ddfbc35c563c/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/inlining/info/elem/InlineableGraph.java#L148&quot;&gt;https://github.com/oracle/graal/blob/5dde777cba22a99ebe3f19745d03ddfbc35c563c/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/inlining/info/elem/InlineableGraph.java#L148&lt;/a&gt;
&lt;!--
GVN
https://github.com/oracle/graal/blob/5dde777cba22a99ebe3f19745d03ddfbc35c563c/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/DominatorBasedGlobalValueNumberingPhase.java#L132
--&gt;&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:aot-split&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;There are some newer papers, especially in Java land, that try to
do a lot of analysis ahead-of-time and bundle the resulting information in
.class files. Then the JIT can read it and see more than local context.&lt;/p&gt;

      &lt;p&gt;Or, if you are an AOT compiler, you can probably do a lot more whole system
reasoning—both for time budget reasons and also because you can see more
functions at once. &lt;a href=&quot;#fnref:aot-split&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:rpython-inliner&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/pypy/pypy/blob/bab69dca82606f9e4feaf5507f8dd8dfb3e968b2/rpython/translator/backendopt/inline.py#L144&quot;&gt;Check it
out&lt;/a&gt;
if you like. I stumbled across it by accident. &lt;a href=&quot;#fnref:rpython-inliner&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:turbolev&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;See also “Turbolev”, which seems to merge Maglev (CFG) with
Turbofan (Sea of Nodes)… somehow. &lt;a href=&quot;#fnref:turbolev&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:fil&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Potentially a misunderstanding based on a private conversation. I’m
working on tracking down the implementation… &lt;a href=&quot;#fnref:fil&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
            <pubDate>Wed, 03 Jun 2026 00:00:00 +0000</pubDate>
            <niceDate>June 3, 2026</niceDate>
            <link>https://bernsteinbear.com/blog/inlining-heuristics/?utm_source=rss</link>
            <guid isPermaLink="true">https://bernsteinbear.com/blog/inlining-heuristics/</guid>
        </item>
        
        <item>
            <title>Checking assembly with Z3</title>
            <description>&lt;p&gt;Short post today. New ZJIT contributor dak2 &lt;a href=&quot;https://github.com/ruby/ruby/pull/17165&quot;&gt;submitted a
PR&lt;/a&gt; to fix an overflow bug in fixnum
division in ZJIT. We did the division fine, but lied about the type of the
result in the case of dividing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FIXNUM_MIN&lt;/code&gt; by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-1&lt;/code&gt;. You can see how this is
special-cased in CRuby:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;inline&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;rb_fix_divmod_fix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VALUE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VALUE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VALUE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;divp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VALUE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;modp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FIXNUM_MIN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;divp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;divp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LONG2NUM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FIXNUM_MIN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;modp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;modp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LONG2FIX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-FIXNUM_MIN&lt;/code&gt; (note the negative) does not fit in a fixnum, it gets
promoted to a bignum. It’s one of two special cases in fixnum division that
does not produce a fixnum, the other being dividing by zero (which produces an
error).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ irb
irb(main):001&amp;gt; LONG_MAX = 2**63 - 1
=&amp;gt; 9223372036854775807
irb(main):002&amp;gt; FIXNUM_MAX = LONG_MAX / 2
=&amp;gt; 4611686018427387903
irb(main):003&amp;gt; LONG_MIN = -LONG_MAX - 1
=&amp;gt; -9223372036854775808
irb(main):004&amp;gt; FIXNUM_MIN = LONG_MIN / 2
=&amp;gt; -4611686018427387904
irb(main):005&amp;gt; (-FIXNUM_MIN) &amp;lt; FIXNUM_MAX
=&amp;gt; false
irb(main):006&amp;gt;
$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is due to the numbers being &lt;a href=&quot;https://en.wikipedia.org/wiki/Two%27s_complement&quot;&gt;two’s
complement&lt;/a&gt; and therefore
having more negative numbers than positive numbers (because of zero).&lt;/p&gt;

&lt;p&gt;dak2’s proposed patch included a branchless test for this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;left == FIXNUM_MIN&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;right == -1&lt;/code&gt; case, making us leave JIT code and enter the interpreter rather
than handle it inline. The patch encodes this branchless test as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xor&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xor&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;or&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;je&lt;/code&gt; in our platform-independent low-level IR (LIR):&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Side exit on FIXNUM_MIN / -1, which overflows to a Bignum, not a Fixnum.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Branchless (left == FIXNUM_MIN &amp;amp;&amp;amp; right == -1): (left ^ MIN) | (right ^ -1) == 0.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left_diff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asm&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.xor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Opnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;VALUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fixnum_from_isize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RUBY_FIXNUM_MIN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right_diff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asm&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.xor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Opnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;VALUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fixnum_from_isize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combined&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asm&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.or&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left_diff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right_diff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;asm&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;combined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;asm&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.je&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;side_exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FixnumDivOverflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I didn’t understand why those were equivalent. Rather than try and bang my head
against it, I thought I’d let Z3 try. After all, I’ve been watching &lt;a href=&quot;https://pypy.org/posts/2024/08/toy-knownbits.html&quot;&gt;CF have
fun with it&lt;/a&gt; &lt;a href=&quot;https://pypy.org/posts/2024/07/finding-simple-rewrite-rules-jit-z3.html&quot;&gt;for a couple
years now&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Z3 is an SMT solver. The core trick to use Z3 as a “proof engine” that I
learned from CF&lt;sup id=&quot;fnref:standard&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:standard&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; is to make Z3 search for counter-examples by negating the
condition:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/env python3
# /// script
# requires-python = &quot;&amp;gt;=3.13&quot;
# dependencies = [
#     &quot;z3-solver&quot;,
# ]
# ///
&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;z3&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;WORD_SIZE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;LONG_MAX&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitVecVal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;63&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WORD_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;LONG_MIN&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LONG_MAX&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;FIXNUM_MIN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LONG_MIN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;prove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cond&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;solver&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Solver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Not&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cond&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unsat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitVec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;left&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WORD_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitVec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;right&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WORD_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# The original C condition
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lhs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;And&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FIXNUM_MIN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# The new branchless LIR
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rhs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FIXNUM_MIN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;prove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lhs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rhs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This negation is required because the core model of Z3 is a machine that finds
example values. This means that there is an implicit “exists” in front of your
condition. To disprove this, Z3 needs to search all inputs. However, if you
negate the condition, it becomes a “for all”. This means that in order to
disprove the “for all”, Z3 only needs to find a single counterexample.&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;uv run prove_fixnum_min.py
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Z3 did not complain, so the new code must be fine. Just as a quick check, in
case &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assert&lt;/code&gt; is turned off or something, I like to see tests fail. So after
modifying one of the constants from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-1&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;uv run prove_fixnum_min.py
&lt;span class=&quot;go&quot;&gt;Traceback (most recent call last):
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;  File &quot;/path/prove_fixnum_min.py&quot;, line 26, in &amp;lt;module&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;    prove(lhs == rhs)
    ~~~~~^^^^^^^^^^^^
  File &quot;/path/prove_fixnum_min.py&quot;, line 18, in prove
    assert solver.check(z3.Not(cond)) == z3.unsat, solver.model()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: [right = 18446744073709551615, left = 13835058055282163712]
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Neat.&lt;/p&gt;

&lt;h3 id=&quot;thanks&quot;&gt;Thanks&lt;/h3&gt;

&lt;p&gt;Thanks to &lt;a href=&quot;https://cfbolz.de/&quot;&gt;CF Bolz-Tereick&lt;/a&gt; for reading and giving feedback
on this post.&lt;/p&gt;

&lt;h3 id=&quot;update&quot;&gt;Update&lt;/h3&gt;

&lt;p&gt;I tried running this with &lt;a href=&quot;https://github.com/BasisResearch/lean.py&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lean_py&lt;/code&gt;&lt;/a&gt;
after seeing a
&lt;a href=&quot;https://bsky.app/profile/kirancodes.me/post/3mmhenqbcy22w&quot;&gt;post&lt;/a&gt; about it. I
installed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;elan&lt;/code&gt; and got a Lean installation going and manage to successfully
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import lean_py.z3 as z3&lt;/code&gt; and whatnot, but then I think compiling the Lean
program failed somehow:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cedar% uv run prove_fixnum_min.py
Traceback (most recent call last):
  File &quot;/home/max/Documents/code/bernsteinbear.com/prove_fixnum_min.py&quot;, line 28, in &amp;lt;module&amp;gt;
    z3.prove(lhs == rhs)
...
...
  File &quot;/home/max/.cache/uv/environments-v2/prove-fixnum-min-67c8d67f8106c77d/lib/python3.13/site-packages/lean_py/utils.py&quot;, line 17, in run_command
    raise RuntimeError(f&quot;Command {args} exited with {res.returncode}: {stderr}&quot;)
RuntimeError: Command [&apos;lake&apos;, &apos;build&apos;] exited with 1: error: build failed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If I have time, I will come back to this and try and figure out what the
inscrutable error is. I’ve never used Lean before, though…&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:standard&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;They note that this is a “standard technique” and they did not
come up with it. &lt;a href=&quot;#fnref:standard&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
            <pubDate>Mon, 01 Jun 2026 00:00:00 +0000</pubDate>
            <niceDate>June 1, 2026</niceDate>
            <link>https://bernsteinbear.com/blog/asm-z3/?utm_source=rss</link>
            <guid isPermaLink="true">https://bernsteinbear.com/blog/asm-z3/</guid>
        </item>
        
        <item>
            <title>Travel notes: RubyKaigi Hakodate</title>
            <description>&lt;p&gt;I just got back from a three and a half week trip to Japan. It was the longest
trip I have ever been on (aside from studying abroad in Germany, which felt
different). I made the following wild circuit with only a backpack and a
duffel:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Tokyo&lt;/li&gt;
  &lt;li&gt;Toyama&lt;/li&gt;
  &lt;li&gt;Kanazawa&lt;/li&gt;
  &lt;li&gt;Nara ish&lt;/li&gt;
  &lt;li&gt;Ito&lt;/li&gt;
  &lt;li&gt;Hakodate&lt;/li&gt;
  &lt;li&gt;Nikko&lt;/li&gt;
  &lt;li&gt;Mashiko&lt;/li&gt;
  &lt;li&gt;Karuizawa&lt;/li&gt;
  &lt;li&gt;Tokyo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This trip was split into three parts: time with my immediate family, going to a
conference, and then time with my partner. They were all great and also I am
glad to be home.&lt;/p&gt;

&lt;p&gt;I’ll post my abbreviated travel notes here, including activity and food
recommendations.&lt;/p&gt;

&lt;h2 id=&quot;part-one&quot;&gt;Part one&lt;/h2&gt;

&lt;p&gt;We started in Tokyo but we were only there for about 40 hours. We focused our
time mostly on arts and crafts: we did a kintsugi workshop, spent time at an
artists cooperative, and then did a lot of walking around. This was a good
intro to the trip, because everyone kept waking up at 4am and crashing at 7pm
due to the jet lag. 4am wakeup makes for nice morning walks to 7-Eleven.&lt;/p&gt;

&lt;p&gt;I brought my family to &lt;a href=&quot;https://plus.codes/MQH8+WQ&quot;&gt;T’s Tantan&lt;/a&gt; in Tokyo
Station because I’m vegetarian and it’s otherwise hard to find ramen that
approaches kosher in Japan. It continues to be great and I really appreciate
having a steady vegetarian option available. Many years ago when I visited
Tokyo there was a place that served a delicious tomato-based vegetarian ramen,
but I hear it has since permanently closed. Bummer.&lt;/p&gt;

&lt;p&gt;We took the shinkansen to Kanazawa. I love the train. It’s fast. It’s quiet. You
can eat your snacks on board and gaze out the window as the world whizzes by.
It’s nice.&lt;/p&gt;

&lt;p&gt;We toured a soy sauce factory (meh; they don’t let you in the room where the
magic happens) and the old town (pretty!) before finally eventually ending up
at our small hotel in Toyama: Satoyama Auberge Maki No Oto. I highly recommend
this hotel. It is beautiful, the staff is lovely, the food was excellent, and
they were very accomodating of me being vegetarian.&lt;/p&gt;

&lt;p&gt;We continued on to Toyama, which is a port town. We got to talking with an
older local guy who told us all about his favorite local spots. We learned
after leaving that this guy has extraordinarily fancy taste and they were all
either Michelin starred or at least Michelin rated and with a lead time of
months. We opted to instead go to a local brewery, which had a ghost pepper
beer (!) and pizza.&lt;/p&gt;

&lt;p&gt;We then moved on via train to Osaka, where we transferred to a car to head
(eventually) to our hotel in the hills near Nara. We toured the Daimon sake
brewery. They explained every little thing about the process, which was
especially interesting to me, as I’ve done some small amount of homebrewing and
I bake. They sounded similar. We had a tasting and even got to talk to
Daimon-san. I recommend going.&lt;/p&gt;

&lt;p&gt;I also recommend the Akame 48 waterfalls walk/hike, which has some exquisite
falls, and Murou Art Forest. They had some really wonderful installations.&lt;/p&gt;

&lt;p&gt;My brother and I parted ways from the rest of my family in Osaka: they headed
further west and we headed north to Itō on the Izu peninsula. We got a surprise
perfectly clear view of Fuji along the way.&lt;/p&gt;

&lt;p&gt;It’s beautiful there. They don’t seem to welcome foreigners in a lot of their
restaurants (we were turned away several times) but one place had a guy who
enthusiastically welcomed us in. We ended that evening enjoying a some food and
a beer while also being stared at by a 300lb completely tattooed guy. It was a
little unsettling but we left without incident.&lt;/p&gt;

&lt;p&gt;My brother and I made our way to Tokyo for the day before his flight and before
my train north to Hakodate for RubyKaigi. I once again did that thing where I
walked around in humid 80F heat with a large backpack and pants and was
extraordinarily warm toward the end of the day. After about a liter of Aquarius
on the train north I felt better.&lt;/p&gt;

&lt;h2 id=&quot;part-two&quot;&gt;Part two&lt;/h2&gt;

&lt;p&gt;I stayed at &lt;a href=&quot;https://maps.app.goo.gl/fQaP6XfCQMUGA7Zx8&quot;&gt;Yunokawa Prince Hotel
Nagisatei&lt;/a&gt; which I would like to
especially call out for having an enormous, diverse, and very vegetarian
friendly breakfast. Every morning I got to try new and tasty things and even
feel full after. It was great.&lt;/p&gt;

&lt;p&gt;Hakodate is &lt;em&gt;beautiful&lt;/em&gt; in the spring. I arrived at peak cherry blossom season
and Goryokaku, their star shaped fort, is absolutely decked out in cherry
blossoms. It is also moderately swarmed by tourists (in this case, three cruise
ships). It didn’t feel over-crowded though. I enjoyed eating at &lt;em&gt;The Bear King&lt;/em&gt;
which had a vegetarian friendly option.&lt;/p&gt;

&lt;p&gt;The next day was the committer meeting. I don’t remember a ton from it other
than people talking at length about the semantics of deep freezing an object
(do you freeze its class? its class’s superclass? …?). I picked up my badge
and also got to check out my colleague Chris Salzberg’s bar
&lt;a href=&quot;https://maps.app.goo.gl/AE48wZopJB16VBta9&quot;&gt;SOLENOID&lt;/a&gt;! It’s a neat spot. I
headed out to go find some dinner.&lt;/p&gt;

&lt;p&gt;This is about when I got a message on my phone that there was going to be an
earthquake, so I walked back into the bar and said “hey, did you get this?”
just before everything started shaking. It was the biggest earthquake I’ve
experienced, but I was metaphorically not too shaken up. Then we got the
tsunami warning.&lt;/p&gt;

&lt;p&gt;Chris’s bar is already something like 8 meters above sea level and at the foot
of Mt Hakodate. With the city sirens going off and the police directing traffic
with batons, though, I decided my best bet was just to march directly up the
mountain to get more elevation. Since the tsunami wasn’t scheduled to arrive
for about 20 or 30 minutes and my hotel was across the sea-level part of town,
I parked myself on a little concrete post. Chris found me eventually.
Someone told us that there was a middle school offering refuge, so we went and
hung out on the side of the gymnasium. They were really nice about it.&lt;/p&gt;

&lt;p&gt;On Wednesday, the conference started. It was really well signed and organized.
My usual complaint with conferences is that there’s nothing to eat for
vegetarians (or that we get mashed with the gluten-free people and each group
only gets a salad and bad bread) but that did not happen! They had really
stellar vegetarian bento. They had a lot of leftovers toward the end of lunch
so I even went and got a second. This was about when I started freaking out
because my speaking slot was approaching and I wasn’t yet feeling my talk.&lt;/p&gt;

&lt;p&gt;Normally when I give a talk, I get up in front of people and I pace and
gesticulate and productively complain and throw in some fun anecdotes and the
audience, one way or another, ends up learning about JITs at scale, or Scheme
semantics, or something. It’s what I’d done for my little lunch talk at Brown
two weeks prior. I even titled that talk &lt;em&gt;One must imagine compiler engineers
happy&lt;/em&gt; so there was plenty of room for educational complaining. But this
RubyKaigi talk was in front of an enormous crowd and toward a more general
audience than I was used to addressing. The slides did not feel like they were
flowing until about twenty minutes before my talk.&lt;/p&gt;

&lt;p&gt;In the end it went alright. I realized about 40 seconds in that I had way
too much content so I ended up speaking rapidly for 30 minutes straight,
completely unaware of the audience (which you can’t see anyway because of the
lights). I only really noticed people when I made a dumb six-seven joke and
Aaron laughed.&lt;/p&gt;

&lt;p&gt;The rest of the conference I was able to relax and enjoy other people’s talks.
I got some good hallway track in, too. I think there’s a good group of people
who are interested in Ruby tracing (for example, &lt;a href=&quot;https://railsatscale.com/2026-03-27-using-perfetto-in-zjit/&quot;&gt;Perfetto in
ZJIT&lt;/a&gt;) so maybe
we will make something happen.&lt;/p&gt;

&lt;p&gt;We had a nice small dinner at &lt;em&gt;Yasai Bar Miruya&lt;/em&gt;, which was vegan (!) and had
some nice sake. The host was very friendly, too.&lt;/p&gt;

&lt;p&gt;I nerd-sniped John and J into implementing a VM for the &lt;a href=&quot;https://www.boundvariable.org/task.shtml&quot;&gt;Universal
Machine&lt;/a&gt;. This was a daunting
homework assignment back in undergrad but it was a fun project later in life.&lt;/p&gt;

&lt;p&gt;S joined toward the end of the conference. She’s also vegetarian so we got some
really excellent vegetarian ramen at &lt;a href=&quot;https://maps.app.goo.gl/JNqiB6aH9sStnZ5UA&quot;&gt;MAIDO
Ramen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, S and I headed south on the shinkansen for Nikko.&lt;/p&gt;

&lt;h2 id=&quot;part-three&quot;&gt;Part three&lt;/h2&gt;

&lt;p&gt;Nikko is small, beautiful, and a tourist day-trip town. Dinner closes early.
Shops close earlier. Since we were staying there we had to make sure to track
down and visit the one or two vegetarian places before they shuttered.&lt;/p&gt;

&lt;p&gt;S and I, along with J and J, took the bus up from Nikko, up the windiest
switchbacks, to the Kegon Falls. We were going to take a boat across the lake,
but the water level was too low for the dock on the other side, so we ended up
half hiking and half taking a bus. Then we continued our hike through the
Senjōgahara Marshland (beautiful), to the Yudaki Cascades (lovely), which also
had a surprise restaurant and ice cream shop at the base! It’s called &lt;a href=&quot;https://maps.app.goo.gl/G5tNK46WamixEUJp8&quot;&gt;Yutaki
Rest House&lt;/a&gt;. After some great
(vegetarian friendly!! wow!!) udon, we marched up the waterfall and around Yuno
Lake at the top to Yumoto Onsen. In order to make the last reasonable bus back
to town, we just enjoyed putting our feet in the foot bath.&lt;/p&gt;

&lt;p&gt;One day was rainy. In the evening, J and I thought it would be fun to continue
our Universal Machine implementations. As Norman Ramsey would say, “my
implementation is 90 lines long and runs sandmark in under six seconds.”&lt;/p&gt;

&lt;p&gt;We also enjoyed doing a tour of the shrines right above Nikko. The shrines are
resplendent against the backdrop of forest.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Pro bus tip: you can either pay by IC card or credit card. No need to grab a
ticket if you do that.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;S and I shipped our bags (thanks, Yamato) before continuing on to the small
town of Moka, the staging area for our big pottery festival day. Unfortunately,
there was no good way to get there: there was no reasonable series of trains
and no taxi would take us. Ultimately we ended up taking the train to
Utsonomiya and catching the long local bus to Moka. About twenty minutes into
this ride, in the middle of nowhere, bus nearly empty, the bus driver pulled
over and ran over to us looking kind of panicked. He asked where we were going
and was visibly relieved when we said Moka. I suppose we are not the usual
riders. Very nice of him.&lt;/p&gt;

&lt;p&gt;Upon arrival, S introduced me to CoCo ICHIBANYA, which is also super vegetarian
friendly. I loved it. We ate really well before walking to our tiny hotel.&lt;/p&gt;

&lt;p&gt;We did not really know what to expect from the Mashiko pottery festival. The
internet said it would be crowded and to arrive early, so we got up at 6:30am
for estimated 7am departure on the tiny train from Moka to Mashiko. On most
trains you can pay with an IC card but we were out in the sticks so we asked
the only other guy on the platform how to pay for the train. He said he had no
idea and that this was his first time here. When the train showed up completely
packed to the gills and we had to (politely) push onto it, we started to
realize that this was The Event and it was going to be mayhem.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Also, fun fact: the way the Moka train payment works is that you grab a
little ticket from the train, and, upon arrival, wait in line to present your
ticket to two very overwhelmed looking people at a table, who charge you, and
you pay in cash.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Onto Mashiko: the festival was &lt;em&gt;packed&lt;/em&gt;. There’s pottery everywhere the eye can
see. There are tents and there are full buildings. It varies in quality and
artistry from fine to jaw-droppingly spectacular. You could completely stock
your kitchen from this fair alone and it would even be cost-effective. The main
bummer for us is that we had to get pottery safely back home. We limited
ourselves to a reasonable assortment but we really wanted to buy a beautiful
painted 20 inch plate with a bird on a branch.&lt;/p&gt;

&lt;p&gt;After a ton of walking around, we took another long long bus back to Utsonomiya
and continued onto Karuizawa. We didn’t know what to expect from Karuizawa but,
having been, I could probably concisely describe it as “Aspen for people from
Tokyo”. It was… fine. We loved our hotel, Tsuruya Ryokan. The manager was
very excited when we borrowed a Studio Ghibli DVD from their collection.&lt;/p&gt;

&lt;p&gt;We continued on to Tokyo, our final stop. We our usual tour of stationery
stores and bakeries—the bread was something to write home about (har har). We
enjoyed a (vegetarian!! friendly!!) kaiseki meal at &lt;a href=&quot;https://maps.app.goo.gl/NCwQk2ReXdgKndRJ7&quot;&gt;Hyoki Shabu-shabu
Ginza&lt;/a&gt; before enjoying some live
music at &lt;a href=&quot;https://maps.app.goo.gl/Cw3vvo2vpYFbWpAHA&quot;&gt;Rocky Top&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We also recommend &lt;a href=&quot;https://maps.app.goo.gl/bNNgFjMXytKacC5U9&quot;&gt;Jikasei MENSHO&lt;/a&gt;
for vegetarian ramen.&lt;/p&gt;

&lt;p&gt;Bakery checklist:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;BOUL’ANGE NIHONBASHI (check! good croissants)&lt;/li&gt;
  &lt;li&gt;Bricolage bread &amp;amp; co (check! good everything)&lt;/li&gt;
  &lt;li&gt;Brasserie Viron Marunouchi&lt;/li&gt;
  &lt;li&gt;Beaver Bread&lt;/li&gt;
  &lt;li&gt;Bricolage bread &amp;amp; co.&lt;/li&gt;
  &lt;li&gt;Bartizan Bread Factory&lt;/li&gt;
  &lt;li&gt;Gontran Cherrier Tokyo Aoyama Shop&lt;/li&gt;
  &lt;li&gt;Comme’N Tokyo&lt;/li&gt;
  &lt;li&gt;Shiomi Bakery&lt;/li&gt;
  &lt;li&gt;BRØD&lt;/li&gt;
  &lt;li&gt;The Little BAKERY
&amp;lt;!–&lt;/li&gt;
  &lt;li&gt;https://www.jocjapantravel.com/kanto-tokyo-bakeries/
–&amp;gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We had an uneventful and reasonably easy trip home. Whew. Long post for a long
trip. See you next year in Miyazaki!&lt;/p&gt;
</description>
            <pubDate>Mon, 18 May 2026 00:00:00 +0000</pubDate>
            <niceDate>May 18, 2026</niceDate>
            <link>https://bernsteinbear.com/blog/travel-notes-rubykaigi-hakodate/?utm_source=rss</link>
            <guid isPermaLink="true">https://bernsteinbear.com/blog/travel-notes-rubykaigi-hakodate/</guid>
        </item>
        
        <item>
            <title>Partial static single information form</title>
            <description>&lt;p&gt;In compilers, static single information form (SSI) is a common extension to
static single assignment form (SSA). It was introduced by C. Scott Ananian in
1999 in his &lt;a href=&quot;/assets/img/ananian-thesis.pdf&quot;&gt;MS thesis&lt;/a&gt; (PDF) &lt;sup id=&quot;fnref:et-al&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:et-al&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;SSI extends your existing SSA intermediate representation by discovering facts
from your existing program and reifying them as path-dependent/flow-sensitive
IR nodes. That might sound complicated, but at least the basic idea is pretty
natural. I talk a little bit about it in &lt;a href=&quot;/blog/irs/&quot;&gt;What I talk about when I talk about
IRs&lt;/a&gt; and I’ll rehash here in more depth, starting with some
motivating examples. Consider this admittedly contrived example:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Integer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# ...
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PositiveInteger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbsoluteValue&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v0&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We should be able to learn from the comparison that in some branches in the IR,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0&lt;/code&gt; is positive. In that region, we can add a new IR instruction &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v2&lt;/code&gt; that
attaches that knowledge right in the instruction’s type field (yay,
sparseness!) and then rewrite uses of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0&lt;/code&gt; to now use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v2&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Integer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PositiveInteger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RefineType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Positive&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# ...
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PositiveInteger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbsoluteValue&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v2&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Because we’ve done that, our (imaginary) optimization rule that gets rid of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AbsoluteValue&lt;/code&gt; on known-positive integers can kick in, and we can delete the
invocation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AbsoluteValue&lt;/code&gt;. Yay, optimization!&lt;/p&gt;

&lt;p&gt;But a couple of questions remain, at least for me:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Where/when in the compiler pipeline do we insert and remove these type
refinements?&lt;/li&gt;
  &lt;li&gt;Do we need to refine after &lt;em&gt;every&lt;/em&gt; conditional?&lt;/li&gt;
  &lt;li&gt;Do we need to implement the whole into-SSI and out-of-SSI algorithms from
all the complicated-looking papers?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We’ll go through them, starting with the compiler pipeline.&lt;/p&gt;

&lt;h2 id=&quot;when-do-we-insert-type-refinements&quot;&gt;When do we insert type refinements?&lt;/h2&gt;

&lt;p&gt;The original SSI paper starts with (I think?) SSA form and places some number
of new refinement nodes based on conditionals. I have admittedly not tried very
hard, but the into-SSI algorithms look complicated and kind of heavyweight. As
a reward, you get “linear” into-SSI time complexity.&lt;/p&gt;

&lt;p&gt;But I am a humble compiler engineer, and I don’t have the time to go through
and load all of this into my head. Instead what I have seen done and have been
doing is to take a shortcut: build &lt;em&gt;partial SSI&lt;/em&gt; during SSA
construction&lt;sup id=&quot;fnref:llvm-partial-ssi&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:llvm-partial-ssi&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Most of the time this is from bytecode, but it could also be from some other
non-SSA IR. In any case, this is an excellent shortcut for two reasons:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;It lets me cleanly separate adding the type refinements (pretty
straightforward) from the hard part of doing all of the operand rewriting
and phi placement and marking and all manner of other nonsense.&lt;/li&gt;
  &lt;li&gt;In addition to separating the concerns, the hard part is &lt;em&gt;already done&lt;/em&gt; by
SSA construction. We can actually just skip it! SSA construction handles phi
placement, operand rewriting, all of it. It probably fits neatly into a
naive or a &lt;a href=&quot;/assets/img/braun13cc.pdf&quot;&gt;Braun-style&lt;/a&gt; (PDF) construction.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is pretty compelling. We can learn from the bytecode with a very small
amount of marginal new complexity. See &lt;a href=&quot;https://github.com/ruby/ruby/pull/15915/changes#diff-a3cbeb79bf318b2aa8cc979260ba03b0204b436f745dd199a0e0c8ea5c871058&quot;&gt;my implementation in
ZJIT&lt;/a&gt;, for example. All it really does is modify the abstract
interpreter state when building SSA out of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;branchnil&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;branchif&lt;/code&gt;, and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;branchunless&lt;/code&gt; bytecode instructions to take into account the new refined
values.&lt;/p&gt;

&lt;p&gt;This is fine for branches that are already in the user’s source program but
sometimes optimization, especially of dynamic languages, adds new branches that
were not there before. And sometimes these branches get added much later, long
after SSA construction. What then? Can we do something similar and rely on
existing infrastructure?&lt;/p&gt;

&lt;h3 id=&quot;during-ssa-optimization&quot;&gt;During SSA optimization&lt;/h3&gt;

&lt;p&gt;Implicit in this “can we do it” is the assumption that your IR tracks data
dependencies from use to corresponding def, but &lt;em&gt;not&lt;/em&gt; from def to uses. Sea of
Nodes (at least the &lt;a href=&quot;https://github.com/SeaOfNodes/Simple&quot;&gt;Simple&lt;/a&gt;
implementation), is an IR that tracks both directions all the time for easier
rewriting. Many IRs do not do this, so we will continue assuming that there’s
no “easy way out”.&lt;/p&gt;

&lt;p&gt;JIT optimization of dynamic language compilers often adds synthetic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Guard&lt;/code&gt;
instructions to the IR that enforce pre-conditions. These guards allow
optimizing happy/fast path cases in JIT code while leaving the interpreter as a
fallback. For example, we might be able to optimize two back-to-back
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setinstancevariable&lt;/code&gt; instructions (a very dynamic operation in the world of
ideas, but fast when concretely implemented using object shapes) from:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;x = ...
setinstancevariable x, :@a, 1
setinstancevariable x, :@b, 2
# ... use x somewhere ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;which is very generic and involves calling into C code that might raise an
exception, to something more like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;x = ...
v0 = GuardHeapObject x
v1 = GuardShape v0, 0xcafe
v2 = Const 1
StoreField v1, 0x8, v2
v3 = GuardHeapObject x
v4 = GuardShape v3, 0xcafe
v5 = Const 2
StoreField v4, 0x10, v5
# ... use x somewhere ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;which is &lt;em&gt;much faster&lt;/em&gt; (assuming shape stability at run-time). There’s an
irritating problem, though, which is that we have a bunch of duplicate
instructions littered around the IR now because our optimizer worked on each
instruction individually. Kind of a “template optimizer” situation. Now we need
some pass to clean up the detritus.&lt;/p&gt;

&lt;p&gt;Global value numbering (GVN) will do a good job of de-duplicating instructions.
It should notice that we already have an instruction that looks like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GuardHeapObject x&lt;/code&gt; called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0&lt;/code&gt; and rewrite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v3&lt;/code&gt; into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v3 = v0&lt;/code&gt;. That’s great
because we have de-duplicated the guard. GVN may not get everything, though; if
some instructions later use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt;, they will not get rewritten to instead use the
output of these new guard instructions. To do that, we need to add some kind of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canonicalize&lt;/code&gt; pass or augment GVN with some canonicalization feature. That
canonicalization would handle rewriting operands to use the “latest version” of
some value, so to speak. See the canonicalization section of Chris Fallin’s
&lt;a href=&quot;https://cfallin.org/blog/2026/04/09/aegraph/&quot;&gt;excellent aegraphs blog post&lt;/a&gt;
for more (and of course the (currently block-local) &lt;a href=&quot;https://github.com/ruby/ruby/commit/ece14b61f505eea1ebefb3b8295df0fcf4d22567&quot;&gt;implementation in
ZJIT&lt;/a&gt;).&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;canonicalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;rewrite_map&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;bb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map_operands!&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;rewrite_map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;opcode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:guardtype&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;rewrite_map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;operands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Where I’m going with all of this, though, is that you may already have some
dominance-based instruction rewriting mechanism in your compiler, either as
part of GVN or separately! And you can use this to do a very low code
into-partial-SSI in the middle of your optimizer.&lt;/p&gt;

&lt;p&gt;This means you could very well get away with inserting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RefineType&lt;/code&gt;
instructions in successor blocks of conditionals and get the into-SSI “for
free”.&lt;/p&gt;

&lt;!--
  * Why not &quot;just&quot; use union-find?
--&gt;

&lt;h2 id=&quot;after-which-conditionals-do-we-refine&quot;&gt;After which conditionals do we refine?&lt;/h2&gt;

&lt;p&gt;That’s up to you. There’s a trade-off between compile-time and run-time,
especially in JITs. Inserting more instructions and rewriting more times may
slow down your compiler. It’s a cheap lunch, not a free one.&lt;/p&gt;

&lt;h2 id=&quot;how-does-this-compare-to-the-complicated-looking-papers&quot;&gt;How does this compare to the complicated looking papers?&lt;/h2&gt;

&lt;p&gt;I don’t know. I don’t have a good grasp of how this “partial SSI” compares to
the “full SSI”. I don’t plan on implementing full SSI in the near future.&lt;/p&gt;

&lt;p&gt;I will note that this partial SSI approach doesn’t do two things:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;It doesn’t split variables with a new sigma node, and it generally inserts
the refine node within the target block rather than above the branch&lt;/li&gt;
  &lt;li&gt;(For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canonicalize&lt;/code&gt; only) It doesn’t insert new phi nodes; it just leaves
both IR nodes available and, instead of re-merging, drops them&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I can’t tell what impact this has.&lt;/p&gt;

&lt;h2 id=&quot;in-other-compilers&quot;&gt;In other compilers&lt;/h2&gt;

&lt;p&gt;Like Simple, &lt;a href=&quot;https://truffleruby.dev/&quot;&gt;TruffleRuby&lt;/a&gt; is built on a Sea of Nodes
IR (Graal). Chris Seaton has an &lt;a href=&quot;https://chrisseaton.com/truffleruby/stamping-out-overflow-checks/&quot;&gt;excellent blog
post&lt;/a&gt; about
TruffleRuby’s use of “stamp nodes” (“Pi nodes”&lt;sup id=&quot;fnref:pi-nodes&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:pi-nodes&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;). The
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;replaceAtUsagesAndDelete&lt;/code&gt; function does a lot of heavy lifting, I think
because Graal tracks uses.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/facebookincubator/cinderx&quot;&gt;Cinder&lt;/a&gt; mostly inserts
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RefineType&lt;/code&gt; instructions in the HIR builder, before into-SSA, and then lets
the SSA construction take care of things. That’s where I learned this trick,
actually. Here is &lt;a href=&quot;https://github.com/facebookincubator/cinderx/blob/38c0a17d71df4fddf39ca10d9fdf48d7bcafc1d9/cinderx/Jit/hir/builder.cpp#L4745&quot;&gt;one
example&lt;/a&gt;
of refining the type of the matched operand when building IR for pattern
matching.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/luau-lang/luau&quot;&gt;Luau&lt;/a&gt; is working on something like this,
but for their type checker. Chatting with someone on their team is actually
part of the reason I got motivated to write this post.&lt;/p&gt;

&lt;!-- LLVM PredicateInfo --&gt;
&lt;!-- HHVM AssertType --&gt;

&lt;p&gt;Android ART looks like it has
&lt;a href=&quot;https://github.com/LineageOS/android_art/blob/8ce603e0c68899bdfbc9cd4c50dcc65bbf777982/compiler/optimizing/nodes.h#L7759&quot;&gt;HBoundType&lt;/a&gt;
and inserts them &lt;a href=&quot;https://github.com/LineageOS/android_art/blob/8ce603e0c68899bdfbc9cd4c50dcc65bbf777982/compiler/optimizing/reference_type_propagation.cc#L194&quot;&gt;in reference type
propagation&lt;/a&gt;.
This handles class checks, null checks, and instanceof checks.&lt;/p&gt;

&lt;!-- Dart
https://github.com/dart-lang/sdk/blob/1c947dd88acd6e4b282e445b56e62eb631e87bd8/runtime/vm/compiler/backend/flow_graph.cc#L2012
RedefinitionInstr
--&gt;

&lt;h2 id=&quot;aside-logic-for-eg-heapobject-upgrade&quot;&gt;Aside: logic for e.g. HeapObject upgrade&lt;/h2&gt;

&lt;p&gt;Last, I want to talk a little bit about some interesting reasoning you can do
when you have two implementations of something that you can switch between. For
example, JIT (+ interpreter), or aliasing and non-aliasing cases in C code, or
the weirdo NULL-UB reasoning LLVM can do to C code, things like that.&lt;/p&gt;

&lt;p&gt;In ZJIT, we currently insert &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RefineType&lt;/code&gt;s opportunistically in “easy” cases
when building our HIR from the interpreter bytecode.&lt;/p&gt;

&lt;p&gt;For example, if in the bytecode there is a branch that compares some value &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt;
with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt;, it will have two outgoing control-flow edges: one block where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt;
is definitely &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt;, and one block where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; is definitely &lt;em&gt;not&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt;. In each
of these control-flow edges, we can insert corresponding type refinement hints.
That’s pretty standard. But we can also do weirder stuff.&lt;/p&gt;

&lt;p&gt;CRuby has a notion of heap objects vs immediate objects. Many (most?) objects
are heap objects. However, integer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;, for example is not allocated on the
heap but instead represented by a &lt;a href=&quot;/blog/small-objects/&quot;&gt;tagged bit pattern&lt;/a&gt;
that pretends to be an address: the whole value is encoded in the pointer
itself.&lt;/p&gt;

&lt;p&gt;We encode this knowledge in the HIR’s type system: “heapness” and
“immediateness” each get a bit in the &lt;a href=&quot;/blog/lattice-bitset/&quot;&gt;type lattice&lt;/a&gt;. We
use this in the optimizer to reason about &lt;a href=&quot;/blog/compiler-effects/&quot;&gt;effects&lt;/a&gt;,
among other things.&lt;/p&gt;

&lt;p&gt;We can’t know a lot of the time what type a thing is, so we pessimistically
type most objects flowing through bytecode as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BasicObject&lt;/code&gt;. This type
encapsulates the entire world of possible values that could go on the stack or
in a local variable.&lt;/p&gt;

&lt;p&gt;On most &lt;em&gt;heap&lt;/em&gt; objects, with only a few exceptions, you can write instance
variables (fields, attributes, whatever you want to call them). You can &lt;em&gt;never&lt;/em&gt;
write an instance variable to an immediate. This means that if we observe the
following pattern in the bytecode:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;x: BasicObject = ...
setinstancevariable x, :@abc, 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then after building and emitting HIR for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setinstancevariable&lt;/code&gt; opcode, we
can upgrade the type of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; from a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BasicObject&lt;/code&gt; to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HeapBasicObject&lt;/code&gt;. We can
do this because if it &lt;em&gt;weren’t&lt;/em&gt; a heap-allocated object, we would have left the
compiled code and entered the interpreter.&lt;/p&gt;

&lt;p&gt;This is another SSI-type thing you can do in your compiler.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Uhh I guess the conclusion is that you don’t have to do full SSI and partial
SSI is available and not too scary? Does your compiler do this? Reader, please
write in.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:et-al&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;…and &lt;a href=&quot;/assets/img/singer-ssi.pdf&quot;&gt;optimized in 2002&lt;/a&gt; (PDF),
&lt;a href=&quot;/assets/img/ssi-revisited.pdf&quot;&gt;revisited in 2009&lt;/a&gt; (PDF), &lt;a href=&quot;/assets/img/efficient-ssi.pdf&quot;&gt;implemented in
LLVM in 2010&lt;/a&gt; (PDF), &lt;a href=&quot;/assets/img/ssi-abstract-compilation.pdf&quot;&gt;investigated in
2017 for abstract compilation&lt;/a&gt;
(PDF), and probably more. The 2009 paper by Boissinot, Brisk, Darte, and
Rastello even shows that both Ananian and Singer’s papers have bugs, while
perhaps unintentionally also making an &lt;em&gt;excellent&lt;/em&gt; pun about the literature
being “sparse”. &lt;a href=&quot;#fnref:et-al&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:llvm-partial-ssi&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;This blog post is different than the what the &lt;a href=&quot;/assets/img/efficient-ssi.pdf&quot;&gt;LLVM
paper&lt;/a&gt; (PDF) calls partial SSI. Partial for
different reasons. Maybe it’s not even single information anymore. &lt;a href=&quot;#fnref:llvm-partial-ssi&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:pi-nodes&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Today I learned that this terminology comes from the &lt;a href=&quot;/assets/img/abcd.pdf&quot;&gt;ABCD
paper&lt;/a&gt; (PDF). &lt;a href=&quot;#fnref:pi-nodes&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
            <pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate>
            <niceDate>May 12, 2026</niceDate>
            <link>https://bernsteinbear.com/blog/partial-ssi/?utm_source=rss</link>
            <guid isPermaLink="true">https://bernsteinbear.com/blog/partial-ssi/</guid>
        </item>
        
        <item>
            <title>Value numbering</title>
            <description>&lt;p&gt;Welcome back to compiler land. Today we’re going to talk about &lt;em&gt;value
numbering&lt;/em&gt;, which is like SSA, but more.&lt;/p&gt;

&lt;p&gt;Static single assignment (SSA) gives names to values: every expression has a
name, and each name corresponds to exactly one expression. It transforms
programs like this:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;where the variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; is assigned more than once in the program text, into
programs like this:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;v0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;v1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;v2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;where each assignment to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; has been replaced with an assignment to a new
fresh name.&lt;/p&gt;

&lt;p&gt;It’s great because it makes clear the differences between the two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x + 1&lt;/code&gt;
expressions. Though they textually look similar, they compute different values.
The first computes 1 and the second computes 2. In this example, it is not
possible to substitute in a variable and re-use the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x + 1&lt;/code&gt;, because
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt;s are different.&lt;/p&gt;

&lt;p&gt;But what if we see two “textually” identical instructions in SSA? That sounds
much more promising than non-SSA because the transformation into SSA form has
removed (much of) the statefulness of it all. When can we re-use the result?&lt;/p&gt;

&lt;p&gt;Identifying instructions that are known at compile-time to always produce the
same value at run-time is called &lt;em&gt;value numbering&lt;/em&gt;. &lt;!-- This is also called common
subexpression elimination (CSE), though for some reason the two mean slightly
different things to different groups of people. --&gt;&lt;/p&gt;

&lt;h2 id=&quot;eliminating-common-subexpressions&quot;&gt;Eliminating common subexpressions&lt;/h2&gt;

&lt;p&gt;To understand value numbering, let’s extend the above IR snippet with two more
instructions, v3 and v4.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;v0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;v1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;v2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;v3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# new
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;do_something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# new
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this new snippet, v3 looks the same as v1: adding v0 and 1. Assuming our
addition operation is some ideal mathematical addition, we can absolutely
re-use v1; no need to compute the addition again. We can rewrite the IR to
something like:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;v0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;v1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;v2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;v3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;v4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;do_something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is kind of similar to the destructive union-find representation that
JavaScriptCore and a couple other compilers use, where the optimizer doesn’t
eagerly re-write all uses but instead leaves a little breadcrumb
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Identity&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assign&lt;/code&gt; instruction&lt;sup id=&quot;fnref:cinder&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:cinder&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;We could then run our copy propagation pass (“union-find cleanup”?) and get:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;v0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;v1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;v2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;v4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;do_something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Great. But how does this happen? How does an optimizer identify reusable
instruction candidates that are “textually identical”? Generally, there is &lt;a href=&quot;https://pointersgonewild.com/2011/10/07/optimizing-global-value-numbering/&quot;&gt;no
actual text in the
IR&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One popular solution is to compute a hash of each instruction. Then any
instructions with the same hash (that also compare equal, in case of
collisions) are considered equivalent. This is called &lt;em&gt;hash-consing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;When trying to figure all this out, I read through a couple of different
implementations. I particularly like the &lt;a href=&quot;https://maxine-vm.readthedocs.io/en/stable/&quot;&gt;Maxine VM&lt;/a&gt; implementation.
For example, here is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;valueNumber&lt;/code&gt; (hashing) and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;valueEqual&lt;/code&gt;
functions for most binary operations, slightly modified for clarity:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Instruction&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// The base class for binary operations&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Op2&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Instruction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Each binary operation has an opcode and two opearands&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opcode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// (IMUL, IADD, ...)&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;valueNumber&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// There are other fields but only opcode, and operands get hashed.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Always set at least one bit in case the hash wraps to zero.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x20000000&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opcode&lt;/span&gt;
           &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;identityHashCode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
           &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;identityHashCode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;valueEqual&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Instruction&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Op2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Op2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Op2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opcode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;opcode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The rest of the value numbering implementation assumes that if a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;valueNumber&lt;/code&gt;
function returns 0, it does not wish to be considered for value
numbering. Why might an instruction opt-out of value numbering?&lt;/p&gt;

&lt;h2 id=&quot;pure-vs-impure&quot;&gt;Pure vs impure&lt;/h2&gt;

&lt;p&gt;An instruction might opt out of value numbering if it is not “pure”.&lt;/p&gt;

&lt;p&gt;Some instructions are not pure. Purity is in the eye of the beholder, but in
general it means that an instruction does not interact with the state of the
outside world, except for trivial computation on its operands. (What does it
mean to de-duplicate/cache/reuse &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;printf&lt;/code&gt;?)&lt;/p&gt;

&lt;p&gt;A load from an array object is also not a pure operation&lt;sup id=&quot;fnref:heap-ssa&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:heap-ssa&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. The load operation
implicitly relies on the state of the memory. Also, even if the array was
known-constant, in some runtime
systems, the load might raise an exception. Changing the source location where
an exception is raised is generally frowned upon. Languages such as Java often
have requirements about where exceptions are raised codified in their
specifications.&lt;/p&gt;

&lt;p&gt;We’ll work only on pure operations for now, but we’ll come back to this later.
We do often want to optimize impure operations as well!&lt;/p&gt;

&lt;p&gt;We’ll start off with the simplest form of value numbering, which operates only
on linear sequences of instructions, like basic blocks or traces.&lt;/p&gt;

&lt;h2 id=&quot;local-value-numbering&quot;&gt;Local value numbering&lt;/h2&gt;

&lt;p&gt;Let’s build a small implementation of local value numbering (LVN). We’ll start with
straight-line code—no branches or anything tricky.&lt;/p&gt;

&lt;p&gt;Most compiler optimizations on control-flow graphs (CFGs) iterate over the
instructions “top to bottom”&lt;sup id=&quot;fnref:order&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:order&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; and it seems like we can do the same thing
here too.&lt;/p&gt;

&lt;p&gt;From what we’ve seen so far optimizing our made-up IR snippet, we can do
something like this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;initialize a map from instruction numbers to instruction pointers&lt;/li&gt;
  &lt;li&gt;for each instruction &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt; wants to participate in value numbering
        &lt;ul&gt;
          &lt;li&gt;if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt;’s value number is already in the map, replace all pointers to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt;
in the rest of the program with the corresponding value from the map&lt;/li&gt;
          &lt;li&gt;otherwise, add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt; to the map&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The find-and-replace, remember, is not a literal find-and-replace, but instead
something like:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;instr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opcode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Assign&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;instr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;replacement&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;instr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;make_equal_to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replacement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(if you have been following along with the &lt;a href=&quot;https://pypy.org/categories/toy-optimizer.html&quot;&gt;toy optimizer&lt;/a&gt; series)&lt;/p&gt;

&lt;p&gt;This several-line function (as long as you already have a hash map and a
union-find available to you) is enough to build local value numbering! And real
compilers are built this way, too.&lt;/p&gt;

&lt;p&gt;If you don’t believe me, take a look at this slightly edited snippet from
&lt;a href=&quot;https://maxine-vm.readthedocs.io/en/stable/&quot;&gt;Maxine’s&lt;/a&gt; value numbering implementation. It has all of the components
we just talked about: iterating over instructions, map lookup, and some
substitution.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Local value numbering&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;BlockBegin&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...;&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;ValueMap&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentMap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ValueMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;InstructionSubstituter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subst&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;InstructionSubstituter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// visit all instructions of this block&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Instruction&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// attempt value numbering (uses valueNumber() and valueEqual())&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// return a previous instruction if it exists in the map, or insert the&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// current instruction into the map and return it&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Instruction&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;findInsert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// remember the replacement in the union-find&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;subst&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setSubst&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This alone will get you pretty far. Code generators of all shapes tend to leave
messy repeated computations all over their generated code and this will make
short work of them.&lt;/p&gt;

&lt;p&gt;Sometimes, though, your computations are spread across control flow—over
multiple basic blocks. What do you do then?&lt;/p&gt;

&lt;!--
## Equivalence classes
--&gt;

&lt;h2 id=&quot;global-value-numbering&quot;&gt;Global value numbering&lt;/h2&gt;

&lt;p&gt;Computing value numbers for an entire function is called &lt;em&gt;global value
numbering&lt;/em&gt; (GVN) and it requires dealing with control flow (if, loops, etc). I
don’t just mean that for an entire function, we run local value numbering
block-by-block. Global value numbering implies that expressions can be
de-duplicated and shared across blocks.&lt;/p&gt;

&lt;p&gt;Let’s tackle control flow case by case.&lt;/p&gt;

&lt;p&gt;First is the simple case from above: one block. In this case, we can go top to
bottom with our value numbering and do alright.&lt;/p&gt;

&lt;figure&gt;
  &lt;object class=&quot;svg&quot; type=&quot;image/svg+xml&quot; data=&quot;/assets/img/gvn-one-block.svg&quot;&gt;&lt;/object&gt;
&lt;/figure&gt;

&lt;p&gt;The second case is also reasonable to handle: one block flowing into another. In this
case, we can still go top to bottom. We just have to find a way to iterate over
the blocks.&lt;/p&gt;

&lt;p&gt;If we’re not going to share value maps between blocks, the order doesn’t
matter. But since the point of global value numbering is to share values, we
have to iterate them in topological order (reverse post order (RPO)). This
ensures that predecessors get visited before successors. If you have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bb0 -&amp;gt;
bb1&lt;/code&gt;, we have to visit first &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bb0&lt;/code&gt; and then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bb1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Because of how SSA works and how CFGs work, the second block can “look up” into
the first block and use the values from it. To get global value numbering
working, we have to copy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bb0&lt;/code&gt;’s value map before we start processing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bb1&lt;/code&gt; so
we can re-use the instructions.&lt;/p&gt;

&lt;figure&gt;
  &lt;object class=&quot;svg&quot; type=&quot;image/svg+xml&quot; data=&quot;/assets/img/gvn-two-blocks.svg&quot;&gt;&lt;/object&gt;
&lt;/figure&gt;

&lt;p&gt;Maybe something like:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;value_map&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reverse_post_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;local_value_numbering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then the expressions can accrue across blocks. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bb1&lt;/code&gt; can re-use the
already-computed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Add v0, 1&lt;/code&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bb0&lt;/code&gt; because it is still in the map.&lt;/p&gt;

&lt;p&gt;…but this breaks as soon as you have control-flow splits. Consider the
following shape graph:&lt;/p&gt;

&lt;!--
digraph G {
  node [shape=square];
  A -&gt; B;
  A -&gt; C;
}
--&gt;
&lt;figure&gt;
  &lt;object class=&quot;svg&quot; type=&quot;image/svg+xml&quot; data=&quot;/assets/img/gvn-split.svg&quot;&gt;&lt;/object&gt;
&lt;/figure&gt;

&lt;p&gt;We’re going to iterate over that graph in one of two orders: A B C or A C B. In
either case, we’re going to be adding all this stuff into the value map from
one block (say, B) that is not actually available to its sibling block (say,
C).&lt;/p&gt;

&lt;p&gt;When I say “not available”, I mean “would not have been computed before”. This
is because we execute either A then B or A then C. There’s no world in which we
execute B then C.&lt;/p&gt;

&lt;p&gt;But alright, look at a third case where there is such a world: a control-flow
join. In this diagram, we have two predecessor blocks B and C each flowing into
D. In this diagram, B &lt;em&gt;always&lt;/em&gt; flows into D and also C &lt;em&gt;always&lt;/em&gt; flows into D.
So the iterator order is fine, right?&lt;/p&gt;

&lt;!--
digraph G {
  node [shape=square];
  A -&gt; B;
  A -&gt; C;
  B -&gt; D;
  C -&gt; D;
}
--&gt;
&lt;figure&gt;
  &lt;object class=&quot;svg&quot; type=&quot;image/svg+xml&quot; data=&quot;/assets/img/gvn-join.svg&quot;&gt;&lt;/object&gt;
&lt;/figure&gt;

&lt;p&gt;Well, still no. We have the same sibling problem as before. B and C still can’t
share value maps.&lt;/p&gt;

&lt;p&gt;We also have a weird question when we enter D: where did we come from? If we
came from B, we can re-use expressions from B. If we came from C, we can re-use
expressions from C. But we cannot in general know which predecessor block we
came from.&lt;/p&gt;

&lt;p&gt;The only block we know &lt;em&gt;for sure&lt;/em&gt; that we executed before D is A. This means we
can re-use A’s value map in D because we can guarantee that all execution paths
that enter D have previously gone through A.&lt;/p&gt;

&lt;p&gt;This relationship is called a &lt;em&gt;dominator&lt;/em&gt; relationship and this is the key to
one style of global value numbering that we’re going to talk about in this
post. A block can always use the value map from any other block that dominates
it. For completeness’ sake, in the diamond diagram, A dominates each of B and
C, too.&lt;/p&gt;

&lt;p&gt;We can compute dominators a couple of ways&lt;sup id=&quot;fnref:compute-doms&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:compute-doms&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;, but that’s a little
bit out of scope for this blog post. If we assume that we have dominator
information available in our CFG, we can use that for global value numbering.
And that’s just what—you guessed it—Maxine VM does.&lt;/p&gt;

&lt;p&gt;It iterates over all blocks in reverse post-order, doing local value numbering,
threading through value maps from dominator blocks. In this case, their method
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dominator&lt;/code&gt; gets the &lt;em&gt;immediate dominator&lt;/em&gt;: the “closest” dominator block of
all the blocks that dominate the current one.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GlobalValueNumberer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;BlockBegin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ValueMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valueMaps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;InstructionSubstituter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subst&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;ValueMap&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GlobalValueNumberer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;IR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;subst&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;InstructionSubstituter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// reverse post-order&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;BlockBegin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;linearScanOrder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;valueMaps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;BlockBegin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ValueMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blocks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;optimize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blocks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;subst&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;optimize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;BlockBegin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blocks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numBlocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blocks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;BlockBegin&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startBlock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blocks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// initial value map, with nesting 0&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;valueMaps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;startBlock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ValueMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numBlocks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// iterate through all the blocks&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;BlockBegin&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blocks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;BlockBegin&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dominator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;dominator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// create new value map with increased nesting&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;currentMap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ValueMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valueMaps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dominator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// &amp;lt;&amp;lt; INSERT LOCAL VALUE NUMBERING HERE &amp;gt;&amp;gt;&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// remember value map for successors&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;valueMaps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And that’s it! That’s the core of Maxine’s &lt;a href=&quot;https://github.com/beehive-lab/Maxine-VM/blob/e213a842f78983e2ba112ae46de8c64317bc206e/com.sun.c1x/src/com/sun/c1x/opt/GlobalValueNumberer.java&quot;&gt;GVN implementation&lt;/a&gt;. I
love how short it is. For not very much code, you can remove a lot of duplicate
pure SSA instructions.&lt;/p&gt;

&lt;p&gt;This does still work with loops, but with some caveats. From p7 of &lt;a href=&quot;/assets/img/briggs-gvn.pdf&quot;&gt;Briggs GVN&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The φ-functions require special treatment. Before the compiler can analyze
the φ-functions in a block, it must previously have assigned value numbers to
all of the inputs. This is not possible in all cases; specifically, any
φ-function input whose value flows along a back edge (with respect to the
dominator tree) cannot have a value number. If any of the parameters of a
φ-function have not been assigned a value number, then the compiler cannot
analyze the φ-function, and it must assign a unique, new value number to the
result.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So: we can treat phis as opaque (not numberable) and it works as-is. Otherwise
we have to do a little more work.&lt;/p&gt;

&lt;p&gt;It also talks about eliminating useless phis, which is optional, but would
the strengthen global value numbering pass: it makes more information
transparent.&lt;/p&gt;

&lt;p&gt;But what if we want to handle impure instructions?&lt;/p&gt;

&lt;h2 id=&quot;state-management-and-invalidation&quot;&gt;State management and invalidation&lt;/h2&gt;

&lt;p&gt;Languages such as Java allow for reading fields from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;this&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self&lt;/code&gt; object within
methods as if the field were a variable name. This makes code like the
following common:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CPU&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;exec_adc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result_int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regA&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetched_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flagCARRY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result_int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result_int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result_int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetched_data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;regA&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each of these reference to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;regA&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetched_data&lt;/code&gt; is an implicit reference
to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;this.regA&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;this.fetched_data&lt;/code&gt;, which is semantically a field load off
an object. You can see it in &lt;a href=&quot;https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:&apos;1&apos;,fontScale:14,fontUsePx:&apos;0&apos;,j:1,lang:java,selection:(endColumn:19,endLineNumber:14,positionColumn:19,positionLineNumber:14,selectionStartColumn:19,selectionStartLineNumber:14,startColumn:19,startLineNumber:14),source:&apos;class+CPU+%7B%0A++++private+void+exec_adc()+%7B%0A++++++++int+result_int+%3D+regA+%2B+fetched_data+%2B+flagCARRY%3B%0A++++++++byte+result+%3D+(byte)+result_int%3B%0A++++++++//+...%0A++++++++int+a+%3D+result_int+%5E+regA%3B%0A++++++++int+b+%3D+result_int+%5E+fetched_data%3B%0A++++++++//+...%0A++++++++regA+%3D+result%3B%0A++++%7D%0A%0A++++int+regA%3B%0A++++int+fetched_data%3B%0A++++int+flagCARRY%3B%0A%7D%0A&apos;),l:&apos;5&apos;,n:&apos;0&apos;,o:&apos;Java+source+%231&apos;,t:&apos;0&apos;)),k:50,l:&apos;4&apos;,n:&apos;0&apos;,o:&apos;&apos;,s:0,t:&apos;0&apos;),(g:!((h:compiler,i:(compiler:java2501,filters:(b:&apos;0&apos;,binary:&apos;1&apos;,binaryObject:&apos;1&apos;,commentOnly:&apos;0&apos;,debugCalls:&apos;1&apos;,demangle:&apos;0&apos;,directives:&apos;0&apos;,execute:&apos;1&apos;,intel:&apos;0&apos;,libraryCode:&apos;0&apos;,trim:&apos;1&apos;,verboseDemangling:&apos;0&apos;),flagsViewOpen:&apos;1&apos;,fontScale:14,fontUsePx:&apos;0&apos;,j:1,lang:java,libs:!(),options:&apos;&apos;,overrides:!(),selection:(endColumn:19,endLineNumber:40,positionColumn:1,positionLineNumber:1,selectionStartColumn:19,selectionStartLineNumber:40,startColumn:1,startLineNumber:1),source:1),l:&apos;5&apos;,n:&apos;0&apos;,o:&apos;+jdk+25.0.1+(Editor+%231)&apos;,t:&apos;0&apos;)),k:50,l:&apos;4&apos;,n:&apos;0&apos;,o:&apos;&apos;,s:0,t:&apos;0&apos;)),l:&apos;2&apos;,n:&apos;0&apos;,o:&apos;&apos;,t:&apos;0&apos;)),version:4&quot;&gt;the bytecode&lt;/a&gt; (thanks, Matt Godbolt):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class CPU {
  int regA;

  int fetched_data;

  int flagCARRY;

  CPU();
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object.&quot;&amp;lt;init&amp;gt;&quot;:()V
         4: return


  private void exec_adc();
         0: aload_0
         1: getfield      #7                  // Field regA:I
         4: aload_0
         // ...
        20: getfield      #7                  // Field regA:I
        23: ixor
        24: istore_3
        25: iload_1
        26: aload_0
        27: getfield      #13                 // Field fetched_data:I
        30: ixor
        31: istore        4
        33: aload_0
        34: iload_2
        35: putfield      #7                  // Field regA:I
        38: return
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When straightforwardly building an SSA IR from the JVM bytecode for this
method, you will end up with a bunch of IR that looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;v0 = LoadField self, :regA
v1 = LoadField self, :fetched_data
v2 = LoadField self, :flagCARRY
v3 = IntAdd v0, v1
v4 = IntAdd v3, v2
// ...
v7 = LoadField self, :regA
v8 = IntXor v4, v7
v9 = LoadField self, :fetched_data
v10 = IntXor v4, v9
// ...
StoreField self, :regA, ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pretty much the same as the bytecode. Even though no code in the middle could
modify the field &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;regA&lt;/code&gt; (which would require a re-load), we still have a
duplicate load. Bummer.&lt;/p&gt;

&lt;p&gt;I don’t want to re-hash this too much but it’s possible to fold &lt;a href=&quot;/blog/toy-load-store/&quot;&gt;Load and store
forwarding&lt;/a&gt; into your GVN implementation by either:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;doing load-store forwarding as part of local value numbering and clearing
memory information from the value map at the end of each block, or&lt;/li&gt;
  &lt;li&gt;keeping track of effects across blocks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See, there’s nothing fundamentally stopping you from tracking the state of your
heap at compile-time across blocks. You just have to do a little more
bookkeeping. In our dominator-based GVN implementation, for example, you can:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;track heap write effects for each block&lt;/li&gt;
  &lt;li&gt;at the start of each block B, union all of the “kill” sets for every block
back to its immediate dominator&lt;/li&gt;
  &lt;li&gt;finally, remove the stuff that got killed from the dominator’s value map&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Not so bad.&lt;/p&gt;

&lt;p&gt;Maxine doesn’t do global memory tracking, but they do a limited form of
load-store forwarding while building their HIR from bytecode: see
&lt;a href=&quot;https://github.com/beehive-lab/Maxine-VM/blob/e213a842f78983e2ba112ae46de8c64317bc206e/com.sun.c1x/src/com/sun/c1x/graph/GraphBuilder.java#L871&quot;&gt;GraphBuilder&lt;/a&gt; which uses the &lt;a href=&quot;https://github.com/beehive-lab/Maxine-VM/blob/e213a842f78983e2ba112ae46de8c64317bc206e/com.sun.c1x/src/com/sun/c1x/graph/MemoryMap.java&quot;&gt;MemoryMap&lt;/a&gt; to help track this stuff. At least
they would not have the same duplicate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LoadField&lt;/code&gt; instructions in the example
above!&lt;/p&gt;

&lt;!--
```ruby
module Psych
  module Visitors
    class YAMLTree &lt; Psych::Visitors::Visitor
      def initialize emitter, ss, options
        # ...
        @line_width = options[:line_width]
        if @line_width &amp;&amp; @line_width &lt; 0
          if @line_width == -1
            # Treat -1 as unlimited line-width, same as libyaml does.
            @line_width = nil
          else
            fail(...)
          end
        end
        # ...
    end
  end
end
```
--&gt;

&lt;p&gt;We’ve now looked at one kind of value numbering and one implementation of it.
What else is out there?&lt;/p&gt;

&lt;h2 id=&quot;out-in-the-world&quot;&gt;Out in the world&lt;/h2&gt;

&lt;p&gt;Apparently, you can get better results by having a unified hash table (p9 of
&lt;a href=&quot;/assets/img/briggs-gvn.pdf&quot;&gt;Briggs GVN&lt;/a&gt;) of expressions, not limiting the
value map to dominator-available expressions. Not 100% on how this works yet.
&lt;!-- TODO What do you do in the second pass for available expressions? --&gt;
They note:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Using a unified hash-table has one important algorithmic consequence.
Replacements cannot be performed on-line because the table no longer reflects
availability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which is the first time that it occurred to me that hash-based value numbering
with dominators was an approximation of available expression analysis.&lt;/p&gt;

&lt;p&gt;There’s also a totally different kind of value numbering called value
partitioning (p12 of &lt;a href=&quot;/assets/img/briggs-gvn.pdf&quot;&gt;Briggs GVN&lt;/a&gt;). See also a nice
blog post about this by Allen Wang from the &lt;a href=&quot;https://www.cs.cornell.edu/courses/cs6120/2025sp/blog/global-value-numbering/&quot;&gt;Cornell compiler
course&lt;/a&gt;.
I think this mostly replaces the hashing bit, and you still need some other
thing for the available expressions bit.&lt;/p&gt;

&lt;p&gt;Ben Titzer and Seth Goldstein have some good &lt;a href=&quot;https://www.cs.cmu.edu/~411/slides/s25-24-gvn-inlining.pdf&quot;&gt;slides from
CMU&lt;/a&gt; where they
talk about the worklist dataflow approach. Apparently this is slower but gets
you more available expressions than just looking to dominator blocks. I wonder
how much it differs from dominator+unified hash table.&lt;/p&gt;

&lt;p&gt;While Maxine uses hash table cloning to copy value maps from dominator blocks,
there are also compilers such as Cranelift that use
&lt;a href=&quot;https://github.com/bytecodealliance/wasmtime/blob/main/cranelift/codegen/src/scoped_hash_map.rs&quot;&gt;scoped hash maps&lt;/a&gt;
to track this information more efficiently. (Though &lt;a href=&quot;https://github.com/bytecodealliance/wasmtime/issues/4371#issuecomment-1255956651&quot;&gt;Amanieu
notes&lt;/a&gt; that you may
not need a scoped hash map and instead can tag values in your value map with the
block they came from, ignoring non-dominating values with a quick check. The
dominance check makes sense but I haven’t internalized how this affects the set
of available expressions yet.)&lt;/p&gt;

&lt;p&gt;You may be wondering if this kind of algorithm even helps at all in a dynamic
language JIT context. Surely everything is too dynamic, right? Actually, no!
The JIT hopes to eliminate a lot of method calls and dynamic behaviors,
replacing them with guards, assumptions, and simpler operations. These strength
reductions often leave behind a lot of repeated instructions. Just the other
day, Kokubun filed a &lt;a href=&quot;https://github.com/ruby/ruby/pull/16654&quot;&gt;value-numbering-like
PR&lt;/a&gt; to clean up some of the waste.&lt;/p&gt;

&lt;p&gt;ART has a recent &lt;a href=&quot;https://android-developers.googleblog.com/2025/12/18-faster-compiles-0-compromises.html&quot;&gt;blog
post&lt;/a&gt;
about speeding up GVN.&lt;/p&gt;

&lt;h3 id=&quot;implementations&quot;&gt;Implementations&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/beehive-lab/Maxine-VM/blob/e213a842f78983e2ba112ae46de8c64317bc206e/com.sun.c1x/src/com/sun/c1x/opt/GlobalValueNumberer.java&quot;&gt;Maxine&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://android.googlesource.com/platform/art/+/refs/heads/main/compiler/optimizing/gvn.cc&quot;&gt;ART&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/tekknolagi/v8/blob/f030838700a83cde6992cb8ebcb3facc6a8fc1f1/src/crankshaft/hydrogen-gvn.cc&quot;&gt;V8 Hydrogen&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/facebook/hhvm/blob/1a885fae7421c759d70a8ed85aab1defcf5cc68f/hphp/runtime/vm/jit/gvn.cpp&quot;&gt;HHVM&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/openjdk/jdk/blob/f21e47db805b56d5bf183d7a2cfba076f380612a/src/hotspot/share/c1/c1_ValueMap.cpp#L517&quot;&gt;HotSpot C1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of iterating over the reverse post-order of the blocks in the CFG, some
implementations build a top-down dominator tree (nodes pointing to blocks
dominated by that node) and then walk that tree depth-first. This lets them
&lt;a href=&quot;https://github.com/bytecodealliance/waffle/blob/c0ce14354e1b86f53fcca4d90e3c80507f23df7f/src/passes/dom_pass.rs&quot;&gt;push and pop&lt;/a&gt; from a &lt;a href=&quot;https://github.com/bytecodealliance/wasmtime/blob/d5657d4038b4aedda619ca8d9bd21c84ea2e2878/cranelift/codegen/src/scoped_hash_map.rs&quot;&gt;scoped hash table&lt;/a&gt;, or use
generations, or something else smart like a fast &lt;a href=&quot;https://gist.github.com/Amanieu/c7d43dfdeebc094494cfdfcf55bc9c7a&quot;&gt;pre-order/post-order
dominance check&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This iteration order is a different iteration order but the resulting algorithm
is equivalent (except that the RPO does a lot more allocating). This is because
the shared property is that if block A dominates block B, A will be visited
before B.&lt;/p&gt;

&lt;!--
https://gist.github.com/glaebhoerl/d62d2b19365ae0d7c29102d0a5a6ab03
--&gt;

&lt;h2 id=&quot;wrapping-up-bits-and-bobbles&quot;&gt;Wrapping up; bits and bobbles&lt;/h2&gt;

&lt;p&gt;Go forth and give your values more numbers.&lt;/p&gt;

&lt;p&gt;There’s been an ongoing discussion with Phil Zucker on SSI, GVN, acyclic
egraphs, and scoped union-find. TODO summarize&lt;/p&gt;

&lt;h3 id=&quot;acyclic-e-graphs&quot;&gt;Acyclic e-graphs&lt;/h3&gt;

&lt;p&gt;Commutativity; canonicalization&lt;/p&gt;

&lt;p&gt;Seeding alternative representations into the GVN&lt;/p&gt;

&lt;p&gt;Aegraphs and union-find during GVN &lt;a href=&quot;https://cfallin.org/blog/2026/04/09/aegraph/&quot;&gt;https://cfallin.org/blog/2026/04/09/aegraph/&lt;/a&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canonicalize&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/bytecodealliance/rfcs/blob/main/accepted/cranelift-egraph.md&quot;&gt;https://github.com/bytecodealliance/rfcs/blob/main/accepted/cranelift-egraph.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/bytecodealliance/wasmtime/issues/9049&quot;&gt;https://github.com/bytecodealliance/wasmtime/issues/9049&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/bytecodealliance/wasmtime/issues/4371&quot;&gt;https://github.com/bytecodealliance/wasmtime/issues/4371&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;partial-redundancy-elimination&quot;&gt;Partial redundancy elimination&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://cs.wheaton.edu/~tvandrun/writings/cc04.pdf&quot;&gt;https://cs.wheaton.edu/~tvandrun/writings/cc04.pdf&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:cinder&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Writing this post is roughly the time when I realized that the whole
time I was wondering why Cinder did not use union-find for rewriting, it
actually did! Optimizing instruction &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X = A + 0&lt;/code&gt; by replacing with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X =
Assign A&lt;/code&gt; followed by copy propagation is equivalent to union-find. &lt;a href=&quot;#fnref:cinder&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:heap-ssa&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;In some forms of SSA, like heap-array SSA or sea of nodes, it’s
possible to more easily de-duplicate loads because the memory
representation has been folded into (modeled in) the IR. &lt;a href=&quot;#fnref:heap-ssa&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:order&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;The order is a little more complicated than that: &lt;a href=&quot;https://stackoverflow.com/questions/36131500/what-is-the-reverse-postorder&quot;&gt;reverse
post-order&lt;/a&gt;
(RPO). And there’s a paper called “A Simple Algorithm for Global Data Flow
Analysis Problems” that I don’t yet have a PDF for that claims that RPO is
optimal for solving dataflow problems. &lt;a href=&quot;#fnref:order&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:compute-doms&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;There’s the iterative dataflow way (described in the &lt;a href=&quot;/assets/img/dominators-engineered.pdf&quot;&gt;Cooper
paper&lt;/a&gt; (PDF)),
&lt;a href=&quot;/assets/img/dominators-lengauer-tarjan.pdf&quot;&gt;Lengauer-Tarjan&lt;/a&gt; (PDF), the
&lt;a href=&quot;/assets/img/dominators-engineered.pdf&quot;&gt;Engineered Algorithm&lt;/a&gt; (PDF),
&lt;a href=&quot;/assets/img/dominators-practice.pdf&quot;&gt;hybrid/Semi-NCA approach&lt;/a&gt; (PDF), … &lt;a href=&quot;#fnref:compute-doms&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
            <pubDate>Sat, 04 Apr 2026 00:00:00 +0000</pubDate>
            <niceDate>April 4, 2026</niceDate>
            <link>https://bernsteinbear.com/blog/value-numbering/?utm_source=rss</link>
            <guid isPermaLink="true">https://bernsteinbear.com/blog/value-numbering/</guid>
        </item>
        
        <item>
            <title>Using Perfetto in ZJIT</title>
            <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href=&quot;https://railsatscale.com/2026-03-27-using-perfetto-in-zjit/&quot;&gt;Rails At Scale&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Look! A trace of slow events in a benchmark! Hover over the image to see it get bigger.&lt;/p&gt;

&lt;style&gt;
img {
    max-width: 100%;
}
img:hover {
  transform: scale(2);
  transition: transform 0.1s ease-in;
}
img:not(:hover) {
  transition: transform 0.1s ease-out;
}
&lt;/style&gt;

&lt;figure&gt;

  &lt;p&gt;&lt;img src=&quot;/assets/img/perfetto-demo.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

  &lt;figcaption&gt;
A sneak preview of what the trace looks like.
&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Now read on to see what the slow events are and how we got this pretty picture.&lt;/p&gt;

&lt;h2 id=&quot;the-rules&quot;&gt;The rules&lt;/h2&gt;

&lt;p&gt;The first rule of just-in-time compilers is: you stay in JIT code. The second
rule of JIT is: you STAY in JIT code!&lt;/p&gt;

&lt;p&gt;When control leaves the compiled code to run in the interpreter—what the ZJIT
team calls either a “side-exit” or a “deopt”, depending on who you talk
to—things slow down. In a well-tuned system, this should happen pretty
rarely. Right now, because we’re still bringing up the compiler and runtime
system, it happens more than we would like.&lt;/p&gt;

&lt;p&gt;We’re reducing the number of exits over time.&lt;/p&gt;

&lt;h2 id=&quot;lies-damned-lies-and-statistics&quot;&gt;Lies, damned lies, and statistics&lt;/h2&gt;

&lt;p&gt;We can track our side-exit reduction progress with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--zjit-stats&lt;/code&gt;, which,
on process exit, prints out a tidy summary of the counters for all of the bad
stuff we track. It’s got side-exits. It’s got calls to C code. It’s got calls
to slow-path runtime helpers. It’s got everything.&lt;/p&gt;

&lt;p&gt;Here is a chopped-up sample of stats output for the Lobsters benchmark,
which is a large Rails app:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ WARMUP_ITRS=0 MIN_BENCH_ITRS=20 MIN_BENCH_TIME=0 ruby --zjit-stats benchmarks/lobsters/benchmark.rb
...
***ZJIT: Printing ZJIT statistics on exit***
...
Top-20 side exit reasons (100.0% of total 12,549,876):
                   guard_type_failure: 6,020,734 (48.0%)
                  guard_shape_failure: 5,556,147 (44.3%)
  block_param_proxy_not_iseq_or_ifunc:   445,358 ( 3.5%)
                   unhandled_hir_insn:   215,168 ( 1.7%)
                        compile_error:   181,474 ( 1.4%)
...
compiled_iseq_count:                               5,581
failed_iseq_count:                                     2
compile_time:                                    1,443ms
...
guard_type_count:                            133,425,094
guard_type_exit_ratio:                              4.5%
guard_shape_count:                            49,386,694
guard_shape_exit_ratio:                            11.3%
...
code_region_bytes:                            31,571,968
side_exit_size_ratio:                              33.1%
zjit_alloc_bytes:                             19,329,659
total_mem_bytes:                              50,901,627
...
ratio_in_zjit:                                     82.8%
$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(I’ve cut out significant chunks of the stats output and replaced them with
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;...&lt;/code&gt; because it’s overwhelming the first time you see it.)&lt;/p&gt;

&lt;p&gt;The first thing you might note is that the thing I just described as terrible
for performance is happening &lt;em&gt;over twelve million times&lt;/em&gt;. The second thing you
might notice is that despite this, we’re staying in JIT code seemingly a high
percentage of the time. Or are we? Is 80% high? Is a 4.5% class guard miss
ratio high? What about 11% for shapes? It’s hard to say.&lt;/p&gt;

&lt;p&gt;The counters are great because they’re &lt;em&gt;quick&lt;/em&gt; and they’re reasonably stable
proxies for performance. There’s no substitute for painstaking measurements on
a quiet machine but if the counter for Bad Slow Thing goes down (and others do
not go up), we’re probably doing a good job.&lt;/p&gt;

&lt;p&gt;But they’re not great for building intuition. For intuition, we want more
tangible feeling numbers. We want to see things.&lt;/p&gt;

&lt;h2 id=&quot;building-intuition&quot;&gt;Building intuition&lt;/h2&gt;

&lt;p&gt;The third thing is that you might ask yourself “self, where are these exits
coming from?” Unfortunately, counters cannot tell you that. For that, we
want stack traces. This lets us know where in the guest (Ruby) code triggers
an exit.&lt;/p&gt;

&lt;p&gt;Ideally also we would want some notion of time: we would want to know not just
where these events happen but also when. Are the exits happening early, at
application boot? At warmup? Even during what should be steady state
application time? Hard to say.&lt;/p&gt;

&lt;p&gt;So we need more tools. Thankfully, &lt;a href=&quot;https://perfetto.dev/&quot;&gt;Perfetto&lt;/a&gt; exists.
Perfetto is a system for visualizing and analyzing traces and profiles that your
application generates. It has both a web UI and a command-line UI.&lt;/p&gt;

&lt;p&gt;We can emit traces for Perfetto and visualize them there.&lt;/p&gt;

&lt;h2 id=&quot;a-look-at-perfetto&quot;&gt;A look at Perfetto&lt;/h2&gt;

&lt;p&gt;Take a look at this &lt;a href=&quot;https://ui.perfetto.dev/#!/?url=https://bernsteinbear.com/assets/misc/perfetto-36885.fxt&quot;&gt;sample ZJIT Perfetto
trace&lt;/a&gt;
generated by running Ruby with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--zjit-trace-exits&lt;/code&gt;&lt;sup id=&quot;fnref:sampled&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:sampled&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. What do you see?&lt;/p&gt;

&lt;p&gt;I see a couple arrows on the left. Arrows indicate “instant” point-in-time
events. Then I see a mess of purple to the right of that until the end of the
trace.&lt;/p&gt;

&lt;p&gt;Hover over an arrow. Find out that each arrow is a side-exit. Scream silently.&lt;/p&gt;

&lt;p&gt;But it’s a friendly arrow. It tells you what the side-exit reason is. If you
click it, it even tells you the stack trace in the pop-up panel on the bottom.
If we click a couple of them, maybe we can learn more.&lt;/p&gt;

&lt;p&gt;We can also zoom by mousing over the track, holding Ctrl, and scrolling. That
will get us look closer. But there are so many…&lt;/p&gt;

&lt;p&gt;Fortunately, Perfetto also provides a SQL interface to the traces. We can write
a query to aggregate all of the side exit events from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;slice&lt;/code&gt; table and
line them up with the topmost method from the backtrace arguments in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;args&lt;/code&gt;
table:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;display_value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slice&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg_set_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg_set_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;0&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;display_value&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This pulls up a query box at the bottom showing us that there are a couple big
hotspots:&lt;/p&gt;

&lt;figure&gt;

  &lt;p&gt;&lt;img src=&quot;/assets/img/perfetto-method-query.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

  &lt;figcaption&gt;
Query results showing in columns left to right: reason for side-exit, method
that exited, and count. The top three are above 1k but it quickly falls off
after that.
&lt;/figcaption&gt;

&lt;/figure&gt;

&lt;p&gt;It even has a helpful option to export the results Markdown table so I can
paste (an edited version) into this blog post:&lt;/p&gt;

&lt;div style=&quot;overflow-x: auto; font-size: 0.75em; margin-left: max(-10em, calc(-50vw + 50%)); margin-right: max(-10em, calc(-50vw + 50%));&quot;&gt;

  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;reason&lt;/th&gt;
        &lt;th&gt;method&lt;/th&gt;
        &lt;th&gt;count&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;GuardShape(ShapeId(2475))&lt;/td&gt;
        &lt;td&gt;ActiveModel::AttributeRegistration::ClassMethods#attribute_types&lt;/td&gt;
        &lt;td&gt;5119&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;GuardShape(ShapeId(2099268))&lt;/td&gt;
        &lt;td&gt;ActiveRecord::ConnectionAdapters::AbstractAdapter#extended_type_map_key&lt;/td&gt;
        &lt;td&gt;2295&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;GuardType(FalseClass)&lt;/td&gt;
        &lt;td&gt;ActiveModel::Type::Value#cast&lt;/td&gt;
        &lt;td&gt;1025&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;GuardShape(ShapeId(2099698))&lt;/td&gt;
        &lt;td&gt;ActiveRecord::Associations#association_instance_get&lt;/td&gt;
        &lt;td&gt;904&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;BlockParamProxyNotIseqOrIfunc&lt;/td&gt;
        &lt;td&gt;ActiveRecord::AttributeMethods::Read#_read_attribute&lt;/td&gt;
        &lt;td&gt;902&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;GuardShape(ShapeId(526450))&lt;/td&gt;
        &lt;td&gt;Rack::Request::Env#get_header&lt;/td&gt;
        &lt;td&gt;636&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;GuardType(Class[class_exact*:Class@VALUE(0x128c60100)])&lt;/td&gt;
        &lt;td&gt;ActiveRecord::Base._reflections&lt;/td&gt;
        &lt;td&gt;622&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;GuardType(ObjectSubclass[class_exact:Story])&lt;/td&gt;
        &lt;td&gt;ActiveRecord::Associations#association&lt;/td&gt;
        &lt;td&gt;565&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;GuardShape(ShapeId(2098982))&lt;/td&gt;
        &lt;td&gt;ActiveRecord::Reflection::AssociationReflection#polymorphic?&lt;/td&gt;
        &lt;td&gt;510&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;GuardType(StringSubclass[class_exact:ActiveSupport::SafeBuffer])&lt;/td&gt;
        &lt;td&gt;ActionView::OutputBuffer#&amp;lt;&amp;lt;&lt;/td&gt;
        &lt;td&gt;500&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;GuardShape(ShapeId(2475))&lt;/td&gt;
        &lt;td&gt;ActiveRecord::AttributeMethods::PrimaryKey::ClassMethods#primary_key&lt;/td&gt;
        &lt;td&gt;492&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;GuardType(ObjectSubclass[class_exact:ActiveModel::Type::String])&lt;/td&gt;
        &lt;td&gt;ActiveModel::Type::Value#deserialize&lt;/td&gt;
        &lt;td&gt;442&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;GuardShape(ShapeId(2098982))&lt;/td&gt;
        &lt;td&gt;ActiveRecord::Reflection::AssociationReflection#deprecated?&lt;/td&gt;
        &lt;td&gt;376&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;GuardType(ObjectSubclass[class_exact:Bundler::Dependency])&lt;/td&gt;
        &lt;td&gt;Gem::Dependency#matches_spec?&lt;/td&gt;
        &lt;td&gt;355&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;UnhandledHIRInvokeBuiltin&lt;/td&gt;
        &lt;td&gt;Time#initialize&lt;/td&gt;
        &lt;td&gt;346&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;

&lt;/div&gt;

&lt;p&gt;Looks like we should figure out why we’re having shape misses so much and that will
clear up a lot of exits. (Hint: it’s because once we make our first guess about
what we think the object shape will be, we don’t re-assess… &lt;strong&gt;yet&lt;/strong&gt;.)&lt;/p&gt;

&lt;p&gt;This has been a taste of Perfetto. There’s probably a lot more to explore.
Please join the &lt;a href=&quot;https://zjit.zulipchat.com&quot;&gt;ZJIT Zulip&lt;/a&gt; and let us know if you have any cool
tracing or exploring tricks.&lt;/p&gt;

&lt;p&gt;Now I’ll explain how you too can use Perfetto from your system. Adding support
to ZJIT was pretty straightforward.&lt;/p&gt;

&lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;/h2&gt;

&lt;p&gt;The first thing is that you’ll need some way to get trace data out of your
system. We write to a file with a well-known location
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp/perfetto-PID.fxt&lt;/code&gt;), but you could do any number of things. Perhaps you
can stream events over a socket to another process, or to a server that
aggregates them, or store them internally and expose a webserver that serves
them over the internet, or… anything, really.&lt;/p&gt;

&lt;p&gt;Once you have that, you need a couple lines of code to emit the data. Perfetto
accepts a number of formats. For example, in his &lt;a href=&quot;https://thume.ca/2023/12/02/tracing-methods/&quot;&gt;excellent blog post&lt;/a&gt;,
Tristan Hume opens with such a simple snippet of code for logging Chromium
Trace JSON-formatted events (lightly modified by me):&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;event_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;trace.json&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# ... emit some events here ...
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Log a single event
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;{&quot;name&quot;: &quot;%s&quot;, &quot;ts&quot;: %d, &quot;dur&quot;: %d, &quot;cat&quot;: &quot;hi&quot;, &quot;ph&quot;: &quot;X&quot;, &quot;pid&quot;: 1, &quot;tid&quot;: 1, &quot;args&quot;: {}},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# ... emit some events here ...
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ... at process exit, close the file ...
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# this closing ] isn&apos;t actually required
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This snippet is great. It shows, end-to-end, writing a stream of one event. It
is a &lt;em&gt;complete&lt;/em&gt; (X) event, as opposed to either:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;two discrete timestamped &lt;em&gt;begin&lt;/em&gt; (B) and &lt;em&gt;end&lt;/em&gt; (E) events that book-end
something, or&lt;/li&gt;
  &lt;li&gt;an &lt;em&gt;instant&lt;/em&gt; (i) event that has no duration, or&lt;/li&gt;
  &lt;li&gt;a couple other event types in the &lt;a href=&quot;https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview&quot;&gt;Chromium Trace Event Format doc&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It was enough to get me started. Since it’s JSON, and we have a lot of side
exits, the trace quickly ballooned to 8GB large for a several second benchmark.
Not great. Now, part of this is our fault—we should side exit less—and part
of it is just the verbosity of JSON.&lt;/p&gt;

&lt;p&gt;Thankfully, Perfetto ingests more compact binary formats, such as the &lt;a href=&quot;https://fuchsia.dev/fuchsia-src/reference/tracing/trace-format&quot;&gt;Fuchsia
trace format&lt;/a&gt;.
In addition to being more compact, FXT even supports string interning. After
modifying the tracer to emit FXT, we ended with closer to 100MB for the same
benchmark.&lt;/p&gt;

&lt;p&gt;We can reduce further by &lt;em&gt;sampling&lt;/em&gt;—not writing every exit to the trace, but
instead every &lt;em&gt;K&lt;/em&gt; exits (for some (probably prime) K). This is why we provide
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--zjit-trace-exits-sample-rate=K&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href=&quot;https://github.com/ruby/ruby/blob/eb8051185122d4b7bc9c6a6df694a85f34ced681/zjit/src/stats.rs#L988&quot;&gt;trace writer&lt;/a&gt; implementation from the point this article
was written.&lt;/p&gt;

&lt;h2 id=&quot;tracing-more-things&quot;&gt;Tracing more things&lt;/h2&gt;

&lt;p&gt;We could trace:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;When methods get compiled&lt;/li&gt;
  &lt;li&gt;How big the generated code is&lt;/li&gt;
  &lt;li&gt;How long each compile phase takes&lt;/li&gt;
  &lt;li&gt;When (and where) invalidation events happen&lt;/li&gt;
  &lt;li&gt;When (and where) allocations happen from JITed code&lt;/li&gt;
  &lt;li&gt;Garbage collection events&lt;/li&gt;
  &lt;li&gt;and more!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Visualizations are awesome. Get your data in the right format so you can ask
the right questions easily. Thanks for Perfetto!&lt;/p&gt;

&lt;p&gt;Also, looks like visualizations are now available in Perfetto canary. Time to
go make some fun histograms…&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:sampled&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;This is also sampled/strobed, so not every exit is in there. This
is just 1/K of them for some K that I don’t remember. &lt;a href=&quot;#fnref:sampled&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
            <pubDate>Fri, 27 Mar 2026 00:00:00 +0000</pubDate>
            <niceDate>March 27, 2026</niceDate>
            <link>https://bernsteinbear.com/blog/zjit-perfetto/?utm_source=rss</link>
            <guid isPermaLink="true">https://bernsteinbear.com/blog/zjit-perfetto/</guid>
        </item>
        
        <item>
            <title>A fuzzer for the Toy Optimizer</title>
            <description>&lt;p&gt;&lt;em&gt;Another entry in the &lt;a href=&quot;https://pypy.org/categories/toy-optimizer.html&quot;&gt;Toy Optimizer series&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It’s hard to get compiler optimizers right. Even if you build up a painstaking test
suite by hand, you will likely miss corner cases, especially corner cases at
the interactions of multiple components or multiple optimization passes.&lt;/p&gt;

&lt;p&gt;I wanted to see if I could write a fuzzer to catch some of these bugs
automatically. But a fuzzer alone isn’t much use without some correctness
oracle—in this case, we want a more interesting bug than accidentally
crashing the optimizer. We want to see if the optimizer introduces a
correctness bug in the program.&lt;/p&gt;

&lt;p&gt;So I set off in the most straightforward way possible, inspired by my
hazy memories of a former &lt;a href=&quot;https://pypy.org/posts/2024/03/fixing-bug-incremental-gc.html&quot;&gt;CF blog post&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;generating-programs&quot;&gt;Generating programs&lt;/h2&gt;

&lt;p&gt;Generating random programs isn’t so bad. We have program generation APIs and we
can dynamically pick which ones we want to call. I wrote a small loop that
generates &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt;s from and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;store&lt;/code&gt;s to the arguments at random offsets and with
random values, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;escape&lt;/code&gt;s to random instructions with outputs. The idea
with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;escape&lt;/code&gt; is to keep track of the values as if there was some other
function relying on them.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;generate_program&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bb&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getarg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;num_ops&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ops_with_values&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_ops&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;load&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;store&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;escape&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;a_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ops_with_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;load&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ops_with_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;store&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;bb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;escape&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;bb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;escape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NotImplementedError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unknown operation &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This generates random programs. Here is an example stringified random program:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;var0 = getarg(0)
var1 = getarg(1)
var2 = getarg(2)
var3 = load(var2, 0)
var4 = load(var0, 1)
var5 = load(var1, 1)
var6 = escape(var0)
var7 = store(var0, 2, 3)
var8 = store(var2, 0, 7)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;No idea what would generate something like this, but oh well.&lt;/p&gt;

&lt;h2 id=&quot;verifying-programs&quot;&gt;Verifying programs&lt;/h2&gt;

&lt;p&gt;Then we want to come up with our invariants. I picked the invariant that, under
the same preconditions, the heap will look the same after running an optimized
program as it would under an un-optimized program&lt;sup id=&quot;fnref:equivalence&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:equivalence&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. So we can delete
instructions, but if we don’t have a load-bearing store, store the wrong
information, or cache stale loads, we will probably catch that.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;verify_program&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;before_no_alias&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interpret_program&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;before_alias&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interpret_program&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;optimized&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optimize_load_store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;after_no_alias&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interpret_program&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optimized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;after_alias&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interpret_program&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optimized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;before_no_alias&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;after_no_alias&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;before_alias&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;after_alias&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I have a very silly verifier that tests two cases: one where the arguments do
not alias and one where they are all the same object. Generating partial
aliases would be a good extension here.&lt;/p&gt;

&lt;p&gt;Last, we have the interpreter.&lt;/p&gt;

&lt;h2 id=&quot;running-programs&quot;&gt;Running programs&lt;/h2&gt;

&lt;p&gt;The interpreter is responsible for keeping track of the heap (as indexed by
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(object, offset)&lt;/code&gt; pairs) as well as the results of the various instructions.&lt;/p&gt;

&lt;p&gt;We keep track of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;escape&lt;/code&gt;d values so we can see results of some
instructions even if they do not get written back to the heap. Maybe we should
be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;escape&lt;/code&gt;ing all instructions with output instead of only random ones. Who
knows.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;interpret_program&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;heap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ssa&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;escaped&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;getarg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ssa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;store&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ssa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;heap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;load&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ssa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;unknown&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ssa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;escape&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Constant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;escaped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;escaped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ssa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NotImplementedError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unknown operation &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;heap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;escaped&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;escaped&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heap&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then we return the heap so that the verifier can check.&lt;/p&gt;

&lt;h2 id=&quot;the-harness&quot;&gt;The harness&lt;/h2&gt;

&lt;p&gt;Then we run a bunch of random tests through the verifier!&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_random_programs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Remove random.seed if using in CI... instead print the seed out so you
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# can reproduce crashes if you find them
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;num_programs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100000&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_programs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;program&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generate_program&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;verify_program&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;program&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The number of programs is configurable. Or you could make this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;while True&lt;/code&gt;.
But due to how simple the optimizer is, we will find all the possible bugs
pretty quickly.&lt;/p&gt;

&lt;p&gt;I initially started writing this post because I thought I had found a bug, but
it turns out that I had, with CF’s help, in 2022, walked through every possible
case in the “buggy” situation, and the optimizer handles those cases correctly.
That explains why the verifier didn’t find that bug!&lt;/p&gt;

&lt;h2 id=&quot;testing-the-verifier&quot;&gt;Testing the verifier&lt;/h2&gt;

&lt;p&gt;So does it work? If you run it, it’ll hang for a bit and then report no issues.
That’s helpful, in a sense… it’s revealing that it is unable to find a
certain class of bug in the optimizer.&lt;/p&gt;

&lt;p&gt;Let’s comment out the main load-bearing pillar of correctness in the
optimizer—removing aliasing writes—and see what happens.&lt;/p&gt;

&lt;p&gt;We get a crash nearly instantly:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ uv run --with pytest pytest loadstore.py -k random
...
=========================================== FAILURES ============================================
_____________________________________ test_random_programs ______________________________________

    def test_random_programs():
        random.seed(0)
        num_programs = 100000
        for i in range(num_programs):
            program = generate_program()
&amp;gt;           verify_program(program)

loadstore.py:617:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

bb = [Operation(getarg, [Constant(0)], None, None), Operation(getarg, [Constant(1)], None, None), Operation(getarg, [Consta...], None, None)], None, None), Operation(load, [Operation(getarg, [Constant(0)], None, None), Constant(0)], None, None)]

    def verify_program(bb):
        before_no_alias = interpret_program(bb, [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;])
        a = &quot;a&quot;
        before_alias = interpret_program(bb, [a, a, a])
        optimized = optimize_load_store(bb)
        after_no_alias = interpret_program(optimized, [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;])
        after_alias = interpret_program(optimized, [a, a, a])
        assert before_no_alias == after_no_alias
&amp;gt;       assert before_alias == after_alias
E       AssertionError: assert {(&apos;a&apos;, 0): 4,...&apos;, 3): 1, ...} == {(&apos;a&apos;, 0): 9,...&apos;, 3): 1, ...}
E
E         Omitting 4 identical items, use -vv to show
E         Differing items:
E         {(&apos;a&apos;, 0): 4} != {(&apos;a&apos;, 0): 9}
E         Use -v to get more diff

loadstore.py:610: AssertionError
==================================== short test summary info ====================================
FAILED loadstore.py::test_random_programs - AssertionError: assert {(&apos;a&apos;, 0): 4,...&apos;, 3): 1, ...} == {(&apos;a&apos;, 0): 9,...&apos;, 3): 1, ...}
=============================== 1 failed, 15 deselected in 0.04s ================================
$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We should probably use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bb_to_str(bb)&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bb_to_str(optimized)&lt;/code&gt; to print out
the un-optimized and optimized traces in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assert&lt;/code&gt; failure messages. But we
get a nice diff of the heap automatically, which is neat. And it points to an
aliasing problem!&lt;/p&gt;

&lt;h2 id=&quot;full-code&quot;&gt;Full code&lt;/h2&gt;

&lt;p&gt;See the &lt;a href=&quot;https://github.com/tekknolagi/tekknolagi.github.com/blob/fbccf9696e98721ca77c8d5ec5f828a11492b04c/loadstore.py&quot;&gt;full code&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;extensions&quot;&gt;Extensions&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Synthesize (different) types for non-aliasing objects and add them in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;info&lt;/code&gt;
  &lt;!--
    * CF notes that we could maybe do this by, instead of adding `.info`, have a
      `checktype` guard instruction that the optimizer can use to learn types and
      change aliasing from inside the trace
  --&gt;&lt;/li&gt;
  &lt;li&gt;Shrink/reduce failing examples down for easier debugging&lt;/li&gt;
  &lt;li&gt;Use Hypothesis for property-based testing, which CF notes also gives you
shrinking&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html&quot;&gt;Use Z3 to encode&lt;/a&gt; the generated programs instead of randomly interpreting them&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;thanks&quot;&gt;Thanks&lt;/h2&gt;

&lt;p&gt;Thank you to &lt;a href=&quot;https://cfbolz.de/&quot;&gt;CF Bolz-Tereick&lt;/a&gt; for feedback on this post!&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:equivalence&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;CF notes that this notion of equivalence works for this
optimizer but not for one that does allocation removal (escape analysis).
If we removed allocations and writes to them, we would be changing the heap
results and our verifier would appear to fail. This means we have to, if we
are to delete allocations, pick a more subtle definition of equivalence.&lt;/p&gt;

      &lt;p&gt;Perhaps something that looks like escape analysis in the verifier’s
interpreter? &lt;a href=&quot;#fnref:equivalence&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
            <pubDate>Wed, 25 Feb 2026 00:00:00 +0000</pubDate>
            <niceDate>February 25, 2026</niceDate>
            <link>https://bernsteinbear.com/blog/toy-fuzzer/?utm_source=rss</link>
            <guid isPermaLink="true">https://bernsteinbear.com/blog/toy-fuzzer/</guid>
        </item>
        
    </channel>
</rss>
