<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>PyPy (Posts about z3)</title><link>https://www.pypy.org/</link><description></description><atom:link href="https://www.pypy.org/categories/z3.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2026 &lt;a href="mailto:pypy-dev@pypy.org"&gt;The PyPy Team&lt;/a&gt; </copyright><lastBuildDate>Wed, 30 Sep 2026 20:34:17 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>A DSL for Peephole Transformation Rules of Integer Operations in the PyPy JIT</title><link>https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html</link><dc:creator>CF Bolz-Tereick</dc:creator><description>&lt;p&gt;As is probably apparent from the sequence of blog posts about the topic in the
last year, I have been thinking about and working on integer optimizations in the JIT
compiler a lot. This work was mainly motivated by &lt;a class="reference external" href="https://docs.pydrofoil.org/en/latest/"&gt;Pydrofoil&lt;/a&gt;, where integer
operations matter a lot more than for your typical Python program.&lt;/p&gt;
&lt;p&gt;In this post I'll describe my most recent change, which is a new small domain
specific language that I implemented to specify peephole optimizations on
integer operations in the JIT.
It uses pattern matching to specify how (sequences of) integer operations
should be simplified and optimized. The rules are then compiled to
RPython code that then becomes part of the JIT's optimization passes.&lt;/p&gt;
&lt;p&gt;To make it less likely to introduce incorrect optimizations into the JIT, the
rules are automatically proven correct with Z3 as part of the build process (for
a more hands-on intro to how that works you can look at the &lt;a class="reference external" href="https://pypy.org/posts/2024/08/toy-knownbits.html#proving-correctness-of-the-transfer-functions-with-z3"&gt;knownbits&lt;/a&gt; post).
In this blog post I want to motivate why I introduced the DSL and give an
introduction to how it works.&lt;/p&gt;
&lt;section id="motivation"&gt;
&lt;h2&gt;Motivation&lt;/h2&gt;
&lt;p&gt;This summer, after I wrote my &lt;a class="reference external" href="https://www.pypy.org/posts/2024/07/mining-jit-traces-missing-optimizations-z3.html"&gt;scripts to mine JIT traces for missed optimization&lt;/a&gt;
opportunities, I started implementing a few of the integer peephole rewrite that
the script identified. Unfortunately, doing so led to the problem that the way
we express these rewrites up to now is very imperative and verbose. Here's a
snippet of RPython code that shows some rewrites for integer multiplication
(look at the comments to see what the different parts actually do). You don't
need to understand the code in detail, but basically it's in very imperative
style and there's quite a lot of boilerplate.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-1" name="rest_code_b353225dbe224cb9970c1509a39f9096-1" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;optimize_INT_MUL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-2" name="rest_code_b353225dbe224cb9970c1509a39f9096-2" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-2"&gt;&lt;/a&gt;    &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_box_replacement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-3" name="rest_code_b353225dbe224cb9970c1509a39f9096-3" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-3"&gt;&lt;/a&gt;    &lt;span class="n"&gt;b0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getintbound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-4" name="rest_code_b353225dbe224cb9970c1509a39f9096-4" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-4"&gt;&lt;/a&gt;    &lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_box_replacement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-5" name="rest_code_b353225dbe224cb9970c1509a39f9096-5" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-5"&gt;&lt;/a&gt;    &lt;span class="n"&gt;b1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getintbound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-6" name="rest_code_b353225dbe224cb9970c1509a39f9096-6" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-6"&gt;&lt;/a&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-7" name="rest_code_b353225dbe224cb9970c1509a39f9096-7" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-7"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;b0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;known_eq_const&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-8" name="rest_code_b353225dbe224cb9970c1509a39f9096-8" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-8"&gt;&lt;/a&gt;        &lt;span class="c1"&gt;# 1 * x == x&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-9" name="rest_code_b353225dbe224cb9970c1509a39f9096-9" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-9"&gt;&lt;/a&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_equal_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-10" name="rest_code_b353225dbe224cb9970c1509a39f9096-10" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-10"&gt;&lt;/a&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;known_eq_const&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-11" name="rest_code_b353225dbe224cb9970c1509a39f9096-11" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-11"&gt;&lt;/a&gt;        &lt;span class="c1"&gt;# x * 1 == x&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-12" name="rest_code_b353225dbe224cb9970c1509a39f9096-12" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-12"&gt;&lt;/a&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_equal_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-13" name="rest_code_b353225dbe224cb9970c1509a39f9096-13" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-13"&gt;&lt;/a&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;b0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;known_eq_const&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;known_eq_const&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-14" name="rest_code_b353225dbe224cb9970c1509a39f9096-14" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-14"&gt;&lt;/a&gt;        &lt;span class="c1"&gt;# 0 * x == x * 0 == 0&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-15" name="rest_code_b353225dbe224cb9970c1509a39f9096-15" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-15"&gt;&lt;/a&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_constant_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-16" name="rest_code_b353225dbe224cb9970c1509a39f9096-16" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-16"&gt;&lt;/a&gt;    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-17" name="rest_code_b353225dbe224cb9970c1509a39f9096-17" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-17"&gt;&lt;/a&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;)]:&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-18" name="rest_code_b353225dbe224cb9970c1509a39f9096-18" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-18"&gt;&lt;/a&gt;            &lt;span class="n"&gt;lh_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getintbound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-19" name="rest_code_b353225dbe224cb9970c1509a39f9096-19" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-19"&gt;&lt;/a&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lh_info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-20" name="rest_code_b353225dbe224cb9970c1509a39f9096-20" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-20"&gt;&lt;/a&gt;                &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lh_info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_constant_int&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-21" name="rest_code_b353225dbe224cb9970c1509a39f9096-21" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-21"&gt;&lt;/a&gt;                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-22" name="rest_code_b353225dbe224cb9970c1509a39f9096-22" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-22"&gt;&lt;/a&gt;                    &lt;span class="c1"&gt;# x * (2 ** c) == x &amp;lt;&amp;lt; c&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-23" name="rest_code_b353225dbe224cb9970c1509a39f9096-23" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-23"&gt;&lt;/a&gt;                    &lt;span class="n"&gt;new_rhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConstInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;highest_bit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lh_info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_constant_int&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-24" name="rest_code_b353225dbe224cb9970c1509a39f9096-24" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-24"&gt;&lt;/a&gt;                    &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace_op_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INT_LSHIFT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_rhs&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-25" name="rest_code_b353225dbe224cb9970c1509a39f9096-25" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-25"&gt;&lt;/a&gt;                    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_extra_operation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-26" name="rest_code_b353225dbe224cb9970c1509a39f9096-26" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-26"&gt;&lt;/a&gt;                    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-27" name="rest_code_b353225dbe224cb9970c1509a39f9096-27" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-27"&gt;&lt;/a&gt;                &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-28" name="rest_code_b353225dbe224cb9970c1509a39f9096-28" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-28"&gt;&lt;/a&gt;                    &lt;span class="c1"&gt;# x * -1 == -x&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-29" name="rest_code_b353225dbe224cb9970c1509a39f9096-29" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-29"&gt;&lt;/a&gt;                    &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace_op_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INT_NEG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-30" name="rest_code_b353225dbe224cb9970c1509a39f9096-30" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-30"&gt;&lt;/a&gt;                    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_extra_operation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-31" name="rest_code_b353225dbe224cb9970c1509a39f9096-31" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-31"&gt;&lt;/a&gt;                    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-32" name="rest_code_b353225dbe224cb9970c1509a39f9096-32" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-32"&gt;&lt;/a&gt;            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-33" name="rest_code_b353225dbe224cb9970c1509a39f9096-33" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-33"&gt;&lt;/a&gt;                &lt;span class="c1"&gt;# x * (1 &amp;lt;&amp;lt; y) == x &amp;lt;&amp;lt; y&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-34" name="rest_code_b353225dbe224cb9970c1509a39f9096-34" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-34"&gt;&lt;/a&gt;                &lt;span class="n"&gt;shiftop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_operation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_box_replacement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;rop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INT_LSHIFT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-35" name="rest_code_b353225dbe224cb9970c1509a39f9096-35" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-35"&gt;&lt;/a&gt;                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;shiftop&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-36" name="rest_code_b353225dbe224cb9970c1509a39f9096-36" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-36"&gt;&lt;/a&gt;                    &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-37" name="rest_code_b353225dbe224cb9970c1509a39f9096-37" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-37"&gt;&lt;/a&gt;                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;shiftop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;shiftop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getint&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-38" name="rest_code_b353225dbe224cb9970c1509a39f9096-38" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-38"&gt;&lt;/a&gt;                    &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-39" name="rest_code_b353225dbe224cb9970c1509a39f9096-39" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-39"&gt;&lt;/a&gt;                &lt;span class="n"&gt;shiftvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_box_replacement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shiftop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-40" name="rest_code_b353225dbe224cb9970c1509a39f9096-40" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-40"&gt;&lt;/a&gt;                &lt;span class="n"&gt;shiftbound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getintbound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shiftvar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-41" name="rest_code_b353225dbe224cb9970c1509a39f9096-41" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-41"&gt;&lt;/a&gt;                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;shiftbound&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;known_nonnegative&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;shiftbound&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;known_lt_const&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LONG_BIT&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-42" name="rest_code_b353225dbe224cb9970c1509a39f9096-42" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-42"&gt;&lt;/a&gt;                    &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace_op_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-43" name="rest_code_b353225dbe224cb9970c1509a39f9096-43" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-43"&gt;&lt;/a&gt;                            &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INT_LSHIFT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shiftvar&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-44" name="rest_code_b353225dbe224cb9970c1509a39f9096-44" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-44"&gt;&lt;/a&gt;                    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_extra_operation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-45" name="rest_code_b353225dbe224cb9970c1509a39f9096-45" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-45"&gt;&lt;/a&gt;                    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;a id="rest_code_b353225dbe224cb9970c1509a39f9096-46" name="rest_code_b353225dbe224cb9970c1509a39f9096-46" href="https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html#rest_code_b353225dbe224cb9970c1509a39f9096-46"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Adding more rules to these functions is very tedious and gets super confusing
when the functions get bigger. In addition I am always worried about making
mistakes when writing this kind of code, and there is no feedback at all about
which of these rules are actually applied a lot in real programs.&lt;/p&gt;
&lt;p&gt;Therefore I decided to write a small domain specific language with the goal of
expressing these rules in a more declarative way. In the rest of the post I'll
describe the DSL (most of that description is adapted from the &lt;a class="reference external" href="https://rpython.readthedocs.io/en/latest/jit/ruleopt.html"&gt;documentation&lt;/a&gt;
about it that I wrote).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-peephole-rule-dsl"&gt;
&lt;h2&gt;The Peephole Rule DSL&lt;/h2&gt;
&lt;section id="simple-transformation-rules"&gt;
&lt;h3&gt;Simple transformation rules&lt;/h3&gt;
&lt;p&gt;The rules in the DSL specify how integer operation can be transformed into
cheaper other integer operations. A rule always consists of a name, a pattern,
and a target. Here's a simple rule:&lt;/p&gt;
&lt;pre class="literal-block"&gt;add_zero: int_add(x, 0)
    =&amp;gt; x&lt;/pre&gt;
&lt;p&gt;The name of the rule is &lt;code class="docutils literal"&gt;add_zero&lt;/code&gt;. It matches operations in the trace of the
form &lt;code class="docutils literal"&gt;int_add(x, 0)&lt;/code&gt;, where &lt;code class="docutils literal"&gt;x&lt;/code&gt; will match anything and &lt;code class="docutils literal"&gt;0&lt;/code&gt; will match only the
constant zero. After the &lt;code class="docutils literal"&gt;=&amp;gt;&lt;/code&gt; arrow is the target of the rewrite, i.e. what the
operation is rewritten to, in this case &lt;code class="docutils literal"&gt;x&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The rule language has a list of which of the operations are commutative, so &lt;code class="docutils literal"&gt;add_zero&lt;/code&gt;
will also optimize &lt;code class="docutils literal"&gt;int_add(0, x)&lt;/code&gt; to &lt;code class="docutils literal"&gt;x&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Variables in the pattern can repeat:&lt;/p&gt;
&lt;pre class="literal-block"&gt;sub_x_x: int_sub(x, x)
    =&amp;gt; 0&lt;/pre&gt;
&lt;p&gt;This rule matches against &lt;code class="docutils literal"&gt;int_sub&lt;/code&gt; operations where the two arguments are the
same (either the same box, or the same constant).&lt;/p&gt;
&lt;p&gt;Here's a rule with a more complicated pattern:&lt;/p&gt;
&lt;pre class="literal-block"&gt;sub_add: int_sub(int_add(x, y), y)
    =&amp;gt; x&lt;/pre&gt;
&lt;p&gt;This pattern matches &lt;code class="docutils literal"&gt;int_sub&lt;/code&gt; operations, where the first argument was
produced by an &lt;code class="docutils literal"&gt;int_add&lt;/code&gt; operation. In addition, one of the arguments of the
addition has to be the same as the second argument of the subtraction.&lt;/p&gt;
&lt;p&gt;The constants &lt;code class="docutils literal"&gt;MININT&lt;/code&gt;, &lt;code class="docutils literal"&gt;MAXINT&lt;/code&gt; and &lt;code class="docutils literal"&gt;LONG_BIT&lt;/code&gt; (which is either 32 or 64,
depending on which platform the JIT is built for) can be used in rules, they
behave like writing numbers but allow bit-width-independent formulations:&lt;/p&gt;
&lt;pre class="literal-block"&gt;is_true_and_minint: int_is_true(int_and(x, MININT))
    =&amp;gt; int_lt(x, 0)&lt;/pre&gt;
&lt;p&gt;It is also possible to have a pattern where some arguments needs to be a
constant, without specifying which constant. Those patterns look like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;sub_add_consts: int_sub(int_add(x, C1), C2) # incomplete
    # more goes here
    =&amp;gt; int_sub(x, C)&lt;/pre&gt;
&lt;p&gt;Variables in the pattern that start with a &lt;code class="docutils literal"&gt;C&lt;/code&gt; match against constants only.
However, in this current form the rule is incomplete, because the variable &lt;code class="docutils literal"&gt;C&lt;/code&gt;
that is being used in the target operation is not defined anywhere. We will see
how to compute it in the next section.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="computing-constants-and-other-intermediate-results"&gt;
&lt;h3&gt;Computing constants and other intermediate results&lt;/h3&gt;
&lt;p&gt;Sometimes it is necessary to compute intermediate results that are used in the
target operation. To do that, there can be extra assignments between the rule head
and the rule target.:&lt;/p&gt;
&lt;pre class="literal-block"&gt;sub_add_consts: int_sub(int_add(x, C1), C2) # incomplete
    C = C1 + C2
    =&amp;gt; int_sub(x, C)&lt;/pre&gt;
&lt;p&gt;The right hand side of such an assignment is a subset of Python syntax,
supporting arithmetic using &lt;code class="docutils literal"&gt;+&lt;/code&gt;, &lt;code class="docutils literal"&gt;-&lt;/code&gt;, &lt;code class="docutils literal"&gt;*&lt;/code&gt;, and certain helper functions.
However, the syntax allows you to be explicit about unsignedness for some
operations. E.g. &lt;code class="docutils literal"&gt;&amp;gt;&amp;gt;u&lt;/code&gt; exists for unsigned right shifts (and I plan to add
&lt;code class="docutils literal"&gt;&amp;gt;u&lt;/code&gt;, &lt;code class="docutils literal"&gt;&amp;gt;=u&lt;/code&gt;, &lt;code class="docutils literal"&gt;&amp;lt;u&lt;/code&gt;, &lt;code class="docutils literal"&gt;&amp;lt;=u&lt;/code&gt; for comparisons).&lt;/p&gt;
&lt;p&gt;Here's an example of a rule that uses &lt;code class="docutils literal"&gt;&amp;gt;&amp;gt;u&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;urshift_lshift_x_c_c: uint_rshift(int_lshift(x, C), C)
    mask = (-1 &amp;lt;&amp;lt; C) &amp;gt;&amp;gt;u C
    =&amp;gt; int_and(x, mask)&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="checks"&gt;
&lt;h3&gt;Checks&lt;/h3&gt;
&lt;p&gt;Some rewrites are only true under certain conditions. For example,
&lt;code class="docutils literal"&gt;int_eq(x, 1)&lt;/code&gt; can be rewritten to &lt;code class="docutils literal"&gt;x&lt;/code&gt;, if &lt;code class="docutils literal"&gt;x&lt;/code&gt; is known to store a boolean value. This can
be expressed with &lt;em&gt;checks&lt;/em&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;eq_one: int_eq(x, 1)
    check x.is_bool()
    =&amp;gt; x&lt;/pre&gt;
&lt;p&gt;A check is followed by a boolean expression. The variables from the pattern can
be used as &lt;code class="docutils literal"&gt;IntBound&lt;/code&gt; instances in checks (and also in assignments) to find out
what the abstract interpretation of the JIT knows about the value of a trace variable
(&lt;code class="docutils literal"&gt;IntBound&lt;/code&gt; is the name of the abstract domain that the JIT uses for integers,
despite the fact that it also stores &lt;a class="reference external" href="https://www.pypy.org/posts/2024/08/toy-knownbits.html"&gt;knownbits&lt;/a&gt; information nowadays).&lt;/p&gt;
&lt;p&gt;Here's another example:&lt;/p&gt;
&lt;pre class="literal-block"&gt;mul_lshift: int_mul(x, int_lshift(1, y))
    check y.known_ge_const(0) and y.known_le_const(LONG_BIT)
    =&amp;gt; int_lshift(x, y)&lt;/pre&gt;
&lt;p&gt;It expresses that &lt;code class="docutils literal"&gt;x * (1 &amp;lt;&amp;lt; y)&lt;/code&gt; can be rewritten to &lt;code class="docutils literal"&gt;x &amp;lt;&amp;lt; y&lt;/code&gt; but checks that
&lt;code class="docutils literal"&gt;y&lt;/code&gt; is known to be between &lt;code class="docutils literal"&gt;0&lt;/code&gt; and &lt;code class="docutils literal"&gt;LONG_BIT&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Checks and assignments can be repeated and combined with each other:&lt;/p&gt;
&lt;pre class="literal-block"&gt;mul_pow2_const: int_mul(x, C)
    check C &amp;gt; 0 and C &amp;amp; (C - 1) == 0
    shift = highest_bit(C)
    =&amp;gt; int_lshift(x, shift)&lt;/pre&gt;
&lt;p&gt;In addition to calling methods on &lt;code class="docutils literal"&gt;IntBound&lt;/code&gt; instances, it's also possible to
access their attributes, like in this rule:&lt;/p&gt;
&lt;pre class="literal-block"&gt;and_x_c_in_range: int_and(x, C)
    check x.lower &amp;gt;= 0 and x.upper &amp;lt;= C &amp;amp; ~(C + 1)
    =&amp;gt; x&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="rule-ordering-and-liveness"&gt;
&lt;h3&gt;Rule Ordering and Liveness&lt;/h3&gt;
&lt;p&gt;The generated optimizer code will give preference to applying rules that
produce a constant or a variable as a rewrite result. Only if none of those
match do rules that produce new result operations get applied. For example, the
rules &lt;code class="docutils literal"&gt;sub_x_x&lt;/code&gt; and &lt;code class="docutils literal"&gt;sub_add&lt;/code&gt; are tried before trying &lt;code class="docutils literal"&gt;sub_add_consts&lt;/code&gt;,
because the former two rules optimize to a constant and a variable
respectively, while the latter produces a new operation as the result.&lt;/p&gt;
&lt;p&gt;The rule &lt;code class="docutils literal"&gt;sub_add_consts&lt;/code&gt; has a possible problem, which is that if the
intermediate result of the &lt;code class="docutils literal"&gt;int_add&lt;/code&gt; operation in the rule head is used by
some other operations, then the &lt;code class="docutils literal"&gt;sub_add_consts&lt;/code&gt; rule does not actually
reduce the number of operations (and might actually make things slightly worse
due to increased register pressure). However, currently it would be extremely
hard to take that kind of information into account in the optimization pass of
the JIT, so we optimistically apply the rules anyway.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="checking-rule-coverage"&gt;
&lt;h3&gt;Checking rule coverage&lt;/h3&gt;
&lt;p&gt;Every rewrite rule should have at least one unit test where it triggers. To
ensure this, the &lt;a class="reference external" href="https://github.com/pypy/pypy/blob/d92d0bfd38318ede1cbaadadafd77da69d431fad/rpython/jit/metainterp/optimizeopt/test/test_optimizeintbound.py"&gt;unit test file that mainly checks integer optimizations&lt;/a&gt; in the
JIT has an assert at the end of a test run, that every rule fired at least once.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="printing-rule-statistics"&gt;
&lt;h3&gt;Printing rule statistics&lt;/h3&gt;
&lt;p&gt;The JIT can print statistics about which rule fired how often in the
&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;jit-intbounds-stats&lt;/span&gt;&lt;/code&gt; logging category, using the &lt;a class="reference external" href="https://rpython.readthedocs.io/en/latest/logging.html"&gt;PYPYLOG&lt;/a&gt; mechanism. For
example, to print the category to stdout at the end of program execution, run
PyPy like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;PYPYLOG=jit-intbounds-stats:- pypy ...&lt;/pre&gt;
&lt;p&gt;The output of that will look something like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;int_add
    add_reassoc_consts 2514
    add_zero 107008
int_sub
    sub_zero 31519
    sub_from_zero 523
    sub_x_x 3153
    sub_add_consts 159
    sub_add 55
    sub_sub_x_c_c 1752
    sub_sub_c_x_c 0
    sub_xor_x_y_y 0
    sub_or_x_y_y 0
int_mul
    mul_zero 0
    mul_one 110
    mul_minus_one 0
    mul_pow2_const 1456
    mul_lshift 0
...&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="termination-and-confluence"&gt;
&lt;h3&gt;Termination and Confluence&lt;/h3&gt;
&lt;p&gt;Right now there are unfortunately no checks that the rules actually rewrite
operations towards "simpler" forms. There is no cost model, and also nothing
that prevents you from writing a rule like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;neg_complication: int_neg(x) # leads to infinite rewrites
    =&amp;gt; int_mul(-1, x)&lt;/pre&gt;
&lt;p&gt;Doing this would lead to endless rewrites if there is also another rule that
turns multiplication with -1 into negation.&lt;/p&gt;
&lt;p&gt;There is also no checking for &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Confluence_(abstract_rewriting)"&gt;confluence&lt;/a&gt; (yet?), i.e. the property that all
rewrites starting from the same input trace always lead to the same output
trace, no matter in which order the rules are applied.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="proofs"&gt;
&lt;h3&gt;Proofs&lt;/h3&gt;
&lt;p&gt;It is very easy to write a peephole rule that is not correct in all corner
cases. Therefore all the rules are proven correct with Z3 before compiled into
actual JIT code, by default. When the proof fails, a (hopefully minimal)
counterexample is printed. The counterexample consists of values for all the
inputs that fulfil the checks, values for the intermediate expressions, and
then two &lt;em&gt;different&lt;/em&gt; values for the source and the target operations.&lt;/p&gt;
&lt;p&gt;E.g. if we try to add the incorrect rule:&lt;/p&gt;
&lt;pre class="literal-block"&gt;mul_is_add: int_mul(a, b)
    =&amp;gt; int_add(a, b)&lt;/pre&gt;
&lt;p&gt;We get the following counterexample as output:&lt;/p&gt;
&lt;pre class="literal-block"&gt;Could not prove correctness of rule 'mul_is_add'
in line 1
counterexample given by Z3:
counterexample values:
a: 0
b: 1
operation int_mul(a, b) with Z3 formula a*b
has counterexample result vale: 0
BUT
target expression: int_add(a, b) with Z3 formula a + b
has counterexample value: 1&lt;/pre&gt;
&lt;p&gt;If we add conditions, they are taken into account and the counterexample will
fulfil the conditions:&lt;/p&gt;
&lt;pre class="literal-block"&gt;mul_is_add: int_mul(a, b)
    check a.known_gt_const(1) and b.known_gt_const(2)
    =&amp;gt; int_add(a, b)&lt;/pre&gt;
&lt;p&gt;This leads to the following counterexample:&lt;/p&gt;
&lt;pre class="literal-block"&gt;Could not prove correctness of rule 'mul_is_add'
in line 46
counterexample given by Z3:
counterexample values:
a: 2
b: 3
operation int_mul(a, b) with Z3 formula a*b
has counterexample result vale: 6
BUT
target expression: int_add(a, b) with Z3 formula a + b
has counterexample value: 5&lt;/pre&gt;
&lt;p&gt;Some &lt;code class="docutils literal"&gt;IntBound&lt;/code&gt; methods cannot be used in Z3 proofs because their &lt;a class="reference external" href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#cases-where-this-style-of-z3-proof-doesnt-work)."&gt;control
flow is too complex&lt;/a&gt;. If that is the case, they can have Z3-equivalent
formulations defined (in every case this is done, it's a potential proof hole if
the Z3 friendly reformulation and the real implementation differ from each
other, therefore extra care is required to make very sure they are equivalent).&lt;/p&gt;
&lt;p&gt;It's possible to skip the proof of individual rules entirely by adding
&lt;code class="docutils literal"&gt;SORRY_Z3&lt;/code&gt; to its body (but we should try not to do that too often):&lt;/p&gt;
&lt;pre class="literal-block"&gt;eq_different_knownbits: int_eq(x, y)
    SORRY_Z3
    check x.known_ne(y)
    =&amp;gt; 0&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="checking-for-satisfiability"&gt;
&lt;h3&gt;Checking for satisfiability&lt;/h3&gt;
&lt;p&gt;In addition to checking whether the rule yields a correct optimization, we also
check whether the rule can ever apply. This ensures that there are &lt;em&gt;some&lt;/em&gt;
runtime values that would fulfil all the checks in a rule. Here's an example of
a rule violating this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;never_applies: int_is_true(x)
    check x.known_lt_const(0) and x.known_gt_const(0) # impossible condition, always False
    =&amp;gt; x&lt;/pre&gt;
&lt;p&gt;Right now the error messages if this goes wrong are not completely easy to
understand. I hope to be able to improve this later:&lt;/p&gt;
&lt;pre class="literal-block"&gt;Rule 'never_applies' cannot ever apply
in line 1
Z3 did not manage to find values for variables x such that the following condition becomes True:
And(x &amp;lt;= x_upper,
    x_lower &amp;lt;= x,
    If(x_upper &amp;lt; 0, x_lower &amp;gt; 0, x_upper &amp;lt; 0))&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="implementation-notes"&gt;
&lt;h3&gt;Implementation Notes&lt;/h3&gt;
&lt;p&gt;The implementation of the DSL is done in a relatively ad-hoc manner. It is
parsed using &lt;a class="reference external" href="https://rply.readthedocs.io/"&gt;rply&lt;/a&gt;, there's a small type checker that tries to find common
problems in how the rules are written. Z3 is used via the Python API, like in
the previous blog posts that are using it. The
pattern matching RPython code is generated using an approach inspired by Luc
Maranget's paper &lt;a class="reference external" href="http://moscova.inria.fr/~maranget/papers/ml05e-maranget.pdf"&gt;Compiling Pattern Matching to Good Decision Trees&lt;/a&gt;. See
&lt;a class="reference external" href="https://compiler.club/compiling-pattern-matching/"&gt;this blog post&lt;/a&gt; for an approachable introduction.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now that I've described the DSL, here are the rules that are equivalent to the
imperative code in the motivation section:&lt;/p&gt;
&lt;pre class="literal-block"&gt;mul_zero: int_mul(x, 0)
    =&amp;gt; 0

mul_one: int_mul(x, 1)
    =&amp;gt; x

mul_minus_one: int_mul(x, -1)
    =&amp;gt; int_neg(x)

mul_pow2_const: int_mul(x, C)
    check C &amp;gt; 0 and C &amp;amp; (C - 1) == 0
    shift = highest_bit(C)
    =&amp;gt; int_lshift(x, shift)

mul_lshift: int_mul(x, int_lshift(1, y))
    check y.known_ge_const(0) and y.known_le_const(LONG_BIT)
    =&amp;gt; int_lshift(x, y)&lt;/pre&gt;
&lt;p&gt;The current status of the DSL is that it got merged to PyPy's main branch. I
rewrote a part of the integer rewrites &lt;a class="reference external" href="https://github.com/pypy/pypy/blob/d92d0bfd38318ede1cbaadadafd77da69d431fad/rpython/jit/metainterp/ruleopt/real.rules"&gt;into the DSL&lt;/a&gt;, but some are still in the
old imperative style (mostly for complicated reasons, the easily ported ones are
all done). Since I've only been porting optimizations that had existed prior to
the existence of the DSL, performance numbers of benchmarks didn't change.&lt;/p&gt;
&lt;p&gt;There are a number of features that are still missing and some possible
extensions that I plan to work on in the future:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;All the integer operations that the DSL handles so far are the variants that
do not check for overflow (or where overflow was proven to be impossible to
happen). In regular Python code the overflow-checking variants &lt;cite&gt;int_add_ovf&lt;/cite&gt;
etc are much more common, but the DSL doesn't support them yet. I plan to fix
this, but don't completely understand how the correctness proofs for them
should be done correctly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A related problem is that I don't understand what it means for a rewrite to be
correct if some of the operations are only defined for a subset of the input
values. E.g. division isn't defined if the divisor is zero. In theory, a
division operation in the trace should always be preceded by a check that the
divisor isn't zero. But sometimes other optimization move the check around and
the connection to the division gets lost or muddled. What optimizations can we
still safely perform on the division? There's lots of prior work on this
question, but I still don't understand what the correct approach in our
context would be.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ordering comparisons like &lt;code class="docutils literal"&gt;int_lt&lt;/code&gt;, &lt;code class="docutils literal"&gt;int_le&lt;/code&gt; and their unsigned variants are
not ported to the DSL yet. Comparisons are an area where the JIT is not super
good yet at optimizing away operations. This is a pretty big topic and I've
started a project with Nico Rittinghaus to try to improve the situation a bit
more generally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A more advanced direction of work would be to implement a simplified form of
&lt;a class="reference external" href="https://egraphs-good.github.io/"&gt;e-graphs&lt;/a&gt; (or &lt;a class="reference external" href="https://vimeo.com/843540328"&gt;ae-graphs&lt;/a&gt;). The JIT has like half of an e-graph data
structure already, and we probably can't afford a full one in terms of compile
time costs, but maybe we can have two thirds or something?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="acknowledgements"&gt;
&lt;h2&gt;Acknowledgements&lt;/h2&gt;
&lt;p&gt;Thank you to &lt;a class="reference external" href="https://bernsteinbear.com/"&gt;Max Bernstein&lt;/a&gt; and &lt;a class="reference external" href="https://martinfriedrichberger.net/"&gt;Martin Berger&lt;/a&gt; for super helpful feedback on
drafts of the post!&lt;/p&gt;
&lt;/section&gt;</description><category>jit</category><category>z3</category><guid>https://www.pypy.org/posts/2024/10/jit-peephole-dsl.html</guid><pubDate>Wed, 23 Oct 2024 15:00:00 GMT</pubDate></item><item><title>A Knownbits Abstract Domain for the Toy Optimizer, Correctly</title><link>https://www.pypy.org/posts/2024/08/toy-knownbits.html</link><dc:creator>CF Bolz-Tereick</dc:creator><description>&lt;p&gt;After &lt;a href="https://bernsteinbear.com/blog/toy-abstract-interpretation/"&gt;Max' introduction to abstract interpretation for the toy optimizer&lt;/a&gt; in the
last post, I want to present a more complicated abstract domain in this post.
This abstract domain reasons about the individual bits of a variable in a trace.
Every bit can be either "known zero", "known one" or "unknown". The abstract
domain is useful for optimizing integer operations, particularly the bitwise operations.
The abstract domain follows quite closely the &lt;a href="https://github.com/torvalds/linux/blob/master/kernel/bpf/tnum.c"&gt;tristate abstract domain of the
eBPF verifier in the Linux
Kernel&lt;/a&gt;, as
described by the paper
&lt;a href="https://arxiv.org/abs/2105.05398"&gt;Sound, Precise, and Fast Abstract Interpretation with Tristate
Numbers&lt;/a&gt; by Harishankar Vishwanathan, Matan
Shachnai, Srinivas Narayana, and Santosh Nagarakatte.&lt;/p&gt;
&lt;p&gt;The presentation in this post will still be in the context of the
&lt;a href="https://www.pypy.org/categories/toy-optimizer"&gt;toy optimizer&lt;/a&gt;. We'll spend a significant part of
the post convincing ourselves that the abstract domain transfer functions that
we're writing are really correct, using both property-based testing and
automated proofs (again using Z3).&lt;/p&gt;
&lt;p&gt;PyPy has implemented and merged a more complicated version of the same abstract
domain for the "real" PyPy JIT. A more thorough explanation of that real world
implementation will follow.&lt;/p&gt;
&lt;p&gt;I'd like to thank Max Bernstein and Armin Rigo for lots of great feedback on
drafts of this post. The PyPy implementation was mainly done by Nico
Rittinghaus and me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Contents:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#motivation"&gt;Motivation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#the-knownbits-abstract-domain"&gt;The Knownbits Abstract Domain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#transfer-functions"&gt;Transfer Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#property-based-tests-with-hypothesis"&gt;Property-based Tests with Hypothesis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#when-are-transfer-functions-correct-how-do-we-test-them"&gt;When are Transfer Functions Correct? How do we test them?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#implementing-binary-transfer-functions"&gt;Implementing Binary Transfer Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#addition-and-subtraction"&gt;Addition and Subtraction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#proving-correctness-of-the-transfer-functions-with-z3"&gt;Proving correctness of the transfer functions with Z3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#cases-where-this-style-of-z3-proof-doesnt-work"&gt;Cases where this style of Z3 proof doesn't work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#making-statements-about-precision"&gt;Making Statements about Precision&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#using-the-abstract-domain-in-the-toy-optimizer-for-generalized-constant-folding"&gt;Using the Abstract Domain in the Toy Optimizer for Generalized Constant Folding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#using-the-knownbits-domain-for-conditional-peephole-rewrites"&gt;Using the KnownBits Domain for Conditional Peephole Rewrites&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h3 id="motivation"&gt;Motivation&lt;/h3&gt;
&lt;p&gt;In many programs that do bit-manipulation of integers, some of the bits of the
integer variables of the program can be statically known. Here's a simple
example:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
...
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;:
&lt;span class="w"&gt;    &lt;/span&gt;...
&lt;span class="k"&gt;else&lt;/span&gt;:
&lt;span class="w"&gt;    &lt;/span&gt;...
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After the assignment &lt;code&gt;x = a | 1&lt;/code&gt;, we know that the lowest bit of &lt;code&gt;x&lt;/code&gt; must be &lt;code&gt;1&lt;/code&gt;
(the other bits are unknown) and an optimizer could remove the condition &lt;code&gt;x &amp;amp; 1&lt;/code&gt; by
constant-folding it to &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Another (more complicated) example is:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;assert i &amp;amp; 0b111 == 0 # check that i is a multiple of 8
j = i + 16
assert j &amp;amp; 0b111 == 0
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This kind of code could e.g. happen in a &lt;a href="https://docs.pydrofoil.org/en/latest/"&gt;CPU
emulator&lt;/a&gt;, where &lt;code&gt;i&lt;/code&gt; and &lt;code&gt;j&lt;/code&gt; are
integers that represent emulated pointers, and the &lt;code&gt;assert&lt;/code&gt;s are alignment
checks. The first assert implies that the lowest three bits of i must be &lt;code&gt;0&lt;/code&gt;.
Adding 16 to such a number produces a result where the lowest three bits are
again all &lt;code&gt;0&lt;/code&gt;, therefore the second assert is always true. So we would like a
compiler to remove the second assert.&lt;/p&gt;
&lt;p&gt;Both of these will optimizations are doable with the help of the knownbits
abstract domain that we'll discuss in the rest of the post.&lt;/p&gt;
&lt;h3 id="the-knownbits-abstract-domain"&gt;The Knownbits Abstract Domain&lt;/h3&gt;
&lt;p&gt;An abstract value of the knownbits domain needs to be able to store, for every
bit of an integer variable in a program, whether it is known 0, known 1, or
unknown. To represent
three different states, we need 2 bits, which we will call &lt;code&gt;one&lt;/code&gt; and &lt;code&gt;unknown&lt;/code&gt;.
Here's the encoding:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;one&lt;/th&gt;
&lt;th&gt;unknown&lt;/th&gt;
&lt;th style="text-align: right;"&gt;knownbit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;illegal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;unknown&lt;/code&gt; bit is set if we don't know the value of the bit ("?"), the &lt;code&gt;one&lt;/code&gt;
bit is set if the bit is known to be a &lt;code&gt;1&lt;/code&gt;. Since two bits are enough to encode
four different states, but we only need three, the combination of a set &lt;code&gt;one&lt;/code&gt;
bit and a set &lt;code&gt;unknown&lt;/code&gt; is not allowed.&lt;/p&gt;
&lt;p&gt;We don't just want to encode a single bit, however. Instead, we want to do this
for all the bits of an integer variable. Therefore the instances of the abstract
domain get two integer fields &lt;code&gt;ones&lt;/code&gt; and &lt;code&gt;unknowns&lt;/code&gt;, where each pair of
corresponding bits encodes the knowledge about the corresponding bit of the
integer variable in the program.&lt;/p&gt;
&lt;p&gt;We can start implementing a Python class that works like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dataclasses&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;__post_init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_well_formed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;is_well_formed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# a bit cannot be both 1 and unknown&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="nd"&gt;@staticmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;from_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;""" Construct a KnownBits corresponding to a constant, where all bits&lt;/span&gt;
&lt;span class="sd"&gt;        are known."""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;""" Check if the KnownBits instance represents a constant. """&lt;/span&gt;
        &lt;span class="c1"&gt;# it's a constant if there are no unknowns&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can also add some convenience properties. Sometimes it is easier to work
with an integer where all the &lt;em&gt;known&lt;/em&gt; bits are set, or one where the positions
of all the known zeros have a set bit:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;knowns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;""" return an integer where the known bits are set. """&lt;/span&gt;
        &lt;span class="c1"&gt;# the knowns are just the unknowns, inverted&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;zeros&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;""" return an integer where the places that are known zeros have a bit&lt;/span&gt;
&lt;span class="sd"&gt;        set. """&lt;/span&gt;
        &lt;span class="c1"&gt;# it's a 0 if it is known, but not 1&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;knowns&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Also, for debugging and for writing tests we want a way to print the known bits
in a human-readable form, and also to have a way to construct a &lt;code&gt;KnownBits&lt;/code&gt;
instance from a string. It's not important to understand the details of
&lt;code&gt;__str__&lt;/code&gt; or &lt;code&gt;from_str&lt;/code&gt; for the rest of the post, so I'm putting them into a fold:&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;&lt;code&gt;KnownBits&lt;/code&gt; from and to string conversions&lt;/summary&gt;


&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;__repr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"KnownBits.from_constant(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"KnownBits(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;__str__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt;
        &lt;span class="c1"&gt;# construct the string representation right to left&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;unknowns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="c1"&gt;# we leave off the leading known 0s&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;unknowns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;# -1 has all bits set in two's complement, so the leading&lt;/span&gt;
                &lt;span class="c1"&gt;# bits are all 1&lt;/span&gt;
                &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;# -1 has all bits set in two's complement, so the leading bits&lt;/span&gt;
                &lt;span class="c1"&gt;# are all ?&lt;/span&gt;
                &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ones&lt;/span&gt;
                &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"?"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'?'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'0'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'0'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@staticmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;""" Construct a KnownBits instance that from a string. String can start&lt;/span&gt;
&lt;span class="sd"&gt;        with ...1 to mean that all higher bits are 1, or ...? to mean that all&lt;/span&gt;
&lt;span class="sd"&gt;        higher bits are unknown. Otherwise it is assumed that the higher bits&lt;/span&gt;
&lt;span class="sd"&gt;        are all 0. """&lt;/span&gt;
        &lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;startindex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"...?"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;startindex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"...1"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;startindex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;startindex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'?'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unknowns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@staticmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;all_unknown&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;""" convenience constructor for the "all bits unknown" abstract value&lt;/span&gt;
&lt;span class="sd"&gt;        """&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"...?"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;



&lt;/details&gt;

&lt;p&gt;And here's a &lt;a href="https://pytest.org"&gt;pytest&lt;/a&gt;-style unit test for &lt;code&gt;str&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_str&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'0'&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'101'&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mb"&gt;0b10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'1?1'&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="mb"&gt;0b1111&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mb"&gt;0b10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'...100?0'&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="mb"&gt;0b1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'...?1'&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;An instance of &lt;code&gt;KnownBits&lt;/code&gt; represents a set of integers, namely those that match
the known bits stored in the instance. We can write a method &lt;code&gt;contains&lt;/code&gt; that
takes a concrete &lt;code&gt;int&lt;/code&gt; value and returns &lt;code&gt;True&lt;/code&gt; if the value matches the
pattern of the known bits:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;""" Check whether the KnownBits instance contains the concrete integer&lt;/span&gt;
&lt;span class="sd"&gt;        `value`. """&lt;/span&gt;
        &lt;span class="c1"&gt;# check whether value matches the bit pattern. in the places where we&lt;/span&gt;
        &lt;span class="c1"&gt;# know the bits, the value must agree with ones.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;knowns&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and a test:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_contains&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'1?1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mb"&gt;0b111&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mb"&gt;0b101&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mb"&gt;0b110&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mb"&gt;0b011&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'...?1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# all odd numbers&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="transfer-functions"&gt;Transfer Functions&lt;/h3&gt;
&lt;p&gt;Now that we have implemented the basics of the &lt;code&gt;KnownBits&lt;/code&gt; class, we need to
start implementing the transfer functions. They are for computing what we know
about the &lt;em&gt;results&lt;/em&gt; of an operation, given the knowledge we have about the bits
of the arguments.&lt;/p&gt;
&lt;p&gt;We'll start with a simple unary operation, &lt;code&gt;invert(x)&lt;/code&gt; (which is &lt;code&gt;~x&lt;/code&gt; in Python
and C syntax), which flips all the bits of at integer. If we know some bits of
the arguments, we can compute the corresponding bits of the result. The unknown
bits remain unknown.&lt;/p&gt;
&lt;p&gt;Here's the code:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;abstract_invert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# self.zeros has bits set where the known 0s are in self&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zeros&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And a unit-test:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_invert&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'01?01?01?'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_invert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'...10?10?10?'&lt;/span&gt;

    &lt;span class="n"&gt;k1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'...?'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_invert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'...?'&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Before we continue with further transfer functions, we'll think about
correctness of the transfer functions and build up some test infrastructure. To
test transfer functions, it's quite important to move being simple example-style
unit tests. The state-space for more complicated binary transfer functions is
extremely large and it's too easy to do something wrong in a corner case.
Therefore we'll look at property-based-test for &lt;code&gt;KnownBits&lt;/code&gt; next.&lt;/p&gt;
&lt;h3 id="property-based-tests-with-hypothesis"&gt;Property-based Tests with Hypothesis&lt;/h3&gt;
&lt;p&gt;We want to do property-based tests of &lt;code&gt;KnownBits&lt;/code&gt;, to try
make it less likely that we'll get a corner-case in the implementation wrong.
We'll use &lt;a href="https://hypothesis.readthedocs.io/en/latest/"&gt;Hypothesis&lt;/a&gt; for that.&lt;/p&gt;
&lt;p&gt;I can't give a decent introduction to Hypothesis here, but want to give a few
hints about the API. Hypothesis is a way to run unit tests with randomly
generated input. It provides &lt;em&gt;strategies&lt;/em&gt; to describe the data that the test
functions expects. Hypothesis provides primitive strategies (for things like
integers, strings, floats, etc) and ways to build composite strategies out of
the primitive ones.&lt;/p&gt;
&lt;p&gt;To be able to write the tests, we need to generate random &lt;code&gt;KnownBits&lt;/code&gt; instances,
and we also want an &lt;code&gt;int&lt;/code&gt; instance that is a member of the &lt;code&gt;KnownBits&lt;/code&gt; instance.
We generate tuples of &lt;code&gt;(KnownBits, int)&lt;/code&gt; together, to ensure this property.
We'll ask Hypothesis to generate us a random concrete &lt;code&gt;int&lt;/code&gt; as the concrete
value, and then we'll also generate a second random &lt;code&gt;int&lt;/code&gt; to use as the
&lt;code&gt;unknown&lt;/code&gt; masks (i.e. which bits of the concrete int we don't know in the
&lt;code&gt;KnownBits&lt;/code&gt; instance). Here's a function that takes two such ints and builds the
tuple:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;build_knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;concrete_value&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# to construct a valid KnownBits instance, we need to mask off the unknown&lt;/span&gt;
    &lt;span class="c1"&gt;# bits&lt;/span&gt;
    &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;concrete_value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unknowns&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;concrete_value&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can turn this function into a hypothesis strategy to generate input data
using the &lt;code&gt;strategies.builds&lt;/code&gt; function:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;hypothesis&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;strategies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;given&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;

&lt;span class="n"&gt;ints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strategies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;integers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;random_knownbits_and_contained_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strategies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;builds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;build_knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ints&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ints&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;One important special case of &lt;code&gt;KnownBits&lt;/code&gt; are the constants, which contain only
a single concrete value. We'll also generate some of those specifically, and
then combine the &lt;code&gt;random_knownbits_and_contained_number&lt;/code&gt; strategy with it:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;constant_knownbits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strategies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;builds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;ints&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;knownbits_and_contained_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;constant_knownbits&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;random_knownbits_and_contained_number&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we can write the first property-based tests, for the &lt;code&gt;KnownBits.contains&lt;/code&gt;
method:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nd"&gt;@given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;@given&lt;/code&gt; decorator is used to tell Hypothesis which strategy to use to
generate random data for the test function. Hypothesis will run the test with a
number of random examples (100 by default). If it finds an error, it will try to
minimize the example needed that demonstrates the problem, to try to make it
easier to understand what is going wrong. It also saves all failing cases into
an example database and tries them again on subsequent runs.&lt;/p&gt;
&lt;p&gt;This test is as much a check for whether we got the strategies right as it is
for the logic in &lt;code&gt;KnownBits.contains&lt;/code&gt;. Here's an example output of random
concrete and abstract values that we are getting here:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="mf"&gt;110000011001101&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;???&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;
&lt;span class="mf"&gt;...1011011&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...1011011&lt;/span&gt;
&lt;span class="mf"&gt;...1001101110101000010010011111011&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...1001101110101000010010011111011&lt;/span&gt;
&lt;span class="mf"&gt;...1001101110101000010010011111011&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...100110111010100001&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;010&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;11&lt;/span&gt;
&lt;span class="mf"&gt;1000001101111101001011010011111101000011000111011001011111101&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1000001101111101001011010011111101000011000111011001011111101&lt;/span&gt;
&lt;span class="mf"&gt;1000001101111101001011010011111101000011000111011001011111101&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1000001101111101001011010011111101000011000111&lt;/span&gt;&lt;span class="err"&gt;????&lt;/span&gt;&lt;span class="mf"&gt;01&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;11&lt;/span&gt;&lt;span class="err"&gt;?????&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;
&lt;span class="mf"&gt;1111100000010&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1111100000010&lt;/span&gt;
&lt;span class="mf"&gt;1111100000010&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;11111&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;00000&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;
&lt;span class="mf"&gt;110110&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;110110&lt;/span&gt;
&lt;span class="mf"&gt;110110&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;&lt;span class="err"&gt;????&lt;/span&gt;&lt;span class="mf"&gt;11&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;10&lt;/span&gt;
&lt;span class="mf"&gt;110110&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;
&lt;span class="mf"&gt;...100010111011111&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;10111&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;111&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;
&lt;span class="mf"&gt;...1000100000110001&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;000&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;00000&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;000&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;
&lt;span class="mf"&gt;110000001110&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;000&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0000000&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;&lt;span class="err"&gt;???&lt;/span&gt;&lt;span class="mf"&gt;0000&lt;/span&gt;&lt;span class="err"&gt;?????&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;&lt;span class="err"&gt;???&lt;/span&gt;&lt;span class="mf"&gt;000&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;01&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;000&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;
&lt;span class="mf"&gt;110000001110&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;000000&lt;/span&gt;&lt;span class="err"&gt;???&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;
&lt;span class="mf"&gt;1011011010000001110101001111000010001001011101010010010001000000010101010010001101110101111111010101010010101100110000011110000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1011011010000001110101001111000010001001011101010010010001000000010101010010001101110101111111010101010010101100110000011110000&lt;/span&gt;
&lt;span class="mf"&gt;...1011010010010100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...1011010010010100&lt;/span&gt;
&lt;span class="mf"&gt;...1011111110110011&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...1011111110110011&lt;/span&gt;
&lt;span class="mf"&gt;101000011110110&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;101000011&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;10&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;
&lt;span class="mf"&gt;100101&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That looks suitably random, but we might want to bias our random numbers a
little bit towards common error values like small constants, powers of two, etc.
Like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;
&lt;span class="c1"&gt;# some small integers&lt;/span&gt;
&lt;span class="n"&gt;ints_special&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# powers of two&lt;/span&gt;
&lt;span class="n"&gt;ints_special&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ints_special&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;union&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# powers of two - 1&lt;/span&gt;
&lt;span class="n"&gt;ints_special&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ints_special&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;union&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# negative versions of what we have so far&lt;/span&gt;
&lt;span class="n"&gt;ints_special&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ints_special&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;union&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ints_special&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# bit-flipped versions of what we have so far&lt;/span&gt;
&lt;span class="n"&gt;ints_special&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ints_special&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;union&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ints_special&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ints_special&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ints_special&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# sort them (because hypothesis simplifies towards earlier elements in the list)&lt;/span&gt;
&lt;span class="n"&gt;ints_special&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;ints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strategies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sampled_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ints_special&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;strategies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;integers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we get data like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="mf"&gt;1110&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1110&lt;/span&gt;
&lt;span class="mf"&gt;...10000000000000000001&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...10000&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;0000&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;
&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;0000&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;
&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;
&lt;span class="mf"&gt;...10101100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...10101100&lt;/span&gt;
&lt;span class="mf"&gt;110000000011001010111011111111111111011110010001001100110001011&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;101&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;
&lt;span class="mf"&gt;110000000011001010111011111111111111011110010001001100110001011&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;00000000&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;???&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;??????????????&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;????&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;000&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="mf"&gt;000&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;
&lt;span class="mf"&gt;...1011111111111111111111111111&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;11&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;11&lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;
&lt;span class="mf"&gt;...1011111111111111111111111111&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;??????????????????????????&lt;/span&gt;
&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;??????????????????????????&lt;/span&gt;
&lt;span class="mf"&gt;101101&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;101101&lt;/span&gt;
&lt;span class="mf"&gt;111111111111111111111111111111111111111111111&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;111111111111111111111111111111111111111111111&lt;/span&gt;
&lt;span class="mf"&gt;10111&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;10111&lt;/span&gt;
&lt;span class="mf"&gt;...101100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...1&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;111011&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;
&lt;span class="mf"&gt;101000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;001010&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;
&lt;span class="mf"&gt;101000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;000&lt;/span&gt;
&lt;span class="mf"&gt;110010&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;110010&lt;/span&gt;
&lt;span class="mf"&gt;...100111&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...100111&lt;/span&gt;
&lt;span class="mf"&gt;1111011010010&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1111011010010&lt;/span&gt;
&lt;span class="mf"&gt;...1000000000000000000000000000000000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;...1000000000000000000000000000000000000&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can also write a test that checks that the somewhat tricky logic in
&lt;code&gt;__str__&lt;/code&gt; and &lt;code&gt;from_str&lt;/code&gt; is correct, by making sure that the two functions
round-trip (ie converting a &lt;code&gt;KnownBits&lt;/code&gt; to a string and then back to a
&lt;code&gt;KnownBits&lt;/code&gt; instance produces the same abstract value).&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nd"&gt;@given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_hypothesis_str_roundtrips&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now let's actually apply this infrastructure to test &lt;code&gt;abstract_invert&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="when-are-transfer-functions-correct-how-do-we-test-them"&gt;When are Transfer Functions Correct? How do we test them?&lt;/h3&gt;
&lt;p&gt;Abstract values, i.e. instances of &lt;code&gt;KnownBits&lt;/code&gt; represent &lt;em&gt;sets&lt;/em&gt; of concrete
values. We want the transfer functions to compute &lt;em&gt;overapproximations&lt;/em&gt; of the
concrete values. So if we have an arbitrary abstract value &lt;code&gt;k&lt;/code&gt;, with a concrete
number &lt;code&gt;n&lt;/code&gt; that is a member of the abstract values (i.e.
&lt;code&gt;k.contains(n) == True&lt;/code&gt;) then the result of the concrete operation &lt;code&gt;op(n)&lt;/code&gt;
&lt;strong&gt;must&lt;/strong&gt; be a member of the result of the abstract operation &lt;code&gt;k.abstract_op()&lt;/code&gt;
(i.e. &lt;code&gt;k.abstract_op().contains(op(n)) == True&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Checking the correctness/overapproximation property is a good match for
hypothesis. Here's what the test for &lt;code&gt;abstract_invert&lt;/code&gt; looks like:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nd"&gt;@given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_hypothesis_invert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;
    &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="c1"&gt;# compute the real result&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_invert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# compute the abstract result&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# the abstract result must contain the real result&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the &lt;em&gt;only&lt;/em&gt; condition needed for &lt;code&gt;abstract_invert&lt;/code&gt; to be correct. If
&lt;code&gt;abstract_invert&lt;/code&gt; fulfils this property for every combination of abstract and
concrete value then &lt;code&gt;abstract_invert&lt;/code&gt; is correct. Note however, that this test
does not actually check whether &lt;code&gt;abstract_invert&lt;/code&gt; gives us precise results. A
correct (but imprecise) implementation of &lt;code&gt;abstract_invert&lt;/code&gt; would simply return
a completely unknown result, regardless of what is known about the input
&lt;code&gt;KnownBits&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The "proper" CS term for this notion of correctness is called &lt;em&gt;soundness&lt;/em&gt;. The
correctness condition on the transfer functions is called a &lt;em&gt;Galois
connection&lt;/em&gt;. I won't go into any mathematical/technical details here, but
wanted to at least mention the terms. I found &lt;a href="https://web.njit.edu/~mjk76/"&gt;Martin
Kellogg&lt;/a&gt;'s
&lt;a href="https://web.njit.edu/~mjk76/teaching/cs684-sp24/assets/lecture-12.pdf#34"&gt;slides&lt;/a&gt;
to be quite an approachable introduction to the Galois connection and how to
show soundness.&lt;/p&gt;
&lt;h3 id="implementing-binary-transfer-functions"&gt;Implementing Binary Transfer Functions&lt;/h3&gt;
&lt;p&gt;Now we have infrastructure in place for testing transfer functions with random
inputs. With that we can start thinking about the more complicated case, that of
binary operations. Let's start with the simpler ones, &lt;code&gt;and&lt;/code&gt; and &lt;code&gt;or&lt;/code&gt;. For &lt;code&gt;and&lt;/code&gt;,
we can know a &lt;code&gt;0&lt;/code&gt; bit in the result if either of the input bits are known &lt;code&gt;0&lt;/code&gt;;
or we can know a &lt;code&gt;1&lt;/code&gt; bit in the result if both input bits are known &lt;code&gt;1&lt;/code&gt;.
Otherwise the resulting bit is unknown. Let's look at all the combinations:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;and
input1: 000111???
input2: 01?01?01? 
result: 00001?0??
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;abstract_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="c1"&gt;# known ones&lt;/span&gt;
        &lt;span class="n"&gt;knowns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zeros&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zeros&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ones&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;knowns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here's an example unit-test and a property-based test for &lt;code&gt;and&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_and&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# test all combinations of 0, 1, ? in one example&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'01?01?01?'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'000111???'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# should be: 0...00001?0??&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt;   &lt;span class="s2"&gt;"1?0??"&lt;/span&gt;

&lt;span class="nd"&gt;@given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_hypothesis_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;
    &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;n3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To implement &lt;code&gt;or&lt;/code&gt; is pretty similar. The result is known &lt;code&gt;1&lt;/code&gt; where either of the
inputs is &lt;code&gt;1&lt;/code&gt;. The result is known &lt;code&gt;0&lt;/code&gt; where both inputs are known &lt;code&gt;0&lt;/code&gt;, and &lt;code&gt;?&lt;/code&gt;
otherwise.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;or
input1: 000111???
input2: 01?01?01? 
result: 01?111?1?
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;abstract_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;
        &lt;span class="n"&gt;zeros&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zeros&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zeros&lt;/span&gt;
        &lt;span class="n"&gt;knowns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;zeros&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;knowns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here's an example unit-test and a property-based test for &lt;code&gt;or&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_or&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'01?01?01?'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'000111???'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# should be:  0...01?111?1?&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt;   &lt;span class="s2"&gt;"1?111?1?"&lt;/span&gt;

&lt;span class="nd"&gt;@given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_hypothesis_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;
    &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;n3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Implementing support for &lt;code&gt;abstract_xor&lt;/code&gt; is relatively simple, and left as an
exercise :-).&lt;/p&gt;
&lt;h3 id="addition-and-subtraction"&gt;Addition and Subtraction&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;invert&lt;/code&gt;, &lt;code&gt;and&lt;/code&gt;, and &lt;code&gt;or&lt;/code&gt; are relatively simple transfer functions to write,
because they compose over the individual bits of the integers. The arithmetic
functions &lt;code&gt;add&lt;/code&gt; and &lt;code&gt;sub&lt;/code&gt; are significantly harder, because of carries and
borrows. Coming up with the formulas for them and gaining an intuitive
understanding is quite tricky and involves carefully going through a few
examples with pen and paper. When implementing this in PyPy, Nico and I didn't
come up with the implementation ourselves, but instead took them from the
&lt;a href="https://arxiv.org/abs/2105.05398"&gt;Tristate Numbers&lt;/a&gt; paper. Here's the code,
with example tests and hypothesis tests:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;abstract_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;sum_ones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;
        &lt;span class="n"&gt;sum_unknowns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt;
        &lt;span class="n"&gt;all_carries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sum_ones&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sum_unknowns&lt;/span&gt;
        &lt;span class="n"&gt;ones_carries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;all_carries&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;sum_ones&lt;/span&gt;
        &lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ones_carries&lt;/span&gt;
        &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sum_ones&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unknowns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;abstract_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;diff_ones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;
        &lt;span class="n"&gt;val_borrows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff_ones&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff_ones&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;val_borrows&lt;/span&gt;
        &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;diff_ones&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unknowns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_add&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'0?10?10?10'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'0???111000'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt;   &lt;span class="s2"&gt;"?????01?10"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_sub&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'0?10?10?10'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'0???111000'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt;   &lt;span class="s2"&gt;"...?11?10"&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;    &lt;span class="s1"&gt;'...1?10?10?10'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'...10000???111000'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt;   &lt;span class="s2"&gt;"111?????11?10"&lt;/span&gt;

&lt;span class="nd"&gt;@given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_hypothesis_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;
    &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;n3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_hypothesis_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;
    &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;n3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we are in a pretty good situation, and have implemented abstract versions
for a bunch of important arithmetic and binary functions. What's also surprising
is that the implementation of all of the transfer functions is quite efficient.
We didn't have to write loops over the individual bits at all, instead we found
closed form expressions using primitive operations on the underlying integers
&lt;code&gt;ones&lt;/code&gt; and &lt;code&gt;unknowns&lt;/code&gt;. This means that computing the results of abstract
operations is quite efficient, which is important when using the abstract domain
in the context of a JIT compiler.&lt;/p&gt;
&lt;h3 id="proving-correctness-of-the-transfer-functions-with-z3"&gt;Proving correctness of the transfer functions with Z3&lt;/h3&gt;
&lt;p&gt;As one can probably tell from my recent posts, I've been thinking about
compiler correctness a lot. Getting the transfer functions absolutely
correct is really crucial, because a bug in them would lead to miscompilation of
Python code when the abstract domain is added to the JIT. While the randomized
tests are great, it's still entirely possible for them to miss bugs. The state
space for the arguments of a binary transfer function is &lt;code&gt;3**64 * 3**64&lt;/code&gt;, and if
only a small part of that contains wrong behaviour it would be really unlikely
for us to find it with random tests by chance. Therefore I was reluctant to
merge the PyPy branch that contained the new abstract domain for a long time.&lt;/p&gt;
&lt;p&gt;To increase our confidence in the correctness of the transfer functions further,
we can use Z3 to &lt;em&gt;prove&lt;/em&gt; their correctness, which gives us much stronger
guarantees (not 100%, obviously). In this subsection I will show how to do that.&lt;/p&gt;
&lt;p&gt;Here's an attempt to do this manually in the Python repl:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;z3&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Solver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# like last blog post, proof by failing to find counterexamples&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Not&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unsat&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# let's set up a z3 bitvector variable for an arbitrary concrete value&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'concrete_value'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt;
&lt;span class="n"&gt;concrete_value&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# due to operator overloading we can manipulate z3 formulas&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;n1&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt;
&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;concrete_value&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# now z3 bitvector variables for the ones and zeros fields&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'abstract_ones'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'abstract_unknowns'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# we construct a KnownBits instance with the z3 variables&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unknowns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# due to operator overloading we can call the methods on k1:&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_invert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;
&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;abstract_unknowns&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;abstract_ones&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt;
&lt;span class="n"&gt;abstract_unknowns&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# here's the correctness condition that we want to prove:&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;concrete_value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;abstract_unknowns&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;abstract_unknowns&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;abstract_ones&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# let's try&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;stdin&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;stdin&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;prove&lt;/span&gt;
&lt;span class="ne"&gt;AssertionError&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# it doesn't work! let's look at the counterexample to see why:&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;abstract_unknowns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;abstract_ones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;concrete_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# we can build a KnownBits instance with the values in the&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# counterexample:&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# concrete result&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;counter_example_k1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;counter_example_k1&lt;/span&gt;
&lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;counter_example_k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;counter_example_k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_invert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;counter_example_k2&lt;/span&gt;
&lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# let's check the failing condition&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;counter_example_k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What is the problem here? We didn't tell Z3 that &lt;code&gt;n1&lt;/code&gt; was supposed to be a
member of &lt;code&gt;k1&lt;/code&gt;. We can add this as a precondition to the solver, and then the
prove works:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; solver.add(k1.contains(n1))
&amp;gt;&amp;gt;&amp;gt;&amp;gt; prove(k2.contains(n2)) # works!
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is super cool! It's really a proof about the actual implementation, because
we call the implementation methods directly, and due to the operator overloading
that Z3 does we can be sure that we are actually checking a formula that
corresponds to the Python code. This eliminates one source of errors in formal
methods.&lt;/p&gt;
&lt;p&gt;Doing the proof manually on the Python REPL is kind of annoying though, and we
also would like to make sure that the proofs are re-done when we change the
code. What we would really like to do is writing the proofs as a unit-test that
we can run while developing and in CI. Doing this is possible, and the unit
tests that really perform proofs look pleasingly similar to the
Hypothesis-based ones.&lt;/p&gt;
&lt;p&gt;First we need to set up a bit of infrastructure:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;BitVecVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BitVecVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;z3_setup_variables&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# instantiate a solver&lt;/span&gt;
    &lt;span class="n"&gt;solver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Solver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# a Z3 variable for the first concrete value&lt;/span&gt;
    &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"n1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# a KnownBits instances that uses Z3 variables as its ones and unknowns,&lt;/span&gt;
    &lt;span class="c1"&gt;# representing the first abstract value&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"n1_ones"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"n1_unkowns"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# add the precondition to the solver that the concrete value n1 must be a&lt;/span&gt;
    &lt;span class="c1"&gt;# member of the abstract value k1&lt;/span&gt;
    &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# a Z3 variable for the second concrete value&lt;/span&gt;
    &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"n2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# a KnownBits instances for the second abstract value&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"n2_ones"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"n2_unkowns"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# add the precondition linking n2 and k2 to the solver&lt;/span&gt;
    &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;z3res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Not&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;z3res&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unsat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;z3res&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sat&lt;/span&gt; &lt;span class="c1"&gt;# can't be timeout, we set no timeout&lt;/span&gt;
        &lt;span class="c1"&gt;# make the model with the counterexample global, to make inspecting the&lt;/span&gt;
        &lt;span class="c1"&gt;# bug easier when running pytest --pdb&lt;/span&gt;
        &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"n1=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, n2=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;counter_example_k1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_signed_long&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                                       &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_signed_long&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;counter_example_k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_signed_long&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                                       &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_signed_long&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"k1=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;counter_example_k1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, k2=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;counter_example_k2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"but &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="si"&gt;=}&lt;/span&gt;&lt;span class="s2"&gt; evaluates to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then we can write proof-unit-tests like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_z3_abstract_invert&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_setup_variables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_invert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;n1&lt;/span&gt;
    &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_z3_abstract_and&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_setup_variables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;n3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt;
    &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_z3_abstract_or&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_setup_variables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;n3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt;
    &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_z3_abstract_add&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_setup_variables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;n3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt;
    &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_z3_abstract_sub&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_setup_variables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;n3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt;
    &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It's possible to write a bit more Python-metaprogramming-magic and unify the
Hypothesis and Z3 tests into the same test definition.&lt;sup id="fnref:proof_bitwidths"&gt;&lt;a class="footnote-ref" href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#fn:proof_bitwidths"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3 id="cases-where-this-style-of-z3-proof-doesnt-work"&gt;Cases where this style of Z3 proof doesn't work&lt;/h3&gt;
&lt;p&gt;Unfortunately the approach described in the previous section only works for a
very small number of cases. It breaks down as soon as the &lt;code&gt;KnownBits&lt;/code&gt; methods
that we're calling contain any &lt;code&gt;if&lt;/code&gt; conditions (including hidden ones like
the short-circuiting &lt;code&gt;and&lt;/code&gt; and &lt;code&gt;or&lt;/code&gt; in Python). Let's look at an example and
implement &lt;code&gt;abstract_eq&lt;/code&gt;. &lt;code&gt;eq&lt;/code&gt; is supposed to be an operation that compares two
integers and returns &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;1&lt;/code&gt; if they are different or equal, respectively.
Implementing this in knownbits looks like this (with example and hypothesis
tests):&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;abstract_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# the result is a 0, 1, or ?&lt;/span&gt;

        &lt;span class="c1"&gt;# if they are both the same constant, they must be equal&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# check whether we have known disagreeing bits, then we know the result&lt;/span&gt;
        &lt;span class="c1"&gt;# is 0&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_disagrees&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# an unknown boolean&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;_disagrees&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# check whether the bits disagree in any place where both are known&lt;/span&gt;
        &lt;span class="n"&gt;both_known&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;knowns&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;knowns&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;both_known&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;both_known&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_eq&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'...?'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'...?'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'?'&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'0'&lt;/span&gt;

&lt;span class="nd"&gt;@given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_hypothesis_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;
    &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Trying to do the proof in the same style as before breaks:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;stdin&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;"knownbits.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;246&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;abstract_eq&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_disagrees&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;"venv/site-packages/z3/z3.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;381&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="fm"&gt;__bool__&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;Z3Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Symbolic expressions cannot be cast to concrete Boolean values."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z3types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Z3Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Symbolic&lt;/span&gt; &lt;span class="n"&gt;expressions&lt;/span&gt; &lt;span class="n"&gt;cannot&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;concrete&lt;/span&gt; &lt;span class="n"&gt;Boolean&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We cannot call &lt;code&gt;abstract_eq&lt;/code&gt; on a &lt;code&gt;KnownBits&lt;/code&gt; with Z3 variables as fields,
because once we hit an &lt;code&gt;if&lt;/code&gt; statement, the whole approach of relying on the
operator overloading breaks down. Z3 doesn't actually parse the Python code or
anything advanced like that, we rather build an expression only by running the
code and letting the Z3 formulas build up.&lt;/p&gt;
&lt;p&gt;To still prove the correctness of &lt;code&gt;abstract_eq&lt;/code&gt; we need to manually transform
the control flow logic of the function into a Z3 formula that uses the &lt;code&gt;z3.If&lt;/code&gt;
expression, using a small helper function:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;z3_cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trueval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;falseval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;If&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BitVecVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trueval&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;BitVecVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;falseval&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;z3_abstract_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# follow the *logic* of abstract_eq, we can't call it due to the ifs in it&lt;/span&gt;
    &lt;span class="n"&gt;case1cond&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;case2cond&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_disagrees&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# ones is 1 in the first case, 0 otherwise&lt;/span&gt;
    &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;case1cond&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# in the first two cases, unknowns is 0, 1 otherwise&lt;/span&gt;
    &lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;case1cond&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;case2cond&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unknowns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_z3_abstract_eq_logic&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_setup_variables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;n3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# concrete result&lt;/span&gt;
    &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_abstract_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This proof works. It is a lot less satisfying than the previous ones though,
because we could have done an error in the manual transcription from Python code
to Z3 formulas (there are possibly more heavy-handed approaches where we do
this transformation more automatically using e.g. the &lt;code&gt;ast&lt;/code&gt; module to analyze
the source code, but that's a much more complicated researchy project). To
lessen this problem somewhat we can factor out the parts of the logic that don't
have any conditions into small helper methods (like &lt;code&gt;_disagrees&lt;/code&gt; in this
example) and use them in the manual conversion of the code to Z3 formulas.&lt;sup id="fnref:tests_vs_proofs"&gt;&lt;a class="footnote-ref" href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#fn:tests_vs_proofs"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The final condition that Z3 checks, btw, is this one:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;If(n1 == n2, 1, 0) &amp;amp;
~If(Or(And(n1_unkowns == 0,
           n2_unkowns == 0,
           n1_ones == n2_ones),
       n1_ones &amp;amp; ~n1_unkowns &amp;amp; ~n2_unkowns !=
       n2_ones &amp;amp; ~n1_unkowns &amp;amp; ~n2_unkowns),
    0, 1) ==
If(And(n1_unkowns == 0, n2_unkowns == 0, n1_ones == n2_ones),
   1, 0)
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="making-statements-about-precision"&gt;Making Statements about Precision&lt;/h3&gt;
&lt;p&gt;So far we have only used Z3 to prove statements about correctness, i.e. that
our abstract operations overapproximate what can happen with concrete values.
While proving this property is essential if we want to avoid miscompilation,
correctness alone is not a very strong constraint on the implementation of our
abstract transfer functions. We could simply return &lt;code&gt;Knownbits.unknowns()&lt;/code&gt; for
every &lt;code&gt;abstract_*&lt;/code&gt; method and the resulting overapproximation would be correct,
but useless in practice.&lt;/p&gt;
&lt;p&gt;It's much harder to make statements about whether the transfer functions are
maximally precise. There are two aspects of precision I want to discuss in this
section, however.&lt;/p&gt;
&lt;p&gt;The first aspect is that we would really like it if the transfer functions
compute the maximally precise results for singleton sets. If all abstract
arguments of an operations are constants, i.e. contain only a single concrete
element, then we know that the resulting set also has only a single element. We
can prove that all our transfer functions have this property:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_z3_prove_constant_folding&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_setup_variables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_invert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Implies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                     &lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Implies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
                     &lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Implies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
                     &lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Implies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
                     &lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_abstract_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Implies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
                     &lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Proving with Z3 that the transfer functions are maximally precise for
non-constant arguments seems to be relatively hard. I tried a few completely
rigorous approaches and failed. The paper &lt;a href="https://arxiv.org/pdf/2105.05398"&gt;Sound, Precise, and Fast Abstract
Interpretation with Tristate Numbers&lt;/a&gt;
contains an optimality proof for the transfer functions of addition and
subtraction, so we can be certain that they are as precise as is
possible.&lt;/p&gt;
&lt;p&gt;I still want to show an approach for trying to find concrete examples of
abstract values that are less precise than they could be, using a combination
of Hypothesis and Z3. The idea is to use hypothesis to pick random abstract
values. Then we compute the abstract result using our transfer function.
Afterwards we can ask Z3 to find us an abstract result that is better than the
one our transfer function produced. If Z3 finds a better abstract result, we
have a concrete example of imprecision for our transfer function. Those tests
aren't strict proofs, because they rely on generating random abstract values,
but they can still be valuable (not for the transfer functions in this blog
post, which are all optimal).&lt;/p&gt;
&lt;p&gt;Here is what the code looks like (this is a little bit bonus content, I'll not
explain the details and can only hope that the comments are somewhat helpful):&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nd"&gt;@given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_knownbits_and_contained_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@settings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deadline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_check_precision&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;
    &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;
    &lt;span class="c1"&gt;# apply transfer function&lt;/span&gt;
    &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abstract_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;example_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt;

    &lt;span class="c1"&gt;# try to find a better version of k3 with Z3&lt;/span&gt;
    &lt;span class="n"&gt;solver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Solver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;var1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'v1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;var2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'v2'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ones'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;unknowns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'unknowns'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;better_k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unknowns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# we're trying to find an example for a better k3, so we use check, without&lt;/span&gt;
    &lt;span class="c1"&gt;# negation:&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;# better_k3 should be a valid knownbits instance&lt;/span&gt;
        &lt;span class="n"&gt;better_k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_well_formed&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="c1"&gt;# it should be better than k3, ie there are known bits in better_k3&lt;/span&gt;
        &lt;span class="c1"&gt;# that we don't have in k3&lt;/span&gt;
        &lt;span class="n"&gt;better_k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;knowns&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;knowns&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;# now encode the correctness condition for better_k3 with a ForAll:&lt;/span&gt;
        &lt;span class="c1"&gt;# for all concrete values var1 and var2, it must hold that if&lt;/span&gt;
        &lt;span class="c1"&gt;# var1 is in k1 and var2 is in k2 it follows that var1 + var2 is in&lt;/span&gt;
        &lt;span class="c1"&gt;# better_k3&lt;/span&gt;
        &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;var1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;var2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Implies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var2&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="n"&gt;better_k3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;var2&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
    &lt;span class="c1"&gt;# if this query is satisfiable, we have found a better result for the&lt;/span&gt;
    &lt;span class="c1"&gt;# abstract_add&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;rk3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_signed_long&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unknowns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_signed_long&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"better"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rk3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It does not actually fail for &lt;code&gt;abstract_add&lt;/code&gt; (nor the other abstract
functions). To see the test failing we can add some imprecision to the
implementation of &lt;code&gt;abstract_add&lt;/code&gt; to see Hypothesis and Z3 find examples of
values that are not optimally precise (for example by setting some bits
of &lt;code&gt;unknowns&lt;/code&gt; in the implementation of &lt;code&gt;abstract_add&lt;/code&gt; unconditionally).&lt;/p&gt;
&lt;h3 id="using-the-abstract-domain-in-the-toy-optimizer-for-generalized-constant-folding"&gt;Using the Abstract Domain in the Toy Optimizer for Generalized Constant Folding&lt;/h3&gt;
&lt;p&gt;Now after all this work we can finally actually use the knownbits abstract
domain in the toy optimizer. The code for this follows &lt;a href="https://bernsteinbear.com/blog/toy-abstract-interpretation/"&gt;Max' intro post about
abstract interpretation&lt;/a&gt;
quite closely.&lt;/p&gt;
&lt;p&gt;For completeness sake, in the fold there's the basic infrastructure classes
that make up the IR again (they are identical or at least extremely close to
the previous toy posts).&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;toy infrastructure&lt;/summary&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;NotImplementedError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"abstract"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Operation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;forwarded&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Operation&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forwarded&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;
            &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;next&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;make_equal_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forwarded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;__getattr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;wraparg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;make_op&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Operation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;wraparg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;make_op&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;bb_to_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;varprefix&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;arg_to_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Constant&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;varnames&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;varnames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# give the operation a name used while&lt;/span&gt;
        &lt;span class="c1"&gt;# printing:&lt;/span&gt;
        &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;varprefix&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;varnames&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;
        &lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;", "&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;arg_to_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;strop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
        &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;



&lt;/details&gt;

&lt;p&gt;Now we can write some first tests, the first one simply checking constant
folding:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_constfold_two_ops&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;var0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;var1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;var2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;var3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;var0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;opt_bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simplify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;bb_to_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt_bb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"optvar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"""&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;optvar0 = getarg(0)&lt;/span&gt;
&lt;span class="s2"&gt;optvar1 = int_add(19, optvar0)"""&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Calling the transfer functions on constant &lt;code&gt;KnownBits&lt;/code&gt; produces a constant
results, as we have seen. Therefore "regular" constant folding should hopefully
be achieved by optimizing with the &lt;code&gt;KnownBits&lt;/code&gt; abstract domain too.&lt;/p&gt;
&lt;p&gt;The next two tests are slightly more complicated and can't be optimized by
regular constant-folding. They follow the motivating examples from the start of
this blog post, a hundred years ago:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test_constfold_via_knownbits&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;var0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;var1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;var2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;var3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dummy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;opt_bb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;simplify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;assert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bb_to_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt_bb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"optvar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"""&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;optvar0 = getarg(0)&lt;/span&gt;
&lt;span class="s2"&gt;optvar1 = int_or(optvar0, 1)&lt;/span&gt;
&lt;span class="s2"&gt;optvar2 = dummy(1)"""&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test_constfold_alignment_check&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;var0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;var1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_invert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;b111&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# mask off the lowest three bits, thus var2 is aligned&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;var2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;var1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# add 16 to aligned quantity&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;var3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# check alignment of result&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;var4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;b111&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;var5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# var5 should be const-folded to 1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;var6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dummy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;opt_bb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;simplify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;assert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bb_to_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt_bb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"optvar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"""&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;optvar0 = getarg(0)&lt;/span&gt;
&lt;span class="s2"&gt;optvar1 = int_and(optvar0, -8)&lt;/span&gt;
&lt;span class="s2"&gt;optvar2 = int_add(optvar1, 16)&lt;/span&gt;
&lt;span class="s2"&gt;optvar3 = dummy(1)"""&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here is &lt;code&gt;simplify&lt;/code&gt; to make these tests pass:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;unknown_transfer_functions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;abstract_args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all_unknown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;simplify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;abstract_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="c1"&gt;# dict mapping Operation to KnownBits&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;knownbits_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Constant&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;abstract_values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;opt_bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# apply the transfer function on the abstract arguments&lt;/span&gt;
        &lt;span class="n"&gt;name_without_prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;removeprefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"int_"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;method_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"abstract_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name_without_prefix&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;transfer_function&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unknown_transfer_functions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;abstract_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;knownbits_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;abstract_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;abstract_values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transfer_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;abstract_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# if the result is a constant, we optimize the operation away and make&lt;/span&gt;
        &lt;span class="c1"&gt;# it equal to the constant result&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;abstract_res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_equal_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;abstract_res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="c1"&gt;# otherwise emit the op&lt;/span&gt;
        &lt;span class="n"&gt;opt_bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;opt_bb&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The code follows the approach from the previous blog post very closely. The
only difference is that we apply the transfer function &lt;em&gt;first&lt;/em&gt;, to be able to
detect whether the abstract domain can tell us that the result has to always be
a constant. This code makes all three tests pass.&lt;/p&gt;
&lt;h3 id="using-the-knownbits-domain-for-conditional-peephole-rewrites"&gt;Using the &lt;code&gt;KnownBits&lt;/code&gt; Domain for Conditional Peephole Rewrites&lt;/h3&gt;
&lt;p&gt;So far we are only using the &lt;code&gt;KnownBits&lt;/code&gt; domain to find out that certain
operations have to produce a constant. We can also use the &lt;code&gt;KnownBits&lt;/code&gt; domain
to check whether certain operation rewrites are correct. Let's use one of the
examples from the &lt;a href="https://www.pypy.org/posts/2024/07/mining-jit-traces-missing-optimizations-z3.html"&gt;Mining JIT traces for missing optimizations with
Z3&lt;/a&gt;
post, where Z3 found the inefficiency &lt;code&gt;(x &amp;lt;&amp;lt; 4) &amp;amp; -0xf == x &amp;lt;&amp;lt; 4&lt;/code&gt; in PyPy JIT
traces. We don't have shift operations, but we want to generalize this optimization
anyway. The general form of this rewrite is that under some circumstances &lt;code&gt;x &amp;amp;
y == x&lt;/code&gt;, and we can use the &lt;code&gt;KnownBits&lt;/code&gt; domain to detect situations where this
must be true.&lt;/p&gt;
&lt;p&gt;To understand &lt;em&gt;when&lt;/em&gt; &lt;code&gt;x &amp;amp; y == x&lt;/code&gt; is true, we can think about individual pairs of
bits &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;. If &lt;code&gt;a == 0&lt;/code&gt;, then &lt;code&gt;a &amp;amp; b == 0 &amp;amp; b == 0 == a&lt;/code&gt;. If &lt;code&gt;b == 1&lt;/code&gt;
then &lt;code&gt;a &amp;amp; b == a &amp;amp; 1 == a&lt;/code&gt;. So if either &lt;code&gt;a == 0&lt;/code&gt; or &lt;code&gt;b == 1&lt;/code&gt; is true,
&lt;code&gt;a &amp;amp; b == a&lt;/code&gt; follows. And if either of these conditions is true for &lt;em&gt;all&lt;/em&gt; the
bits of &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;, we can know that &lt;code&gt;x &amp;amp; y == x&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can write a method on &lt;code&gt;KnownBits&lt;/code&gt; to check for this condition:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;is_and_identity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;""" Return True if n1 &amp;amp; n2 == n1 for any n1 in self and n2 in other.&lt;/span&gt;
&lt;span class="sd"&gt;        (or, equivalently, return True if n1 | n2 == n2)"""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zeros&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since my reasoning about this feels ripe for errors, let's check that our
understanding is correct with Z3:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_prove_is_and_identity&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_setup_variables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Implies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_and_identity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now let's use this in the toy optimizer. Here are two tests for this rewrite:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_remove_redundant_and&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;var0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;var1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_invert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mb"&gt;0b1111&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# mask off the lowest four bits&lt;/span&gt;
    &lt;span class="n"&gt;var2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;var1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# applying the same mask is not redundant&lt;/span&gt;
    &lt;span class="n"&gt;var3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;var1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;var4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dummy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;opt_bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simplify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;bb_to_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt_bb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"optvar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"""&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;optvar0 = getarg(0)&lt;/span&gt;
&lt;span class="s2"&gt;optvar1 = int_and(optvar0, -16)&lt;/span&gt;
&lt;span class="s2"&gt;optvar2 = dummy(optvar1)"""&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_remove_redundant_and_more_complex&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;var0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;var1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# var2 has bit pattern ????&lt;/span&gt;
    &lt;span class="n"&gt;var2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mb"&gt;0b1111&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# var3 has bit pattern ...?1111&lt;/span&gt;
    &lt;span class="n"&gt;var3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mb"&gt;0b1111&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# var4 is just var2&lt;/span&gt;
    &lt;span class="n"&gt;var4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;var3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;var5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dummy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;opt_bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simplify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;bb_to_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt_bb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"optvar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"""&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;optvar0 = getarg(0)&lt;/span&gt;
&lt;span class="s2"&gt;optvar1 = getarg(1)&lt;/span&gt;
&lt;span class="s2"&gt;optvar2 = int_and(optvar0, 15)&lt;/span&gt;
&lt;span class="s2"&gt;optvar3 = int_or(optvar1, 15)&lt;/span&gt;
&lt;span class="s2"&gt;optvar4 = dummy(optvar2)"""&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first test could also be made to pass by implementing a reassociation
optimization that turns &lt;code&gt;(x &amp;amp; c1) &amp;amp; c2&lt;/code&gt; into &lt;code&gt;x &amp;amp; (c1 &amp;amp; c2)&lt;/code&gt; and then constant-folds the second &lt;code&gt;and&lt;/code&gt;. But here we want to
use &lt;code&gt;KnownBits&lt;/code&gt; and conditionally rewrite &lt;code&gt;int_and&lt;/code&gt; to its first argument. So to make the tests pass,
we can change &lt;code&gt;simplify&lt;/code&gt; like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;simplify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;abstract_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="c1"&gt;# dict mapping Operation to KnownBits&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;knownbits_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="n"&gt;opt_bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# apply the transfer function on the abstract arguments&lt;/span&gt;
        &lt;span class="n"&gt;name_without_prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;removeprefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"int_"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;method_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"abstract_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name_without_prefix&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;transfer_function&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KnownBits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unknown_transfer_functions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;abstract_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;knownbits_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;abstract_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;abstract_values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transfer_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;abstract_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# if the result is a constant, we optimize the operation away and make&lt;/span&gt;
        &lt;span class="c1"&gt;# it equal to the constant result&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;abstract_res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_constant&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_equal_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;abstract_res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="c1"&gt;# &amp;lt;&amp;lt;&amp;lt;&amp;lt; new code&lt;/span&gt;
        &lt;span class="c1"&gt;# conditionally rewrite int_and(x, y) to x&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_and"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;abstract_args&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_and_identity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_equal_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="c1"&gt;# &amp;gt;&amp;gt;&amp;gt;&amp;gt; end changes&lt;/span&gt;
        &lt;span class="n"&gt;opt_bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;opt_bb&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And with that, the new tests pass as well. A real implementation would also
check the other argument order, but we leave that out for the sake of brevity.&lt;/p&gt;
&lt;p&gt;This rewrite also generalizes the &lt;a href="https://pypy.org/posts/2024/07/finding-simple-rewrite-rules-jit-z3.html"&gt;rewrites&lt;/a&gt; &lt;code&gt;int_and(0, x) -&amp;gt; 0&lt;/code&gt; and
&lt;code&gt;int_and(-1, x) -&amp;gt; x&lt;/code&gt;, let's add a test for those:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_remove_and_simple&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;var0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;var1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;var2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;var0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# == 0&lt;/span&gt;
    &lt;span class="n"&gt;var3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_invert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# == -1&lt;/span&gt;
    &lt;span class="n"&gt;var4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;var3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# == var1&lt;/span&gt;
    &lt;span class="n"&gt;var5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dummy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;opt_bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simplify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;bb_to_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt_bb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"optvar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"""&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;optvar0 = getarg(0)&lt;/span&gt;
&lt;span class="s2"&gt;optvar1 = getarg(1)&lt;/span&gt;
&lt;span class="s2"&gt;optvar2 = dummy(optvar1)"""&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This test just passes. And that's it for this post!&lt;/p&gt;
&lt;h3 id="conclusion"&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;In this post we've seen the implementation, testing and proofs about a 'known
bits' abstract domain, as well as its use in the toy optimizer to generalize
constant folding, and to implement conditional peephole rewrites.&lt;/p&gt;
&lt;p&gt;In the next posts I'll write about the real implementation of a knownbits
domain in PyPy's JIT, its combination with the existing interval abstract
domain, how to deal with gaining information from conditions in the program,
and some lose ends.&lt;/p&gt;
&lt;p&gt;Sources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/llvm/llvm-project/blob/main/llvm/lib/Support/KnownBits.cpp"&gt;Known bits in LLVM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/torvalds/linux/blob/master/kernel/bpf/tnum.c"&gt;Tristate numbers for known bits in Linux eBPF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/abs/2105.05398"&gt;Sound, Precise, and Fast Abstract Interpretation with Tristate Numbers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://people.cs.rutgers.edu/~sn349/papers/agni-cav2023.pdf"&gt;Verifying the Veriﬁer: eBPF Range Analysis Veriﬁcation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dougallj.wordpress.com/2020/01/13/bit-twiddling-addition-with-unknown-bits/"&gt;Bit-Twiddling: Addition with Unknown
  Bits&lt;/a&gt;
  is a super readable blog post by Dougall J. I've taken the &lt;code&gt;ones&lt;/code&gt; and
  &lt;code&gt;unknowns&lt;/code&gt; naming from this post, which I find significantly clearer than
  &lt;code&gt;value&lt;/code&gt; and &lt;code&gt;mask&lt;/code&gt;, which the Linux kernel uses.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bitmath.blogspot.com/"&gt;Bits, Math and Performance(?)&lt;/a&gt;, a fantastic
  blog by &lt;a href="https://mastodon.gamedev.place/@harold"&gt;Harold Aptroot&lt;/a&gt;. There are a
  lot of relevant posts about known bits, range analysis etc. Harold is also
  the author of &lt;a href="http://haroldbot.nl/"&gt;Haroldbot&lt;/a&gt;, a website that can be used
  for bitvector calculations, and also checks bitvector identities.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cea.hal.science/cea-01795779/document"&gt;Sharpening Constraint Programming approaches for Bit-Vector Theory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://users.cs.utah.edu/~regehr/papers/lctes06_2/fp019-regehr.pdf"&gt;Deriving Abstract Transfer Functions for Analyzing Embedded Software&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/abs/2105.00493"&gt;Synthesizing Abstract Transformers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:proof_bitwidths"&gt;
&lt;p&gt;There's a subtletly about the Z3 proofs that I'm sort of
glossing over here. Python integers are of arbitrary width, and the
&lt;code&gt;KnownBits&lt;/code&gt; code is actually carefully written to work for integers of any
size. This property is tested by the Hypothesis tests, which don't limit
the sizes of the generated random integers. However, the Z3 proofs only
check bitvectors of a fixed bitwidth of 64. There are various ways to deal
with this situation. For most "real" compilers, the bitwidth of integers
would be fixed anyway. Then the components &lt;code&gt;ones&lt;/code&gt; and &lt;code&gt;unknowns&lt;/code&gt; of the
&lt;code&gt;KnownBits&lt;/code&gt; class would use the number of bits the corresponding integer
variable has, and the Z3 proofs would use the same width. This is what we
do in the PyPy JIT. &lt;a class="footnote-backref" href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#fnref:proof_bitwidths" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:tests_vs_proofs"&gt;
&lt;p&gt;The less close connection between implementation and proof
for &lt;code&gt;abstract_eq&lt;/code&gt; is one of the reasons why it makes sense to do
unit-testing &lt;em&gt;in addition&lt;/em&gt; to proofs. For a more detailed explanation of
why both tests and proofs are good to
have, see &lt;a href="https://siek.blogspot.com/2024/06/data-structures-and-algorithms-correctly.html#correct-software-via-write-test-and-prove:~:text=We%20recognize%20that%20once%20step,detect%20most%20of%20the%20bugs"&gt;Jeremy Siek's blog
post&lt;/a&gt;,
as well as the &lt;a href="https://www-cs-faculty.stanford.edu/~knuth/faq.html#:~:text=What's%20the%20exact%20citation%20of%20your%20oft%2Dcited%20comment%20about%20bugs?"&gt;Knuth
quote&lt;/a&gt;. &lt;a class="footnote-backref" href="https://www.pypy.org/posts/2024/08/toy-knownbits.html#fnref:tests_vs_proofs" title="Jump back to footnote 2 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>toy-optimizer</category><category>z3</category><guid>https://www.pypy.org/posts/2024/08/toy-knownbits.html</guid><pubDate>Sat, 03 Aug 2024 14:00:00 GMT</pubDate></item><item><title>Mining JIT traces for missing optimizations with Z3</title><link>https://www.pypy.org/posts/2024/07/mining-jit-traces-missing-optimizations-z3.html</link><dc:creator>CF Bolz-Tereick</dc:creator><description>&lt;p&gt;In my last post I've described &lt;a href="https://www.pypy.org/posts/2024/07/finding-simple-rewrite-rules-jit-z3.html"&gt;how to use Z3 to find simple local peephole
optimization patterns&lt;/a&gt;
for the integer operations in PyPy's JIT. An example is &lt;code&gt;int_and(x, 0) -&amp;gt;
0&lt;/code&gt;. In this post I want to scale up the problem of identifying possible
optimizations to much bigger instruction sequences, also using Z3. For that, I
am starting with the JIT traces of &lt;strong&gt;real benchmarks&lt;/strong&gt;, after they have been
optimized by the optimizer of PyPy's JIT. Then we can ask Z3 to find
inefficient integer operations in those traces.&lt;/p&gt;
&lt;p&gt;Starting from the optimized traces of real programs has some big
advantages over the "classical" superoptimization approach of generating and
then trying all possible sequences of instructions. It avoids the
combinatorial explosion that happens with the latter approach. Also, starting
from the traces of benchmarks or (even better) actual programs makes sure that
we actually care about the missing optimizations
that are found in this way. And because the traces are analyzed after they have
been optimized by PyPy's optimizer, we only get reports for &lt;em&gt;missing&lt;/em&gt;
optimizations, that the JIT isn't able to do (yet).&lt;/p&gt;
&lt;p&gt;The techniques and experiments I describe in this post are again the result of
a bunch of discussions with John Regehr at a conference a few weeks ago, as
well as reading his blog posts and papers. Thanks John! Also thanks to &lt;a href="https://bernsteinbear.com/"&gt;Max
Bernstein&lt;/a&gt; for super helpful feedback on the drafts
of this blog post (and for poking me to write things in general).&lt;/p&gt;
&lt;h3 id="high-level-approach"&gt;High-Level Approach&lt;/h3&gt;
&lt;p&gt;The approach that I took works as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run benchmarks or other interesting programs and then dump the IR of the JIT
  traces into a file. The traces have at that point been already optimized by
  the PyPy JIT's optimizer.&lt;/li&gt;
&lt;li&gt;For every trace, ignore all the operations on non-integer variables.&lt;/li&gt;
&lt;li&gt;Translate every integer operation into a Z3 formula.&lt;/li&gt;
&lt;li&gt;For every operation, use Z3 to find out whether the operation is redundant
  (how that is done is described below).&lt;/li&gt;
&lt;li&gt;If the operation is redundant, the trace is less efficient than it could have
  been, because the optimizer could also have removed the operation. Report the
  inefficiency.&lt;/li&gt;
&lt;li&gt;Minimize the inefficient programs by removing as many operations as possible
  to make the problem easier to understand.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the post I will describe the details and show some pseudocode of the
approach. I'll also make the proper code public eventually (but it needs a
healthy dose of cleanups first).&lt;/p&gt;
&lt;h3 id="dumping-pypy-traces"&gt;Dumping PyPy Traces&lt;/h3&gt;
&lt;p&gt;PyPy will write its JIT traces into the file &lt;code&gt;out&lt;/code&gt; if the environment variable
&lt;a href="https://doc.pypy.org/man/pypy.1.html"&gt;&lt;code&gt;PYPYLOG&lt;/code&gt;&lt;/a&gt; is set as follows:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;PYPYLOG=jit-log-opt:out pypy &amp;lt;program.py&amp;gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This environment variable works for PyPy, but also for other virtual machines
built with RPython.&lt;/p&gt;
&lt;p&gt;(This is really a side point for the rest of the blog post, but since the
question came up I wanted to clarify it: Operations on integers in the Python
program that the JIT is running don't all correspond 1-to-1 with the &lt;code&gt;int_...&lt;/code&gt;
operations in the traces. The &lt;code&gt;int_...&lt;/code&gt; trace operations always operate on
machine words. The Python &lt;code&gt;int&lt;/code&gt; type supports arbitrarily large integers. PyPy
will optimistically try to lower the operations on Python integers into machine
word operations, but adds the necessary guards into the trace to make sure that
overflow outside of the range of machine words is caught. In case one of these
guards fails the interpreter switches to a big integer heap-allocated
representation.)&lt;/p&gt;
&lt;h3 id="encoding-traces-as-z3-formulas"&gt;Encoding Traces as Z3 formulas&lt;/h3&gt;
&lt;p&gt;The last blog post already contained the code to encode the results of
individual trace operations into Z3 formulas, so we don't need to repeat that
here. To encode traces of operations we introduce a Z3 variable for every
operation in the trace and then call the &lt;code&gt;z3_expression&lt;/code&gt; function for every
single one of the operations in the trace.&lt;/p&gt;
&lt;p&gt;For example, for the following trace:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;[i1]&lt;/span&gt;
&lt;span class="na"&gt;i2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;uint_rshift(i1, 32)&lt;/span&gt;
&lt;span class="na"&gt;i3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;int_and(i2, 65535)&lt;/span&gt;
&lt;span class="na"&gt;i4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;uint_rshift(i1, 48)&lt;/span&gt;
&lt;span class="na"&gt;i5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;int_lshift(i4, 16)&lt;/span&gt;
&lt;span class="na"&gt;i6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;int_or(i5, i3)&lt;/span&gt;
&lt;span class="na"&gt;jump(i6, i2) # equal&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We would get the Z3 formula:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;z3.And(i2 == LShR(i1, 32),
       i3 == i2 &amp;amp; 65535,
       i4 == LShR(i1, 48),
       i5 == i4 &amp;lt;&amp;lt; 16)
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Usually we won't ask for the formula of the whole trace at once. Instead we go
through the trace operation by operation and try to find inefficiencies in the
current one we are looking at. Roughly like this (pseudo-)code:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;newvar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;find_inefficiencies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;solver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Solver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;var_to_z3var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;input_argument&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inputargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;var_to_z3var&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;input_argument&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newz3var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_argument&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;var_to_z3var&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3resultvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newz3var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resultvarname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;z3arg0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;var_to_z3var&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;z3arg1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;var_to_z3var&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;z3arg1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
        &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z3arg0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z3arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# checking for inefficiencies, see the next sections&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"inefficient"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;

        &lt;span class="c1"&gt;# not inefficient, assert op into the solver and continue with the next op&lt;/span&gt;
        &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3resultvar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="c1"&gt;# no inefficiency found&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="identifying-constant-booleans-with-z3"&gt;Identifying constant booleans with Z3&lt;/h3&gt;
&lt;p&gt;To get started finding inefficiencies in a trace, we can
first focus on boolean variables. For every operation in the trace that
returns a bool we can ask Z3 to prove that this variable must be always True or
always False. Most of the time, neither of these proofs will succeed. But if Z3
manages to prove one of them, we know have found an ineffiency: instead of
computing the boolean result (eg by executing a comparison) the JIT's optimizer
could have replaced the operation with the corresponding boolean constant.&lt;/p&gt;
&lt;p&gt;Here's an example of an inefficiency found that way: if &lt;code&gt;x &amp;lt; y&lt;/code&gt; and &lt;code&gt;y &amp;lt; z&lt;/code&gt; are
both true, PyPy's JIT could conclude that &lt;code&gt;x &amp;lt; z&lt;/code&gt; must also
be true. However, currently the JIT cannot make that conclusion because it
only reasons about the concrete ranges (lower and upper bounds) for every
integer variable, but it has no way to remember anything about relationships
between different variables. This kind of reasoning would quite often be useful
to remove list/string bounds checks. Here's a &lt;a href="https://www.youtube.com/watch?app=desktop&amp;amp;v=1hm5ZVmBEvo"&gt;talk about how LLVM does
this&lt;/a&gt; (but it might be
too heavyweight for a JIT setting).&lt;/p&gt;
&lt;p&gt;Here are some more examples found that way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;x - 1 == x&lt;/code&gt; is always False&lt;/li&gt;
&lt;li&gt;&lt;code&gt;x - (x == -1) == -1&lt;/code&gt; is always False. The pattern &lt;code&gt;x - (x == -1)&lt;/code&gt; happens a
  lot in PyPy's hash computations: To be compatible with the CPython hashes we
  need to make sure that no object's hash is -1 (CPython uses -1 as an error
  value on the C level).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's pseudo-code for how to implement checking boolean operations for
inefficiencies:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;find_inefficiencies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z3arg0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z3arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# check for boolean constant result&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_boolean_result&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"inefficient"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"inefficient"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="c1"&gt;# checking for other inefficiencies, see the next sections&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

        &lt;span class="c1"&gt;# not inefficient, add op to the solver and continue with the next op&lt;/span&gt;
        &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3resultvar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="c1"&gt;# no inefficiency found&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="identifying-redundant-operations"&gt;Identifying redundant operations&lt;/h3&gt;
&lt;p&gt;A more interesting class of redundancy is to try to find two operations in a
trace that compute the same result. We can do that by asking Z3 to prove for
each pair of different operations in the trace to prove that the result is
always the same. If a previous operation returns the same result, the JIT could
have reused that result instead of re-computing it, saving time. Doing this
search for equivalent operations with Z3 is quadratic in the number of
operations, but since traces have a maximum length it is not too bad in
practice.&lt;/p&gt;
&lt;p&gt;This is the real workhorse of my script so far, it's what finds most of the
inefficiencies. Here's a few examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The very first and super useful example the script found is &lt;code&gt;int_eq(b, 1) ==
  b&lt;/code&gt; if &lt;code&gt;b&lt;/code&gt; is known to be a boolean (ie and integer 0 or 1). I have already
  implemented this optimization in the JIT.&lt;/li&gt;
&lt;li&gt;Similarly, &lt;code&gt;int_and(b, 1) == b&lt;/code&gt; for booleans.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(x &amp;lt;&amp;lt; 4) &amp;amp; -0xf == x &amp;lt;&amp;lt; 4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;((x &amp;gt;&amp;gt; 63) &amp;lt;&amp;lt; 1) &amp;lt;&amp;lt; 2) &amp;gt;&amp;gt; 3 == x &amp;gt;&amp;gt; 63&lt;/code&gt;. In general the JIT is quite bad at
  optimizing repeated shifts (the infrastructure for doing better with that is
  already in place, so this will be a relatively easy fix).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(x &amp;amp; 0xffffffff) | ((x &amp;gt;&amp;gt; 32) &amp;lt;&amp;lt; 32) == x&lt;/code&gt;. Having the JIT optimize this
  would maybe require first recognizing that &lt;code&gt;(x &amp;gt;&amp;gt; 32) &amp;lt;&amp;lt; 32&lt;/code&gt; can be expressed
  as a mask: &lt;code&gt;(x &amp;amp; 0xffffffff00000000)&lt;/code&gt;, and then using &lt;code&gt;(x &amp;amp; c1) | (x &amp;amp; c2) ==
  x &amp;amp; (c1 | c2)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A commonly occurring pattern is variations of this one:
  &lt;code&gt;((x &amp;amp; 1345) ^ 2048) - 2048 == x &amp;amp; 1345&lt;/code&gt; (with different constants, of
  course). xor is add without carry, and &lt;code&gt;x &amp;amp; 1345&lt;/code&gt; does not have the bit
  &lt;code&gt;2048&lt;/code&gt; set. Therefore the &lt;code&gt;^ 2048&lt;/code&gt; is equivalent to &lt;code&gt;+ 2048&lt;/code&gt;, which the &lt;code&gt;-
  2048&lt;/code&gt; cancels. More generally, if &lt;code&gt;a &amp;amp; b == 0&lt;/code&gt;, then &lt;code&gt;a + b == a | b == a ^ b&lt;/code&gt;.
  I don't understand at all why this appears so often in the traces, but I
  see variations of it a lot. LLVM can optimize this, but &lt;a href="https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115829"&gt;GCC
  can't&lt;/a&gt;, thanks to
  &lt;a href="https://hachyderm.io/@pinskia/112752641328799157"&gt;Andrew Pinski for filing the
  bug&lt;/a&gt;!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And here's some implementation pseudo-code again:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;find_inefficiencies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z3arg0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z3arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# check for boolean constant result&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="c1"&gt;# searching for redundant operations&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;previous_op&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;previous_op&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="c1"&gt;# done, reached the current op&lt;/span&gt;
            &lt;span class="n"&gt;previous_op_z3var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;var_to_z3var&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;previous_op&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;previous_op_z3var&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"inefficient"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;previous_op&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="c1"&gt;# more code here later&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

        &lt;span class="c1"&gt;# not inefficient, add op to the solver and continue with the next op&lt;/span&gt;
        &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3resultvar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="c1"&gt;# no inefficiency found&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="synthesizing-more-complicated-constants-with-exists-forall"&gt;Synthesizing more complicated constants with exists-forall&lt;/h3&gt;
&lt;p&gt;To find out whether some integer operations always return a constant result, we
can't simply use the same trick as for those operations that return boolean
results, because enumerating 2⁶⁴ possible constants and checking them all
would take too long. Like in the last post, we can use &lt;code&gt;z3.ForAll&lt;/code&gt; to find out
whether Z3 can synthesize a constant for the result of an operation for us.
If such a constant exists, the JIT could have removed the operation,
and replaced it with the constant that Z3 provides.&lt;/p&gt;
&lt;p&gt;Here a few examples of inefficiencies found this way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;(x ^ 1) ^ x == 1&lt;/code&gt; (or, more generally: &lt;code&gt;(x ^ y) ^ x == y&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;if &lt;code&gt;x | y == 0&lt;/code&gt;, it follows that &lt;code&gt;x == 0&lt;/code&gt; and &lt;code&gt;y == 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;if &lt;code&gt;x != MAXINT&lt;/code&gt;, then &lt;code&gt;x + 1 &amp;gt; x&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Implementing this is actually slightly annoying. The &lt;code&gt;solver.add&lt;/code&gt; calls for
non-inefficient ops add assertions to the solver, which are now confusing the
&lt;code&gt;z3.ForAll&lt;/code&gt; query. We could remove all assertion from the solver, then do the
&lt;code&gt;ForAll&lt;/code&gt; query, then add the assertions back. What I ended doing instead was
instantiating a second solver object that I'm using for the &lt;code&gt;ForAll&lt;/code&gt; queries,
that remains empty the whole time.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;find_inefficiencies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;solver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Solver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;empty_solver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Solver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;var_to_z3var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z3arg0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z3arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# check for boolean constant result&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="c1"&gt;# searching for redundant operations&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="c1"&gt;# checking for constant results&lt;/span&gt;
        &lt;span class="n"&gt;constvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'find_const'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;condition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;var_to_z3var&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Implies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;constvar&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;empty_solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;empty_solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;constvar&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_signed_long&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"inefficient"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt;

        &lt;span class="c1"&gt;# not inefficient, add op to the solver and continue with the next op&lt;/span&gt;
        &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3resultvar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="c1"&gt;# no inefficiency found&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="minimization"&gt;Minimization&lt;/h3&gt;
&lt;p&gt;Analyzing an inefficiency by hand in the context of a larger trace is quite
tedious. Therefore I've implemented a (super inefficient) script to try to make
the examples smaller. Here's how that works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First throw out all the operations that occur &lt;em&gt;after&lt;/em&gt; the inefficient operation
  in the trace.&lt;/li&gt;
&lt;li&gt;Then we remove all "dead" operations, ie operations that don't have their
  results used (all the operations that we can analyze with Z3 are without side
  effects).&lt;/li&gt;
&lt;li&gt;Now we try to remove every guard in the trace one by one and check
  afterwards, whether the resulting trace still has an inefficiency.&lt;/li&gt;
&lt;li&gt;We also try to replace every single operation with a new argument to the
  trace, to see whether the inefficiency is still present.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The minimization process is sort of inefficient and I should probably be using
 &lt;a href="https://github.com/DRMacIver/shrinkray"&gt;shrinkray&lt;/a&gt; or
 &lt;a href="https://github.com/csmith-project/creduce"&gt;C-Reduce&lt;/a&gt; instead. However, it
 seems to work well in practice and the runtime isn't too bad.&lt;/p&gt;
&lt;h3 id="results"&gt;Results&lt;/h3&gt;
&lt;p&gt;So far I am using the JIT traces of three programs: 1) Booting Linux on the
&lt;a href="https://docs.pydrofoil.org"&gt;Pydrofoil&lt;/a&gt; RISC-V emulator, 2) booting Linux on the Pydrofoil ARM emulator, and 3)
running the PyPy bootstrap process on top of PyPy.&lt;/p&gt;
&lt;p&gt;I picked these programs because most Python programs don't contain interesting
amounts of integer operations, and the traces of the emulators
contain a lot of them. I also used the bootstrap process because I still wanted
to try a big Python program and personally care about the runtime of this
program a lot.&lt;/p&gt;
&lt;p&gt;The script identifies 94
inefficiencies in the traces, a lot of them come from repeating
patterns. My next steps will be to manually inspect them all, categorize them, and
implement easy optimizations identified that way. I also want a way to sort the
examples by execution count in the benchmarks, to get a feeling for which of
them are most important.&lt;/p&gt;
&lt;p&gt;I didn't investigate the full set of &lt;a href="https://speed.pypy.org"&gt;Python
benchmarks&lt;/a&gt; that PyPy uses yet, because I don't expect
them to contain interesting amounts of integer operations, but maybe I am wrong
about that? Will have to try eventually.&lt;/p&gt;
&lt;h3 id="conclusion"&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This was again much easier to do than I would have expected! Given that I had
the translation of trace ops to Z3 already in place, it was a matter of about a
day's of programming to use this infrastructure to find the first problems and
minimizing them.&lt;/p&gt;
&lt;p&gt;Reusing the results of existing operations or replacing operations by constants
can be seen as "zero-instruction superoptimization". I'll probably be rather
busy for a while to add the missing optimizations identified by my simple
script. But later extensions to actually synthesize one or several operations
in the attempt to optimize the traces more and find more opportunities should
be possible.&lt;/p&gt;
&lt;p&gt;Finding inefficiencies in traces with Z3 is significantly less
annoying and also less error-prone than just manually inspecting traces and
trying to spot optimization opportunities.&lt;/p&gt;
&lt;h3 id="random-notes-and-sources"&gt;Random Notes and Sources&lt;/h3&gt;
&lt;p&gt;Again, John's blog posts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.regehr.org/archives/1109"&gt;Let’s Work on an LLVM Superoptimizer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.regehr.org/archives/1146"&gt;Early Superoptimizer Results&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.regehr.org/archives/1252"&gt;A Few Synthesizing Superoptimizer Results&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.regehr.org/archives/1636"&gt;Synthesizing Constants&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;and papers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/pdf/1711.04422"&gt;A Synthesizing Superoptimizer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/pdf/10.1145/3649837"&gt;Hydra: Generalizing Peephole Optimizations with Program Synthesis&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I remembered recently that I had seen the approach of optimizing the traces of
a tracing JIT with Z3 a long time ago, as part of the (now long dead, I think)
&lt;a href="https://web.archive.org/web/20160304055149/http://research.microsoft.com/en-us/projects/spur/"&gt;SPUR
project&lt;/a&gt;.
There's a &lt;a href="https://web.archive.org/web/20161029162737/http://csl.stanford.edu/~christos/pldi2010.fit/tillmann.provers4jit.pdf"&gt;workshop
paper&lt;/a&gt;
from 2010 about this. SPUR was trying to use Z3 built into the actual JIT (as
opposed to using Z3 only to find places where the regular optimizers could be
improved). In addition to bitvectors, SPUR also used the Z3 support for arrays
to model the C# heap and remove redundant stores. This is still another future
extension for all the Z3 work I've been doing in the context of the PyPy JIT.&lt;/p&gt;</description><category>jit</category><category>z3</category><guid>https://www.pypy.org/posts/2024/07/mining-jit-traces-missing-optimizations-z3.html</guid><pubDate>Fri, 19 Jul 2024 17:01:09 GMT</pubDate></item><item><title>Finding Simple Rewrite Rules for the JIT with Z3</title><link>https://www.pypy.org/posts/2024/07/finding-simple-rewrite-rules-jit-z3.html</link><dc:creator>CF Bolz-Tereick</dc:creator><description>&lt;p&gt;In June I was at the &lt;a href="https://pldi24.sigplan.org/"&gt;PLDI conference&lt;/a&gt; in
Copenhagen to present a &lt;a href="https://dl.acm.org/doi/10.1145/3652588.3663316"&gt;paper&lt;/a&gt;
I co-authored with &lt;a href="https://bernsteinbear.com/"&gt;Max Bernstein&lt;/a&gt;. I also finally
met &lt;a href="https://blog.regehr.org/"&gt;John Regehr&lt;/a&gt;, who I'd been talking on social
media for ages but had never met. John has been working on compiler correctness
and better techniques for building compilers and optimizers since a very long
time. The blog post &lt;a href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html"&gt;Finding JIT Optimizer Bugs using SMT Solvers and
Fuzzing&lt;/a&gt;
was heavily inspired by this work. We talked a lot about his and his groups
work on using Z3 for
&lt;a href="https://en.wikipedia.org/wiki/Superoptimization"&gt;superoptimization&lt;/a&gt; and for
finding missing optimizations. I have applied some of the things John told me
about to the traces of PyPy's JIT, and wanted to blog about that. However, my
draft felt quite hard to understand. Therefore I have now written this current
post, to at least try to provide a somewhat gentler on-ramp to the topic.&lt;/p&gt;
&lt;p&gt;In &lt;em&gt;this&lt;/em&gt; post we will use the Python-API to Z3 to find local peephole rewrite
rules for the operations in the intermediate representation of PyPy's tracing
JIT. The code for this is simple enough that we can go through all of it.&lt;/p&gt;
&lt;p&gt;The PyPy JIT produces traces of machine level instructions, which are optimized
and then turned into machine code. The optimizer uses a number of approaches to
make the traces more efficient. For integer operations it applies a number of
arithmetic simplification rules rules, for example &lt;code&gt;int_add(x, 0) -&amp;gt; x&lt;/code&gt;. When
implementing these rules in the JIT there are &lt;strong&gt;two problems&lt;/strong&gt;: How do we know
that the rules are correct? And how do we know that we haven't forgotten any
rules? We'll try to answer both of these, but the first one in particular.&lt;/p&gt;
&lt;p&gt;We'll be using Z3, a satisfiability module theories (SMT) solver which has good
bitvector support and most importantly an excellent Python API. We can use the
solver to reason about bitvectors, which are how we will model machine
integers.&lt;/p&gt;
&lt;p&gt;To find rewrite rules, we will consider the binary operations (i.e. those
taking two arguments) in PyPy traces that take and produce integers. The
completely general form &lt;code&gt;op(x, y)&lt;/code&gt; is not simplifiable on its own. But if
either &lt;code&gt;x == y&lt;/code&gt;
or if one of the arguments is a constant, we can potentially simplify the
operation into a simpler form. The results are either the variable &lt;code&gt;x&lt;/code&gt;, or a
(potentially different) constant. We'll ignore constant-folding where both
arguments of the binary operation are constants. The possible results for a
simplifiable binary operation are the variable &lt;code&gt;x&lt;/code&gt; or another constant. This
leaves the following patterns as possibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;op(x, x) == x&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;op(x, x) == c1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;op(x, c1) == x&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;op(c1, x) == x&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;op(x, c1) == c2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;op(c1, x) == c2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our approach will be to take every single supported binary integer operation,
instantiate all of these patterns, and try to ask Z3 whether the resulting
simplification is valid for all values of &lt;code&gt;x&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="quick-intro-to-the-z3-python-api"&gt;Quick intro to the Z3 Python-API&lt;/h3&gt;
&lt;p&gt;Here's a terminal session showing the use of the Z3 Python API:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; import z3&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; # construct a Z3 bitvector variable of width 8, with name x:&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; x = z3.BitVec('x', 8)&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; # construct a more complicated formula by using operator overloading:&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; x + x&lt;/span&gt;
&lt;span class="go"&gt;x + x&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; x + 1&lt;/span&gt;
&lt;span class="go"&gt;x + 1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Z3 checks the "satisfiability" of a formula. This means that it tries to find
an example set of concrete values for the variables that occur in a formula,
such that the formula becomes true. Examples:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; solver = z3.Solver()&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; solver.check(x * x == 3)&lt;/span&gt;
&lt;span class="go"&gt;unsat&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; # meaning no x fulfils this property&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; solver.check(x * x == 9)&lt;/span&gt;
&lt;span class="go"&gt;sat&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; model = solver.model()&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; model&lt;/span&gt;
&lt;span class="go"&gt;[x = 253]&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; model[x].as_signed_long()&lt;/span&gt;
&lt;span class="go"&gt;-3&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; # 253 is the same as -3 in two's complement arithmetic with 8 bits&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In order to use Z3 to prove something, we can ask Z3 to find counterexamples
for the statement, meaning concrete values that would make the negation of the
statement true:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; solver.check(z3.Not(x ^ -1 == ~x))&lt;/span&gt;
&lt;span class="go"&gt;unsat&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The result &lt;code&gt;unsat&lt;/code&gt; means that we just proved that &lt;code&gt;x ^ -1 == ~x&lt;/code&gt; is true for
all &lt;code&gt;x&lt;/code&gt;, because there is no value for &lt;code&gt;x&lt;/code&gt; that makes &lt;code&gt;not (x ^ -1 == ~x)&lt;/code&gt;
true (this works because -1 has all the bits set).&lt;/p&gt;
&lt;p&gt;If we try to prove something incorrect in this way, the following happens:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; solver.check(z3.Not(x ^ -1 == x))&lt;/span&gt;
&lt;span class="go"&gt;sat&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;sat&lt;/code&gt; shows that &lt;code&gt;x ^ -1 == x&lt;/code&gt; is (unsurprisingly) not always true, and we can
ask for a counterexample:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; solver.model()&lt;/span&gt;
&lt;span class="go"&gt;[x = 0]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This way of proving this works because the &lt;code&gt;check&lt;/code&gt; calls try to solve an
(implicit) "exists" quantifier, over all the Z3 variables used in the formula.
&lt;code&gt;check&lt;/code&gt; will either return &lt;code&gt;z3.unsat&lt;/code&gt;, which means that no concrete values make
the formula true; or &lt;code&gt;z3.sat&lt;/code&gt;, which means that you can get some concrete
values that make the formula true by calling &lt;code&gt;solver.model()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In math terms we prove things using &lt;code&gt;check&lt;/code&gt; by de-Morgan's rules for quantifiers:&lt;/p&gt;
&lt;p&gt;$$ \lnot \exists x: \lnot f(x) \implies \forall x: f(x) $$&lt;/p&gt;
&lt;p&gt;Now that we've seen the basics of using the Z3 API on a few small examples,
we'll use it in a bigger program.&lt;/p&gt;
&lt;h3 id="encoding-the-integer-operations-of-rpythons-jit-into-z3-formulas"&gt;Encoding the integer operations of RPython's JIT into Z3 formulas&lt;/h3&gt;
&lt;p&gt;Now we'll use the API to reason about the integer operations of the PyPy JIT
intermediate representation (IR). The binary integer operations are:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;opnames2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="s2"&gt;"int_add"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_sub"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_mul"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_or"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_xor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_eq"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_ne"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_lt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_le"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_gt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_ge"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"uint_lt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"uint_le"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"uint_gt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"uint_ge"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_lshift"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_rshift"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"uint_rshift"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"uint_mul_high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_pydiv"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_pymod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There's not much special about the integer operations. Like in LLVM, most of
them are signedness-independent: &lt;code&gt;int_add&lt;/code&gt;, &lt;code&gt;int_sub&lt;/code&gt;, &lt;code&gt;int_mul&lt;/code&gt;, ... work
correctly for unsigned integers but also for
&lt;a href="https://en.wikipedia.org/wiki/Two%27s_complement"&gt;two's-complement&lt;/a&gt; signed
integers. Exceptions for that are order comparisons like &lt;code&gt;int_lt&lt;/code&gt; etc. for
which we have unsigned variants &lt;code&gt;uint_lt&lt;/code&gt; etc. All operations that produce a
boolean result return a full-width integer &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;1&lt;/code&gt; (the PyPy JIT supports
only word-sized integers in its intermediate representation)&lt;/p&gt;
&lt;p&gt;In order to reason about the IR operations, some ground work:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;z3&lt;/span&gt;

&lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;
&lt;span class="n"&gt;solver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Solver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# milliseconds, ie 10s&lt;/span&gt;
&lt;span class="n"&gt;xvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'x'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;constvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'const'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;constvar2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'const2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;TRUEBV&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BitVecVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;FALSEBV&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BitVecVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And here's the a function to turn an integer IR operation of PyPy's JIT into Z3
formulas:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;""" computes a tuple of (result, valid_if) of Z3 formulas. `result` is the&lt;/span&gt;
&lt;span class="sd"&gt;    formula representing the result of the operation, given argument formulas&lt;/span&gt;
&lt;span class="sd"&gt;    arg0 and arg1. `valid_if` is a pre-condition that must be true for the&lt;/span&gt;
&lt;span class="sd"&gt;    result to be meaningful. """&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt; &lt;span class="c1"&gt;# the precondition is mostly True, with few exceptions&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_add"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_mul"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_and"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_or"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_xor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_eq"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_ne"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_lt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_le"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_gt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_ge"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"uint_lt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ULT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"uint_le"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ULE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"uint_gt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UGT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"uint_ge"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UGE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_lshift"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
        &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_rshift"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
        &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"uint_rshift"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LShR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"uint_mul_high"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# zero-extend args to 2*INTEGER_WIDTH bit, then multiply and extract&lt;/span&gt;
        &lt;span class="c1"&gt;# highest INTEGER_WIDTH bits&lt;/span&gt;
        &lt;span class="n"&gt;zarg0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZeroExt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;zarg1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZeroExt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zarg0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;zarg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_pydiv"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
        &lt;span class="n"&gt;psubx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;If&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;psubx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;psubx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_pymod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;If&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INTEGER_WIDTH&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_is_true"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;FALSEBV&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_is_zero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;FALSEBV&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_neg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_invert"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"unknown operation "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3expr&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;""" helper function to turn a Z3 boolean result z3expr into a 1 or 0&lt;/span&gt;
&lt;span class="sd"&gt;    bitvector, using z3.If """&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;If&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TRUEBV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FALSEBV&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We map the semantics of a PyPy JIT operation to Z3 with the &lt;code&gt;z3_expression&lt;/code&gt;
function. It takes the name of a JIT operation and its two (or one) arguments
into a pair of Z3 formulas, &lt;code&gt;result&lt;/code&gt; and &lt;code&gt;valid_if&lt;/code&gt;. The resulting formulas are
constructed with the operator overloading of Z3 variables/formulas.&lt;/p&gt;
&lt;p&gt;The first element &lt;code&gt;result&lt;/code&gt; of the result of &lt;code&gt;z3_expression&lt;/code&gt; represents the result
of performing the operation. &lt;code&gt;valid_if&lt;/code&gt; is a bool that represents a condition that
needs to be &lt;code&gt;True&lt;/code&gt; in order for the result of the operation to be defined. E.g.
&lt;code&gt;int_pydiv(a, b)&lt;/code&gt; is only valid if &lt;code&gt;b != 0&lt;/code&gt;. Most operations are always valid,
so they return &lt;code&gt;True&lt;/code&gt; as that condition (we'll ignore &lt;code&gt;valid_if&lt;/code&gt; for a bit, but it
will become more relevant further down in the post).&lt;/p&gt;
&lt;p&gt;We can define a helper function to prove things by finding counterexamples:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;""" Try to prove a condition cond by searching for counterexamples of its negation. """&lt;/span&gt;
    &lt;span class="n"&gt;z3res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Not&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;z3res&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unsat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;z3res&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# eg on timeout&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;z3res&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"should be unreachable"&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="finding-rewrite-rules"&gt;Finding rewrite rules&lt;/h3&gt;
&lt;p&gt;Now we can start finding our first rewrite rules, following the first pattern
&lt;code&gt;op(x, x) -&amp;gt; x&lt;/code&gt;. We do this by iterating over all the supported binary
operation names, getting the z3 expression for &lt;code&gt;op(x, x)&lt;/code&gt; and then asking Z3 to
prove &lt;code&gt;op(x, x) == x&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;opnames2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;(x, x) -&amp;gt; x, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This yields the simplifications:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;int_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="synthesizing-constants"&gt;Synthesizing constants&lt;/h3&gt;
&lt;p&gt;Supporting the next patterns is harder: &lt;code&gt;op(x, x) == c1&lt;/code&gt;, &lt;code&gt;op(x, c1) == x&lt;/code&gt;, and
&lt;code&gt;op(c1, x) == x&lt;/code&gt;. We don't know which constants to pick to try to get Z3 to
prove the equality. We could iterate over common constants like &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;,
&lt;code&gt;MAXINT&lt;/code&gt;, etc, or even over all the 256 values for a bitvector of length 8.
However, we will instead ask Z3 to find the constants for us too.&lt;/p&gt;
&lt;p&gt;This can be done by using quantifiers, in this case &lt;code&gt;z3.ForAll&lt;/code&gt;. The query we
pose to Z3 is "does there exist a constant &lt;code&gt;c1&lt;/code&gt; such that for all &lt;code&gt;x&lt;/code&gt; the
following is true: &lt;code&gt;op(x, c1) == x&lt;/code&gt;? Note that the constant &lt;code&gt;c1&lt;/code&gt; is not
necessarily unique, there could be many of them. We generate several matching
constant, and add that they must be different to the condition of the second
and further queries.&lt;/p&gt;
&lt;p&gt;We can express this in a helper function:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;find_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number_of_results&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;condition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;z3expr&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number_of_results&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;checkres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;checkres&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# if a solver check succeeds, we can ask for a model, which is&lt;/span&gt;
            &lt;span class="c1"&gt;# concrete values for the variables constvar&lt;/span&gt;
            &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;constvar&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_signed_long&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt;
            &lt;span class="c1"&gt;# make sure we don't generate the same constant again on the&lt;/span&gt;
            &lt;span class="c1"&gt;# next call&lt;/span&gt;
            &lt;span class="n"&gt;condition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;constvar&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# no (more) constants found&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can use this new function for the three mentioned patterns:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# try to find constants for op(x, x) == c&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;opnames2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;find_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;constvar&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;(x, x) -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# try to find constants for op(x, c) == x and op(c, x) == x&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;opnames2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constvar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;find_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;(x, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) -&amp;gt; x"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constvar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;find_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, x) -&amp;gt; x"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# this code is not quite correct, we'll correct it later&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Together this yields the following new simplifications:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="cp"&gt;# careful, these are not all correct!&lt;/span&gt;
&lt;span class="n"&gt;int_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_xor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;int_ne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_le&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;int_gt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_ge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;uint_lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;uint_le&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;uint_gt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;uint_ge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;uint_rshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_pymod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_mul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_mul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_xor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_xor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_lshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_rshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;uint_rshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_pydiv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_pymod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Most of these look good at first glance, but the last one reveals a problem:
we've been ignoring the &lt;code&gt;valid_if&lt;/code&gt; expression up to now. We can stop doing that by
changing the code like this, which adds &lt;code&gt;z3.And(valid_if, ...)&lt;/code&gt; to the argument of
the calls to &lt;code&gt;find_constant&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# try to find constants for op(x, x) == c, op(x, c) == x and op(c, x) == x&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;opnames2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;find_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valid_if&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;constvar&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;(x, x) -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# try to find constants for op(x, c) == x and op(c, x) == x&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;opnames2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constvar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;find_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;(x, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) -&amp;gt; x"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constvar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;find_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, x) -&amp;gt; x"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And we get this list instead:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;int_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_xor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;int_ne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_le&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;int_gt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_ge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;uint_lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;uint_le&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;uint_gt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;uint_ge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;int_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_mul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_mul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_xor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_xor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_lshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_rshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;uint_rshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;int_pydiv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="synthesizing-two-constants"&gt;Synthesizing two constants&lt;/h3&gt;
&lt;p&gt;For the patterns &lt;code&gt;op(x, c1) == c2&lt;/code&gt; and &lt;code&gt;op(c1, x) == c2&lt;/code&gt; we need to synthesize
two constants. We can again write a helper method for that:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;find_2consts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number_of_results&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;condition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;z3expr&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number_of_results&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;checkres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;checkres&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;constvar&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_signed_long&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;const2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;constvar2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_signed_long&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;const2&lt;/span&gt;
            &lt;span class="n"&gt;condition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;constvar&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constvar2&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;const2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then use it like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;opnames2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# try to find constants c1, c2 such that op(c1, x) -&amp;gt; c2&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constvar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;consts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;find_2consts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valid_if&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;constvar2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;const2&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;consts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, x) -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;const2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# try to find constants c1, c2 such that op(x, c1) -&amp;gt; c2&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constvar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;consts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;find_2consts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valid_if&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;constvar2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;const2&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;consts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;(x, &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;) -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;const2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which yields some straightforward simplifications:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;int_mul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_mul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;uint_lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;uint_le&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;uint_gt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;uint_ge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;int_lshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_rshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;uint_rshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;uint_mul_high&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;uint_mul_high&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;uint_mul_high&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;uint_mul_high&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_pymod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_pymod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A few require a bit more thinking:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;int_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;int_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The are true because in two's complement, &lt;code&gt;-1&lt;/code&gt; has all bits set.&lt;/p&gt;
&lt;p&gt;The following ones require recognizing that &lt;code&gt;-9223372036854775808 == -2**63&lt;/code&gt; is
the most negative signed 64-bit integer, and &lt;code&gt;9223372036854775807 == 2 ** 63 -
1&lt;/code&gt; is the most positive one:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;int_lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9223372036854775807&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9223372036854775808&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_le&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9223372036854775808&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;int_le&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9223372036854775807&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;int_gt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9223372036854775808&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_gt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9223372036854775807&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;int_ge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9223372036854775807&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;int_ge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9223372036854775808&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The following ones are true because the bitpattern for &lt;code&gt;-1&lt;/code&gt; is the largest
unsigned number:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;uint_lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;uint_le&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;uint_gt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;uint_ge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="strength-reductions"&gt;Strength Reductions&lt;/h3&gt;
&lt;p&gt;All the patterns so far only had a variable or a constant on the target of the
rewrite. We can also use the machinery to do strengh-reductions where we
generate a single-argument operation &lt;code&gt;op1(x)&lt;/code&gt; for input operations &lt;code&gt;op(x, c1)&lt;/code&gt;
or &lt;code&gt;op(c1, x)&lt;/code&gt;. To achieve this, we try all combinations of binary and unary
operations. (We won't consider strength reductions where a binary operation
gets turned into a "cheaper" other binary operation here.)&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;opnames1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="s2"&gt;"int_is_true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_is_zero"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_neg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;"int_invert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;opnames2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;opname1&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;opnames1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constvar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# try to find a constant op(x, c) == g(x)&lt;/span&gt;
        &lt;span class="n"&gt;result1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opname1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;consts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;find_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valid_if&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;result1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;consts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;(x, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;opname1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;(x)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# try to find a constant op(c, x) == g(x)&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constvar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;result1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opname1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xvar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;consts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;find_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valid_if&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_if1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;result1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;consts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;opname&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, x) -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;opname1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;(x)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which yields the following new simplifications:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;int_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_neg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;int_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_invert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;int_mul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_neg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;int_mul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_neg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;int_xor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_invert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;int_xor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_invert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;int_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_is_zero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;int_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_is_zero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;int_ne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_is_true&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;int_ne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_is_true&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;uint_lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_is_true&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;uint_lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_is_zero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;uint_le&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_is_true&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;uint_le&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_is_zero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;uint_gt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_is_true&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;uint_gt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_is_zero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;uint_ge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_is_true&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;uint_ge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_is_zero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;int_pydiv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int_neg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="conclusions"&gt;Conclusions&lt;/h3&gt;
&lt;p&gt;With not very little code we managed to generate a whole lot of local
simplifications for integer operations in the IR of PyPy's JIT. The rules
discovered that way are "simple", in the sense that they only require looking
at a single instruction, and not where the arguments of that instruction came
from. They also don't require any knowledge about the properties of the
arguments of the instructions (e.g. that they are positive).&lt;/p&gt;
&lt;p&gt;The rewrites in this post have mostly been in PyPy's JIT already. But now we
mechanically confirmed that they are correct. I've also added the remaining
useful looking ones, in particular &lt;code&gt;int_eq(x, 0) -&amp;gt; int_is_zero(x)&lt;/code&gt; etc.&lt;/p&gt;
&lt;p&gt;If we wanted to scale this approach up, we would have to work much harder!
There are a bunch of problems that come with generalizing the approach to
looking at sequences of instructions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Combinatorial explosion: if we look at sequences of instructions, we very
  quickly get a combinatorial explosion and it becomes untractable to try all
  combinations.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finding non-minimal patterns: Some complicated simplifications can be
  instances of simpler ones. For example, because &lt;code&gt;int_add(x, 0) -&amp;gt; x&lt;/code&gt;, it's
  also true that &lt;code&gt;int_add(int_sub(x, y), 0) -&amp;gt; int_sub(x, y)&lt;/code&gt;. If we simply
  generate all possible sequences, we will find the latter simplification rule,
  which we would usually not care about.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Unclear usefulness: if we simply generate all rewrites up to a certain number
  of instructions, we will get a lot of patterns that are useless in the sense
  that they typically aren't found in realistic programs. It would be much
  better to somehow focus on the patterns that real benchmarks are using.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the &lt;a href="https://www.pypy.org/posts/2024/07/mining-jit-traces-missing-optimizations-z3.html"&gt;next blog post&lt;/a&gt; I'll discuss an alternative approach to simply generating
all possible sequences of instructions, that tries to address these problems.
This works by analyzing the real traces of benchmarks and mining those for
inefficiencies, which only shows problems that occur in actual programs.&lt;/p&gt;
&lt;h3 id="sources"&gt;Sources&lt;/h3&gt;
&lt;p&gt;I've been re-reading a lot of blog posts from John's blog:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.regehr.org/archives/1109"&gt;Let’s Work on an LLVM Superoptimizer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.regehr.org/archives/1146"&gt;Early Superoptimizer Results&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.regehr.org/archives/1252"&gt;A Few Synthesizing Superoptimizer Results&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.regehr.org/archives/1636"&gt;Synthesizing Constants&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;but also papers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/pdf/1711.04422"&gt;A Synthesizing Superoptimizer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/pdf/10.1145/3649837"&gt;Hydra: Generalizing Peephole Optimizations with Program Synthesis&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another of my favorite blogs has been &lt;a href="https://www.philipzucker.com/"&gt;Philipp Zucker's
blog&lt;/a&gt; in the last year or two, lots of excellent
posts about/using Z3 on there.&lt;/p&gt;</description><category>jit</category><category>z3</category><guid>https://www.pypy.org/posts/2024/07/finding-simple-rewrite-rules-jit-z3.html</guid><pubDate>Fri, 12 Jul 2024 19:14:09 GMT</pubDate></item><item><title>Finding JIT Optimizer Bugs using SMT Solvers and Fuzzing</title><link>https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html</link><dc:creator>CF Bolz-Tereick</dc:creator><description>&lt;p&gt;In this blog post I want to describe a recent bug finding technique that I've
added to the PyPy JIT testing infrastructure. This technique uses the Z3
theorem prover to find bugs in the optimizer of PyPy's JIT, in particular its
integer operation optimizations. The approach is
based on things I have learned from &lt;a class="reference external" href="https://www.cs.utah.edu/~regehr/"&gt;John Regehr's&lt;/a&gt; &lt;a class="reference external" href="https://blog.regehr.org/"&gt;blog&lt;/a&gt; (&lt;a class="reference external" href="https://blog.regehr.org/archives/1122"&gt;this post&lt;/a&gt; is a
good first one to read), &lt;a class="reference external" href="https://twitter.com/johnregehr/"&gt;Twitter&lt;/a&gt;, and on
his (et al) paper &lt;a class="reference external" href="https://www.cs.utah.edu/~regehr/alive2-pldi21.pdf"&gt;Alive2: Bounded Translation Validation for LLVM&lt;/a&gt;. The work
was triggered by a recent miscompilation bug my current bachelor student Nico
Rittinghaus found.&lt;/p&gt;
&lt;section id="background-python-integers-in-the-pypy-jit"&gt;
&lt;h2&gt;Background: Python Integers in the PyPy JIT&lt;/h2&gt;
&lt;p&gt;The optimizer of PyPy's JITs operates on traces, which are linear sequences of
instructions with guards. The instructions in the traces operate on different
machine-level data types, machine integers, doubles, pointers, bools, etc. In
this post we'll be mostly concerned with machine integers.&lt;/p&gt;
&lt;p&gt;To given some wider context I'll explain a bit how Python ints in the user code
relate to the types that are used in traces when the PyPy Python implementation
is used.
When PyPy turns a regular Python 3 function into a trace, there is a lot of work
happening in the JIT frontend to try to observe and infer the types that the
Python function concretely uses at runtime. The traces are generated under these
typing assumptions. Therefore, code that uses &lt;code class="docutils literal"&gt;ints&lt;/code&gt; in the Python code can
typically be translated into traces that operate on machine integers. In order
to make sure that the Python integer semantics are upheld, many of the
operations in the traces need to check that the integer results of some
operations still fit into a machine integer. If that is not the case (a rare
situation for most programs), the trace is left via a guard, execution falls
back to the interpreter, and there a big integer representation is chosen for
the too big value (the big integer representation is done via a pointer and
some storage on the heap).&lt;/p&gt;
&lt;p&gt;All of this machinery is not going to be too relevant for the rest of the
post. For the post it's important to know that trace instructions operate on
machine integers and other low-level types, and some of the operations can
optionally check whether the
results still fit into a machine integer. These trace operations are improved by
the optimizer, which tries to transform the trace into one that behaves the
same, but is less costly to execute.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="background-bounds-analysis-in-pypy-s-jit"&gt;
&lt;h2&gt;Background: Bounds Analysis in PyPy's JIT&lt;/h2&gt;
&lt;p&gt;The optimizer of PyPy's JIT has an analysis based on &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Abstract_interpretation"&gt;abstract interpretation&lt;/a&gt;
that tries to find out whether the integer values stored in a variable are
actually not using the full 64 bit (or 32 bit) range, but instead fit into some
smaller range. This means that for every integer variable &lt;code class="docutils literal"&gt;x&lt;/code&gt; in a trace, the
JIT compiler tracks upper and lower bounds of the runtime value of that
variable: a range &lt;code class="docutils literal"&gt;[a, b]&lt;/code&gt; such that for every concrete runtime value &lt;code class="docutils literal"&gt;v&lt;/code&gt;
that gets stored in variable &lt;code class="docutils literal"&gt;x&lt;/code&gt;, &lt;code class="docutils literal"&gt;a &amp;lt;= v &amp;lt;= b&lt;/code&gt; must be true.
&lt;code class="docutils literal"&gt;a&lt;/code&gt; and &lt;code class="docutils literal"&gt;b&lt;/code&gt; start out
as the most general &lt;code class="docutils literal"&gt;MININT&lt;/code&gt; and &lt;code class="docutils literal"&gt;MAXINT&lt;/code&gt;, but sometimes there is extra
information that makes it possible to improve these known bounds, and that is
often useful to optimize the code.&lt;/p&gt;
&lt;p&gt;A typical example is that the JIT knows that the length of a string is
non-negative, so for this kind of code: &lt;code class="docutils literal"&gt;x = len(s)&lt;/code&gt; where &lt;code class="docutils literal"&gt;s&lt;/code&gt; is a string,
&lt;code class="docutils literal"&gt;x&lt;/code&gt; gets a range &lt;code class="docutils literal"&gt;[0, MAXINT]&lt;/code&gt; assigned. With this information we could for
example remove a check &lt;code class="docutils literal"&gt;x + 10 &amp;lt; 0&lt;/code&gt; completely, because it can never be true.&lt;/p&gt;
&lt;p&gt;The bounds information is useful for optimization, but the analysis of the
bounds is also a source of bugs in the JIT, because the reasoning is often
subtle and easy to get wrong in corner cases. We already use a number of testing
techniques to try to make sure that it is correct. A simple one is
&lt;a class="reference external" href="https://hypothesis.works/articles/what-is-property-based-testing/"&gt;property-based testing&lt;/a&gt; using &lt;a class="reference external" href="https://github.com/HypothesisWorks/hypothesis"&gt;Hypothesis&lt;/a&gt; on the operations on bounds. Even
though Hypothesis is fantastic, it unfortunately does not catch
absolutely all the bugs even if we'd like it too, as we'll see in the next
section.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="motivation-a-jit-miscompilation"&gt;
&lt;h2&gt;Motivation: A JIT Miscompilation&lt;/h2&gt;
&lt;p&gt;I am currently supervising a Bachelor thesis by Nico Rittinghaus, who is
extending the integer analysis in the JIT. He'll probably write a separate blog
post about that soon. In the process of his work, the current bounds analysis
code got a lot of scrutiny, and we found out that one of the unit tests of the
bounds analysis was actually incorrect, and the example code in that unit test
was optimized incorrectly. This case of incorrect optimization is not a big deal
for regular Python code, because it involved a "wrapping integer addition
operation", i.e. one where overflowing results just wrap around to negative
values. All the additions and other arithmetic operations that the PyPy Python
frontend generates actually have
overflow checks (to be able to switch to a big integer representation if
needed).
However, it's still possible to trigger the problem with the
&lt;code class="docutils literal"&gt;__pypy__.intop.int_add&lt;/code&gt; API which is a function that exposes wraparound
arithmetic on Python ints.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://foss.heptapod.net/pypy/pypy/-/issues/3832"&gt;Here's the miscompilation&lt;/a&gt;. The JIT optimizes the following function:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_ce3517a7fb2b4879815d719b289d4f43-1" name="rest_code_ce3517a7fb2b4879815d719b289d4f43-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ce3517a7fb2b4879815d719b289d4f43-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;__pypy__&lt;/span&gt;
&lt;a id="rest_code_ce3517a7fb2b4879815d719b289d4f43-2" name="rest_code_ce3517a7fb2b4879815d719b289d4f43-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ce3517a7fb2b4879815d719b289d4f43-2"&gt;&lt;/a&gt;
&lt;a id="rest_code_ce3517a7fb2b4879815d719b289d4f43-3" name="rest_code_ce3517a7fb2b4879815d719b289d4f43-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ce3517a7fb2b4879815d719b289d4f43-3"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;wrong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_ce3517a7fb2b4879815d719b289d4f43-4" name="rest_code_ce3517a7fb2b4879815d719b289d4f43-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ce3517a7fb2b4879815d719b289d4f43-4"&gt;&lt;/a&gt;    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;__pypy__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_ce3517a7fb2b4879815d719b289d4f43-5" name="rest_code_ce3517a7fb2b4879815d719b289d4f43-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ce3517a7fb2b4879815d719b289d4f43-5"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_ce3517a7fb2b4879815d719b289d4f43-6" name="rest_code_ce3517a7fb2b4879815d719b289d4f43-6" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ce3517a7fb2b4879815d719b289d4f43-6"&gt;&lt;/a&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_ce3517a7fb2b4879815d719b289d4f43-7" name="rest_code_ce3517a7fb2b4879815d719b289d4f43-7" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ce3517a7fb2b4879815d719b289d4f43-7"&gt;&lt;/a&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;a id="rest_code_ce3517a7fb2b4879815d719b289d4f43-8" name="rest_code_ce3517a7fb2b4879815d719b289d4f43-8" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ce3517a7fb2b4879815d719b289d4f43-8"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a id="rest_code_ce3517a7fb2b4879815d719b289d4f43-9" name="rest_code_ce3517a7fb2b4879815d719b289d4f43-9" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ce3517a7fb2b4879815d719b289d4f43-9"&gt;&lt;/a&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Into the following code:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_3fa3a14277974f0cba1e90bdfca74d26-1" name="rest_code_3fa3a14277974f0cba1e90bdfca74d26-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_3fa3a14277974f0cba1e90bdfca74d26-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;__pypy__&lt;/span&gt;
&lt;a id="rest_code_3fa3a14277974f0cba1e90bdfca74d26-2" name="rest_code_3fa3a14277974f0cba1e90bdfca74d26-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_3fa3a14277974f0cba1e90bdfca74d26-2"&gt;&lt;/a&gt;
&lt;a id="rest_code_3fa3a14277974f0cba1e90bdfca74d26-3" name="rest_code_3fa3a14277974f0cba1e90bdfca74d26-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_3fa3a14277974f0cba1e90bdfca74d26-3"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;wrong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_3fa3a14277974f0cba1e90bdfca74d26-4" name="rest_code_3fa3a14277974f0cba1e90bdfca74d26-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_3fa3a14277974f0cba1e90bdfca74d26-4"&gt;&lt;/a&gt;    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;__pypy__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_3fa3a14277974f0cba1e90bdfca74d26-5" name="rest_code_3fa3a14277974f0cba1e90bdfca74d26-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_3fa3a14277974f0cba1e90bdfca74d26-5"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_3fa3a14277974f0cba1e90bdfca74d26-6" name="rest_code_3fa3a14277974f0cba1e90bdfca74d26-6" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_3fa3a14277974f0cba1e90bdfca74d26-6"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;a id="rest_code_3fa3a14277974f0cba1e90bdfca74d26-7" name="rest_code_3fa3a14277974f0cba1e90bdfca74d26-7" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_3fa3a14277974f0cba1e90bdfca74d26-7"&gt;&lt;/a&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Basically the faulty reasoning of the JIT looks like this: if &lt;code class="docutils literal"&gt;int_add(x, 10) &amp;lt; 15&lt;/code&gt;
then it must follow that &lt;code class="docutils literal"&gt;x &amp;lt; 5&lt;/code&gt;, which is stronger than &lt;code class="docutils literal"&gt;x &amp;lt; 6&lt;/code&gt;, so the
second &lt;code class="docutils literal"&gt;if&lt;/code&gt; is always true. This sounds good, but is actually wrong
if the addition &lt;code class="docutils literal"&gt;+ 10&lt;/code&gt; wrapped around. So if &lt;code class="docutils literal"&gt;x == MAXINT&lt;/code&gt;, then
&lt;code class="docutils literal"&gt;int_add(x, 10) == MININT + 9 &amp;lt; 15&lt;/code&gt;. But &lt;code class="docutils literal"&gt;MAXINT &amp;lt; 5&lt;/code&gt; is not
correct.&lt;/p&gt;
&lt;p&gt;Note how the same reasoning with overflow-checking addition is correct! If &lt;code class="docutils literal"&gt;x +
10 &amp;lt; 15&lt;/code&gt; and the &lt;code class="docutils literal"&gt;+&lt;/code&gt; didn't overflow, then indeed &lt;code class="docutils literal"&gt;x &amp;lt; 6&lt;/code&gt;. And if your
mind bends starting to think about all this, you understand some of the
difficulty of getting the JIT correct in this area.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="how-could-we-have-avoided-this-bug"&gt;
&lt;h2&gt;How could we have avoided this bug?&lt;/h2&gt;
&lt;p&gt;One &lt;a class="reference external" href="https://twitter.com/cfbolz/status/1482649144099586051"&gt;exercise I try to do after finding bugs&lt;/a&gt; is to reflect on ways that the
bug could have been avoided. I think this is particularly important in the JIT,
where bugs are potentially really annoying to find and can cause very strange
behaviour in basically arbitrary Python code.&lt;/p&gt;
&lt;p&gt;It's easy to always answer this question with "try to think more carefully
when working", but that approach cannot be relied on in complicated situations,
because humans don't concentrate perfectly for long stretches of time.&lt;/p&gt;
&lt;p&gt;A situation-specific problem I identified was the bad design of the range analysis API.
A range is not just represented by two numbers, instead it's two numbers
and two bools that are supposed to represent that some operation did or did not
underflow/overflow. The meaning of these bools was quite hard to grasp and easy
to get wrong, so probably they should never have been introduced in the first
place (and my bugfix indeed removed them).&lt;/p&gt;
&lt;p&gt;But in the rest of this blog post I want to talk about another, systematic
approach that can be applied to the problem of mis-optimizations of integer
operations, and that is done by applying an SMT solver to the problem.&lt;/p&gt;
&lt;p&gt;An SMT solver (&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Satisfiability_modulo_theories"&gt;Satisfyability Modulo Theories&lt;/a&gt;) is a tool that can be used to
find out whether mathematical formulas are "satisfiable", i.e. whether
some chosen set of inputs exists that will make the formulas evaluate to true. SMT solvers are
commonly used in a wide range of CS applications including program correctness
proofs, program synthesis, etc. The most widely known one is probably &lt;a class="reference external" href="https://github.com/Z3Prover"&gt;Z3&lt;/a&gt; by
Microsoft Research which has the nice advantage of coming with an easy-to-use
Python binding.&lt;/p&gt;
&lt;p&gt;Going into this I basically knew next to nothing about SMT solvers (despite
having been embedded in a formal methods research group for years!) so it was an
interesting new world to learn about.&lt;/p&gt;
&lt;p&gt;As briefly mentioned in the introduction, the approach I took followed a similar
(but &lt;em&gt;much&lt;/em&gt; more properly executed) one applied to LLVM operations, called
&lt;a class="reference external" href="https://github.com/AliveToolkit/alive2/"&gt;Alive2&lt;/a&gt;. Krister Waldfridsson has done &lt;a class="reference external" href="https://kristerw.github.io/2022/09/13/translation-validation/"&gt;similar work for GCC recently&lt;/a&gt;,
described on his blog.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="z3-proof-of-concept"&gt;
&lt;h2&gt;Z3 Proof of Concept&lt;/h2&gt;
&lt;p&gt;The first thing I did was to try to get Z3 find the above bug, by encoding the
input program into an SMT formula by hand and trying to get Z3 to prove the condition
that the JIT thinks is always true. The Z3 code for this looks as follows:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_00215978594140869cde3d042222cd9a-1" name="rest_code_00215978594140869cde3d042222cd9a-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_00215978594140869cde3d042222cd9a-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;z3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Implies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prove&lt;/span&gt;
&lt;a id="rest_code_00215978594140869cde3d042222cd9a-2" name="rest_code_00215978594140869cde3d042222cd9a-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_00215978594140869cde3d042222cd9a-2"&gt;&lt;/a&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'x'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_00215978594140869cde3d042222cd9a-3" name="rest_code_00215978594140869cde3d042222cd9a-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_00215978594140869cde3d042222cd9a-3"&gt;&lt;/a&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;a id="rest_code_00215978594140869cde3d042222cd9a-4" name="rest_code_00215978594140869cde3d042222cd9a-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_00215978594140869cde3d042222cd9a-4"&gt;&lt;/a&gt;&lt;span class="n"&gt;cond1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
&lt;a id="rest_code_00215978594140869cde3d042222cd9a-5" name="rest_code_00215978594140869cde3d042222cd9a-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_00215978594140869cde3d042222cd9a-5"&gt;&lt;/a&gt;&lt;span class="n"&gt;cond2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
&lt;a id="rest_code_00215978594140869cde3d042222cd9a-6" name="rest_code_00215978594140869cde3d042222cd9a-6" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_00215978594140869cde3d042222cd9a-6"&gt;&lt;/a&gt;&lt;span class="n"&gt;prove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Implies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cond1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cond2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here, &lt;code class="docutils literal"&gt;x&lt;/code&gt; is defined to be a bit vector variable of width 64, which is a
datatype that can be used to represent bounded machine integers. Addition on
bit vectors performs wraparound arithmetic, like the &lt;code class="docutils literal"&gt;__pypy__.intop.int_add&lt;/code&gt;
call in the original code. The JIT optimized the second condition away, so
essentially it was convinced that the first condition implies the second one.
The above snippet tries to get Z3 to confirm this.&lt;/p&gt;
&lt;p&gt;When run, the above program prints:&lt;/p&gt;
&lt;pre class="literal-block"&gt;counterexample
[x = 9223372036854775803]&lt;/pre&gt;
&lt;p&gt;Which shows the bug. As a small side-note, I thought it was cool that the
process of "proving" something in Z3 basically means trying to find an example
for the negation of the formula. If no counterexample can be found for the
negation, the original formula is true. If the original formula turns out to be
false (like here) we get a nice example that shows the problem to go with it.&lt;/p&gt;
&lt;p&gt;It's not realistic to hand-translate all the hundreds of
unit-tests into Z3 formulas and then ask Z3 to prove the optimizations. Instead,
we want to have a program that does this for us.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="smt-checking-of-the-jit-optimizer"&gt;
&lt;h2&gt;SMT Checking of the JIT Optimizer&lt;/h2&gt;
&lt;p&gt;What we want from this program is the following: given an unoptimized trace and
its optimized version, we want to use Z3 to check whether the optimized trace
behaves identically to the unoptimized one. One question is what "behaves
identically" means. What we care about is the outputs of the trace being the
same values, no matter how they are computed. Also, for every guard we want to
make sure that it fails in identical ways in the optimized and unoptimized
versions. A guard is only allowed to be optimized away if it can never fail.
The code that comes after a guard can assume that the guard has not failed,
because otherwise execution would have left the trace. All of this should be
true regardless for the values of the input variables of the trace.&lt;/p&gt;
&lt;p&gt;So in order to check that the two traces are behaving identically, we do the
following:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;We create Z3 variables for every input variable. We use the same input
variables both for the unoptimized as well as the optimized trace.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We align the two traces at the corresponding guards. Thankfully the optimizer
keeps track of which optimized guard corresponds to which unoptimized input
guard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;All the operations before a guard are translated into Z3 formulas, for both
versions of the trace.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For two corresponding guards, we ask Z3 to prove that the guard conditions are
identical.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For a guard that was optimized away we ask Z3 to prove that the condition is
always true.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After a guard, we tell Z3 that from now on it can assume that the guard
condition is true.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We repeat this, guard for guard, until we reach the end of the trace. There,
we ask Z3 to prove that the output variables in the unoptimized trace and the
optimized trace are identical (every trace can return one or many values).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I implemented this, it's &lt;a class="reference external" href="https://foss.heptapod.net/pypy/pypy/-/blob/branch/default/rpython/jit/metainterp/optimizeopt/test/test_z3checktests.py"&gt;not a lot of code&lt;/a&gt;, basically a couple of hundred lines
of (somewhat hacky) Python code. So far I only support integer
operations. Here are some parts of the code to give you a flavor of what this
looks like.&lt;/p&gt;
&lt;p&gt;This is the code that translates operations into Z3 formulas:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-1" name="rest_code_4d691075f2384f8298b9089c45a87dc0-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;add_to_solver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-2" name="rest_code_4d691075f2384f8298b9089c45a87dc0-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-2"&gt;&lt;/a&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-3" name="rest_code_4d691075f2384f8298b9089c45a87dc0-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-3"&gt;&lt;/a&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'v'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# is it an operation with a result&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-4" name="rest_code_4d691075f2384f8298b9089c45a87dc0-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-4"&gt;&lt;/a&gt;            &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;newvar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-5" name="rest_code_4d691075f2384f8298b9089c45a87dc0-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-5"&gt;&lt;/a&gt;        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# or does it return void&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-6" name="rest_code_4d691075f2384f8298b9089c45a87dc0-6" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-6"&gt;&lt;/a&gt;            &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-7" name="rest_code_4d691075f2384f8298b9089c45a87dc0-7" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-7"&gt;&lt;/a&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-8" name="rest_code_4d691075f2384f8298b9089c45a87dc0-8" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-8"&gt;&lt;/a&gt;       &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-9" name="rest_code_4d691075f2384f8298b9089c45a87dc0-9" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-9"&gt;&lt;/a&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-10" name="rest_code_4d691075f2384f8298b9089c45a87dc0-10" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-10"&gt;&lt;/a&gt;        &lt;span class="c1"&gt;# convert arguments&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-11" name="rest_code_4d691075f2384f8298b9089c45a87dc0-11" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-11"&gt;&lt;/a&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;numargs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-12" name="rest_code_4d691075f2384f8298b9089c45a87dc0-12" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-12"&gt;&lt;/a&gt;            &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;convertarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-13" name="rest_code_4d691075f2384f8298b9089c45a87dc0-13" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-13"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;numargs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-14" name="rest_code_4d691075f2384f8298b9089c45a87dc0-14" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-14"&gt;&lt;/a&gt;            &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;convertarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-15" name="rest_code_4d691075f2384f8298b9089c45a87dc0-15" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-15"&gt;&lt;/a&gt;            &lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;convertarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-16" name="rest_code_4d691075f2384f8298b9089c45a87dc0-16" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-16"&gt;&lt;/a&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-17" name="rest_code_4d691075f2384f8298b9089c45a87dc0-17" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-17"&gt;&lt;/a&gt;        &lt;span class="c1"&gt;# compute results&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-18" name="rest_code_4d691075f2384f8298b9089c45a87dc0-18" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-18"&gt;&lt;/a&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_add"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-19" name="rest_code_4d691075f2384f8298b9089c45a87dc0-19" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-19"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-20" name="rest_code_4d691075f2384f8298b9089c45a87dc0-20" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-20"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-21" name="rest_code_4d691075f2384f8298b9089c45a87dc0-21" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-21"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-22" name="rest_code_4d691075f2384f8298b9089c45a87dc0-22" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-22"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_mul"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-23" name="rest_code_4d691075f2384f8298b9089c45a87dc0-23" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-23"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-24" name="rest_code_4d691075f2384f8298b9089c45a87dc0-24" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-24"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_and"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-25" name="rest_code_4d691075f2384f8298b9089c45a87dc0-25" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-25"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-26" name="rest_code_4d691075f2384f8298b9089c45a87dc0-26" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-26"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_or"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-27" name="rest_code_4d691075f2384f8298b9089c45a87dc0-27" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-27"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-28" name="rest_code_4d691075f2384f8298b9089c45a87dc0-28" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-28"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_xor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-29" name="rest_code_4d691075f2384f8298b9089c45a87dc0-29" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-29"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-30" name="rest_code_4d691075f2384f8298b9089c45a87dc0-30" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-30"&gt;&lt;/a&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-31" name="rest_code_4d691075f2384f8298b9089c45a87dc0-31" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-31"&gt;&lt;/a&gt;        &lt;span class="c1"&gt;# ...  more operations, some shown below&lt;/span&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-32" name="rest_code_4d691075f2384f8298b9089c45a87dc0-32" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-32"&gt;&lt;/a&gt;
&lt;a id="rest_code_4d691075f2384f8298b9089c45a87dc0-33" name="rest_code_4d691075f2384f8298b9089c45a87dc0-33" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4d691075f2384f8298b9089c45a87dc0-33"&gt;&lt;/a&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;New Z3 variables are defined by the helper function &lt;code class="docutils literal"&gt;newvar&lt;/code&gt;, which adds the
operation to a dictionary &lt;code class="docutils literal"&gt;box_to_z3&lt;/code&gt; mapping boxes (=variables) to Z3
variables. Due to the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Static_single-assignment_form"&gt;SSA&lt;/a&gt; property that traces have, a variable must be defined
before its first use.&lt;/p&gt;
&lt;p&gt;Here's what &lt;code class="docutils literal"&gt;newvar&lt;/code&gt; looks like (&lt;code class="docutils literal"&gt;LONG_BIT&lt;/code&gt; is a constant that is either
&lt;code class="docutils literal"&gt;64&lt;/code&gt; or &lt;code class="docutils literal"&gt;32&lt;/code&gt;, depending on the target architecture):&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-1" name="rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;newvar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-2" name="rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-2"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# ... some logic around making the string representation&lt;/span&gt;
&lt;a id="rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-3" name="rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-3"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# somewhat nicer omitted&lt;/span&gt;
&lt;a id="rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-4" name="rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-4"&gt;&lt;/a&gt;    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BitVec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LONG_BIT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-5" name="rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-5"&gt;&lt;/a&gt;    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box_to_z3&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;a id="rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-6" name="rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-6" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_d5cb0b4c69b549c1a8c5a658d7b6690f-6"&gt;&lt;/a&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;convert&lt;/code&gt; method turns an operation argument (either a constant or a
variable) into a Z3 formula (either a constant bit vector or an already defined
Z3 variable). &lt;code class="docutils literal"&gt;convertarg&lt;/code&gt; is a helper function that takes an operation, reads
its nth argument and converts it.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_993985e53650433ebd986b1f3b248d4d-1" name="rest_code_993985e53650433ebd986b1f3b248d4d-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_993985e53650433ebd986b1f3b248d4d-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_993985e53650433ebd986b1f3b248d4d-2" name="rest_code_993985e53650433ebd986b1f3b248d4d-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_993985e53650433ebd986b1f3b248d4d-2"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ConstInt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_993985e53650433ebd986b1f3b248d4d-3" name="rest_code_993985e53650433ebd986b1f3b248d4d-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_993985e53650433ebd986b1f3b248d4d-3"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BitVecVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getint&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;LONG_BIT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_993985e53650433ebd986b1f3b248d4d-4" name="rest_code_993985e53650433ebd986b1f3b248d4d-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_993985e53650433ebd986b1f3b248d4d-4"&gt;&lt;/a&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box_to_z3&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a id="rest_code_993985e53650433ebd986b1f3b248d4d-5" name="rest_code_993985e53650433ebd986b1f3b248d4d-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_993985e53650433ebd986b1f3b248d4d-5"&gt;&lt;/a&gt;
&lt;a id="rest_code_993985e53650433ebd986b1f3b248d4d-6" name="rest_code_993985e53650433ebd986b1f3b248d4d-6" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_993985e53650433ebd986b1f3b248d4d-6"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;convertarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_993985e53650433ebd986b1f3b248d4d-7" name="rest_code_993985e53650433ebd986b1f3b248d4d-7" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_993985e53650433ebd986b1f3b248d4d-7"&gt;&lt;/a&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The lookup of variables in &lt;code class="docutils literal"&gt;box_to_z3&lt;/code&gt; that &lt;code class="docutils literal"&gt;convert&lt;/code&gt; does cannot fail,
because the variable must have been defined before use.&lt;/p&gt;
&lt;p&gt;Comparisons return the bit vector 0 or bit vector 1, we use a helper function
&lt;code class="docutils literal"&gt;cond&lt;/code&gt; to turn the Z3 truth value of the comparison into a bit vector:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-1" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z3expr&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-2" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-2"&gt;&lt;/a&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;If&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TRUEBV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FALSEBV&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-3" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-3"&gt;&lt;/a&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-4" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-4"&gt;&lt;/a&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-5" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-5"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;add_to_solver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-6" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-6" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-6"&gt;&lt;/a&gt;        &lt;span class="c1"&gt;# ... start as above&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-7" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-7" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-7"&gt;&lt;/a&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-8" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-8" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-8"&gt;&lt;/a&gt;        &lt;span class="c1"&gt;# more cases&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-9" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-9" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-9"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_eq"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-10" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-10" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-10"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-11" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-11" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-11"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_ne"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-12" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-12" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-12"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-13" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-13" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-13"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_lt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-14" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-14" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-14"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-15" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-15" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-15"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_le"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-16" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-16" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-16"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-17" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-17" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-17"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_gt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-18" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-18" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-18"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-19" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-19" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-19"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_ge"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-20" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-20" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-20"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-21" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-21" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-21"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_is_true"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-22" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-22" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-22"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;FALSEBV&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-23" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-23" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-23"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"uint_lt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-24" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-24" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-24"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ULT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-25" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-25" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-25"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"uint_le"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-26" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-26" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-26"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ULE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-27" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-27" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-27"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"uint_gt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-28" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-28" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-28"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UGT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-29" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-29" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-29"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"uint_ge"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-30" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-30" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-30"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UGE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-31" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-31" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-31"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_is_zero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-32" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-32" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-32"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;FALSEBV&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-33" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-33" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-33"&gt;&lt;/a&gt;
&lt;a id="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-34" name="rest_code_ab1fd70db0ea4eed96e261eb8d46488b-34" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_ab1fd70db0ea4eed96e261eb8d46488b-34"&gt;&lt;/a&gt;        &lt;span class="c1"&gt;# ... rest as above&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So basically for every trace operation that operates on integers I had to give a
translation into Z3 formulas, which is mostly straightforward.&lt;/p&gt;
&lt;p&gt;Guard operations get converted into a Z3 boolean by their own helper function,
which looks like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_c38d04d949534772af14c62547ceca90-1" name="rest_code_c38d04d949534772af14c62547ceca90-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_c38d04d949534772af14c62547ceca90-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;guard_to_condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_c38d04d949534772af14c62547ceca90-2" name="rest_code_c38d04d949534772af14c62547ceca90-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_c38d04d949534772af14c62547ceca90-2"&gt;&lt;/a&gt;    &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getopname&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_c38d04d949534772af14c62547ceca90-3" name="rest_code_c38d04d949534772af14c62547ceca90-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_c38d04d949534772af14c62547ceca90-3"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"guard_true"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_c38d04d949534772af14c62547ceca90-4" name="rest_code_c38d04d949534772af14c62547ceca90-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_c38d04d949534772af14c62547ceca90-4"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;convertarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;TRUEBV&lt;/span&gt;
&lt;a id="rest_code_c38d04d949534772af14c62547ceca90-5" name="rest_code_c38d04d949534772af14c62547ceca90-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_c38d04d949534772af14c62547ceca90-5"&gt;&lt;/a&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"guard_false"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_c38d04d949534772af14c62547ceca90-6" name="rest_code_c38d04d949534772af14c62547ceca90-6" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_c38d04d949534772af14c62547ceca90-6"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;convertarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;FALSEBV&lt;/span&gt;
&lt;a id="rest_code_c38d04d949534772af14c62547ceca90-7" name="rest_code_c38d04d949534772af14c62547ceca90-7" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_c38d04d949534772af14c62547ceca90-7"&gt;&lt;/a&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"guard_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_c38d04d949534772af14c62547ceca90-8" name="rest_code_c38d04d949534772af14c62547ceca90-8" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_c38d04d949534772af14c62547ceca90-8"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;convertarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;convertarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_c38d04d949534772af14c62547ceca90-9" name="rest_code_c38d04d949534772af14c62547ceca90-9" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_c38d04d949534772af14c62547ceca90-9"&gt;&lt;/a&gt;
&lt;a id="rest_code_c38d04d949534772af14c62547ceca90-10" name="rest_code_c38d04d949534772af14c62547ceca90-10" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_c38d04d949534772af14c62547ceca90-10"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# ... some more exist, shown below&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Some operations are a bit trickier. An important example in the context of
this blog post are integer operations that check for overflow. The overflow
operations return a result, but also a boolean whether the operation overflowed
or not.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-1" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;add_to_solver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-2" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-2"&gt;&lt;/a&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-3" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-3"&gt;&lt;/a&gt;        &lt;span class="c1"&gt;# ... more cases&lt;/span&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-4" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-4"&gt;&lt;/a&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-5" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-5"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_add_ovf"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-6" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-6" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-6"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-7" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-7" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-7"&gt;&lt;/a&gt;            &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SignExt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LONG_BIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SignExt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LONG_BIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-8" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-8" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-8"&gt;&lt;/a&gt;            &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_ovf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SignExt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LONG_BIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-9" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-9" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-9"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_sub_ovf"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-10" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-10" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-10"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-11" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-11" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-11"&gt;&lt;/a&gt;            &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SignExt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LONG_BIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SignExt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LONG_BIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-12" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-12" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-12"&gt;&lt;/a&gt;            &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_ovf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SignExt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LONG_BIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-13" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-13" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-13"&gt;&lt;/a&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"int_mul_ovf"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-14" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-14" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-14"&gt;&lt;/a&gt;            &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-15" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-15" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-15"&gt;&lt;/a&gt;            &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SignExt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LONG_BIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SignExt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LONG_BIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-16" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-16" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-16"&gt;&lt;/a&gt;            &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_ovf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SignExt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LONG_BIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-17" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-17" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-17"&gt;&lt;/a&gt;
&lt;a id="rest_code_52300a59131b4d7f9a6d36ba365325fa-18" name="rest_code_52300a59131b4d7f9a6d36ba365325fa-18" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_52300a59131b4d7f9a6d36ba365325fa-18"&gt;&lt;/a&gt;        &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The boolean is computed by comparing the result of the bit vector operation with
the result of converting the input bit vectors into an abstract (arbitrary
precision) integer and the result back to bit vectors. Let's go through the
addition case step by step, the other cases work analogously.&lt;/p&gt;
&lt;p&gt;The addition in the first &lt;code class="docutils literal"&gt;elif&lt;/code&gt; that computes &lt;code class="docutils literal"&gt;expr&lt;/code&gt; is an addition on bit
vectors, therefore it is performing wraparound arithmetic.
&lt;code class="docutils literal"&gt;z3.SignExt(LONG_BIT, arg0)&lt;/code&gt; sign-extends &lt;code class="docutils literal"&gt;arg0&lt;/code&gt; from a bit vector of
&lt;code class="docutils literal"&gt;LONG_BIT&lt;/code&gt; bits to an abstract, arbitrary precision integer. The addition in
the second line is therefore an addition between abstract integers, so it will
never overflow and just compute the correct result as an integer.&lt;/p&gt;
&lt;p&gt;The condition to check for overflow is now: if the results of the two different
ways to do the addition are the same, then overflow did not occur. So in order
to compute &lt;code class="docutils literal"&gt;state.no_ovf&lt;/code&gt; in the addition case the
code converts the result of the bit vector wraparound addition to
an abstract integer (using &lt;code class="docutils literal"&gt;SignExt&lt;/code&gt; again), and then compares that to the integer
result.&lt;/p&gt;
&lt;p&gt;This boolean can then be checked by the guard operations &lt;code class="docutils literal"&gt;guard_no_overflow&lt;/code&gt;
and &lt;code class="docutils literal"&gt;guard_overflow&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_bf6ed1e540f94c7692bf04dd9998473f-1" name="rest_code_bf6ed1e540f94c7692bf04dd9998473f-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_bf6ed1e540f94c7692bf04dd9998473f-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;guard_to_condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_bf6ed1e540f94c7692bf04dd9998473f-2" name="rest_code_bf6ed1e540f94c7692bf04dd9998473f-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_bf6ed1e540f94c7692bf04dd9998473f-2"&gt;&lt;/a&gt;
&lt;a id="rest_code_bf6ed1e540f94c7692bf04dd9998473f-3" name="rest_code_bf6ed1e540f94c7692bf04dd9998473f-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_bf6ed1e540f94c7692bf04dd9998473f-3"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# ... more cases&lt;/span&gt;
&lt;a id="rest_code_bf6ed1e540f94c7692bf04dd9998473f-4" name="rest_code_bf6ed1e540f94c7692bf04dd9998473f-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_bf6ed1e540f94c7692bf04dd9998473f-4"&gt;&lt;/a&gt;
&lt;a id="rest_code_bf6ed1e540f94c7692bf04dd9998473f-5" name="rest_code_bf6ed1e540f94c7692bf04dd9998473f-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_bf6ed1e540f94c7692bf04dd9998473f-5"&gt;&lt;/a&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"guard_no_overflow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_bf6ed1e540f94c7692bf04dd9998473f-6" name="rest_code_bf6ed1e540f94c7692bf04dd9998473f-6" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_bf6ed1e540f94c7692bf04dd9998473f-6"&gt;&lt;/a&gt;        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_ovf&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;a id="rest_code_bf6ed1e540f94c7692bf04dd9998473f-7" name="rest_code_bf6ed1e540f94c7692bf04dd9998473f-7" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_bf6ed1e540f94c7692bf04dd9998473f-7"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_ovf&lt;/span&gt;
&lt;a id="rest_code_bf6ed1e540f94c7692bf04dd9998473f-8" name="rest_code_bf6ed1e540f94c7692bf04dd9998473f-8" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_bf6ed1e540f94c7692bf04dd9998473f-8"&gt;&lt;/a&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;opname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"guard_overflow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_bf6ed1e540f94c7692bf04dd9998473f-9" name="rest_code_bf6ed1e540f94c7692bf04dd9998473f-9" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_bf6ed1e540f94c7692bf04dd9998473f-9"&gt;&lt;/a&gt;        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_ovf&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;a id="rest_code_bf6ed1e540f94c7692bf04dd9998473f-10" name="rest_code_bf6ed1e540f94c7692bf04dd9998473f-10" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_bf6ed1e540f94c7692bf04dd9998473f-10"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;z3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Not&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_ovf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_bf6ed1e540f94c7692bf04dd9998473f-11" name="rest_code_bf6ed1e540f94c7692bf04dd9998473f-11" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_bf6ed1e540f94c7692bf04dd9998473f-11"&gt;&lt;/a&gt;
&lt;a id="rest_code_bf6ed1e540f94c7692bf04dd9998473f-12" name="rest_code_bf6ed1e540f94c7692bf04dd9998473f-12" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_bf6ed1e540f94c7692bf04dd9998473f-12"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# ... more cases&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="finding-the-bug-again"&gt;
&lt;h2&gt;Finding the Bug, Again&lt;/h2&gt;
&lt;p&gt;Let's actually make all of this more concrete by applying it to the trace of our
original bug. The input trace and the incorrectly optimized trace for that look
like this (differences highlighted):&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_e33da18f256e4c2bb824212c23521535-1" name="rest_code_e33da18f256e4c2bb824212c23521535-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_e33da18f256e4c2bb824212c23521535-1"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# input                       # optimized&lt;/span&gt;
&lt;a id="rest_code_e33da18f256e4c2bb824212c23521535-2" name="rest_code_e33da18f256e4c2bb824212c23521535-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_e33da18f256e4c2bb824212c23521535-2"&gt;&lt;/a&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                          &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a id="rest_code_e33da18f256e4c2bb824212c23521535-3" name="rest_code_e33da18f256e4c2bb824212c23521535-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_e33da18f256e4c2bb824212c23521535-3"&gt;&lt;/a&gt;&lt;span class="n"&gt;i1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;int_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="n"&gt;i1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;int_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_e33da18f256e4c2bb824212c23521535-4" name="rest_code_e33da18f256e4c2bb824212c23521535-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_e33da18f256e4c2bb824212c23521535-4"&gt;&lt;/a&gt;&lt;span class="n"&gt;i2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;int_lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           &lt;span class="n"&gt;i2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;int_lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_e33da18f256e4c2bb824212c23521535-5" name="rest_code_e33da18f256e4c2bb824212c23521535-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_e33da18f256e4c2bb824212c23521535-5"&gt;&lt;/a&gt;&lt;span class="n"&gt;guard_true&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                &lt;span class="n"&gt;guard_true&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_e33da18f256e4c2bb824212c23521535-6" name="rest_code_e33da18f256e4c2bb824212c23521535-6" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_e33da18f256e4c2bb824212c23521535-6"&gt;&lt;/a&gt;&lt;span class="hll"&gt;&lt;span class="n"&gt;i3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;int_lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;            &lt;span class="n"&gt;jump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;a id="rest_code_e33da18f256e4c2bb824212c23521535-7" name="rest_code_e33da18f256e4c2bb824212c23521535-7" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_e33da18f256e4c2bb824212c23521535-7"&gt;&lt;/a&gt;&lt;span class="hll"&gt;&lt;span class="n"&gt;guard_true&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;a id="rest_code_e33da18f256e4c2bb824212c23521535-8" name="rest_code_e33da18f256e4c2bb824212c23521535-8" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_e33da18f256e4c2bb824212c23521535-8"&gt;&lt;/a&gt;&lt;span class="hll"&gt;&lt;span class="n"&gt;jump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that the trace represents just one of the paths through the control flow
graph of the original function, which is typical for tracing JITs (the other
paths could incrementally get added later).&lt;/p&gt;
&lt;p&gt;The first guards in both these traces correspond to each other, so the first
chunks to check are the first three operations (lines 1-4). Those operations
don't get changed by the optimizer at all.&lt;/p&gt;
&lt;p&gt;These two identical traces get translated to the following Z3 formulas:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code text"&gt;&lt;a id="rest_code_0ecc9904a05b4820ab2f4dce33fe6eb3-1" name="rest_code_0ecc9904a05b4820ab2f4dce33fe6eb3-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_0ecc9904a05b4820ab2f4dce33fe6eb3-1"&gt;&lt;/a&gt;i1unoptimized == input_i0 + 10
&lt;a id="rest_code_0ecc9904a05b4820ab2f4dce33fe6eb3-2" name="rest_code_0ecc9904a05b4820ab2f4dce33fe6eb3-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_0ecc9904a05b4820ab2f4dce33fe6eb3-2"&gt;&lt;/a&gt;i2unoptimized == If(i1unoptimized &amp;lt; 15, 1, 0)
&lt;a id="rest_code_0ecc9904a05b4820ab2f4dce33fe6eb3-3" name="rest_code_0ecc9904a05b4820ab2f4dce33fe6eb3-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_0ecc9904a05b4820ab2f4dce33fe6eb3-3"&gt;&lt;/a&gt;i1optimized == input_i0 + 10
&lt;a id="rest_code_0ecc9904a05b4820ab2f4dce33fe6eb3-4" name="rest_code_0ecc9904a05b4820ab2f4dce33fe6eb3-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_0ecc9904a05b4820ab2f4dce33fe6eb3-4"&gt;&lt;/a&gt;i2optimized == If(i1optimized &amp;lt; 15, 1, 0)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To check that the two corresponding guards are the same, the solver is asked to
prove that &lt;code class="docutils literal"&gt;(i2unoptimized == 1) == (i2optimized == 1)&lt;/code&gt;. This is
correct, because the formulas for &lt;code class="docutils literal"&gt;i2unoptimized&lt;/code&gt; and &lt;code class="docutils literal"&gt;i2optimized&lt;/code&gt; are
completely identical.&lt;/p&gt;
&lt;p&gt;After checking that the guards behave the same, we add the knowledge to the
solver that the guards passed. So the Z3 formulas become:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code text"&gt;&lt;a id="rest_code_d2ae0c79fdf74db6a8784948b958d52a-1" name="rest_code_d2ae0c79fdf74db6a8784948b958d52a-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_d2ae0c79fdf74db6a8784948b958d52a-1"&gt;&lt;/a&gt;i1unoptimized == input_i0 + 10
&lt;a id="rest_code_d2ae0c79fdf74db6a8784948b958d52a-2" name="rest_code_d2ae0c79fdf74db6a8784948b958d52a-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_d2ae0c79fdf74db6a8784948b958d52a-2"&gt;&lt;/a&gt;i2unoptimized == If(i1unoptimized &amp;lt; 15, 1, 0)
&lt;a id="rest_code_d2ae0c79fdf74db6a8784948b958d52a-3" name="rest_code_d2ae0c79fdf74db6a8784948b958d52a-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_d2ae0c79fdf74db6a8784948b958d52a-3"&gt;&lt;/a&gt;i1optimized == input_i0 + 10
&lt;a id="rest_code_d2ae0c79fdf74db6a8784948b958d52a-4" name="rest_code_d2ae0c79fdf74db6a8784948b958d52a-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_d2ae0c79fdf74db6a8784948b958d52a-4"&gt;&lt;/a&gt;i2optimized == If(i1optimized &amp;lt; 15, 1, 0)
&lt;a id="rest_code_d2ae0c79fdf74db6a8784948b958d52a-5" name="rest_code_d2ae0c79fdf74db6a8784948b958d52a-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_d2ae0c79fdf74db6a8784948b958d52a-5"&gt;&lt;/a&gt;i1optimized == 1
&lt;a id="rest_code_d2ae0c79fdf74db6a8784948b958d52a-6" name="rest_code_d2ae0c79fdf74db6a8784948b958d52a-6" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_d2ae0c79fdf74db6a8784948b958d52a-6"&gt;&lt;/a&gt;i2optimized == 1
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we continue with the remaining operations of the two traces (lines 6-8).&lt;/p&gt;
&lt;p&gt;We start by adding the &lt;code class="docutils literal"&gt;int_lt&lt;/code&gt; operation in the unoptimized trace to the Z3
formulas:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code text"&gt;&lt;a id="rest_code_c027b29cdefb42bf8e008bfe402cf07d-1" name="rest_code_c027b29cdefb42bf8e008bfe402cf07d-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_c027b29cdefb42bf8e008bfe402cf07d-1"&gt;&lt;/a&gt;...
&lt;a id="rest_code_c027b29cdefb42bf8e008bfe402cf07d-2" name="rest_code_c027b29cdefb42bf8e008bfe402cf07d-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_c027b29cdefb42bf8e008bfe402cf07d-2"&gt;&lt;/a&gt;i3unoptimized == If(input_i0 &amp;lt; 6, 1, 0)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Because the second guard was optimized away, we need to ask Z3 to prove that
&lt;code class="docutils literal"&gt;i3unoptimized == 1&lt;/code&gt; is always true, which fails and gives the following
counterexample:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code text"&gt;&lt;a id="rest_code_2cfc1001ad14432bb83b758409b9e5c2-1" name="rest_code_2cfc1001ad14432bb83b758409b9e5c2-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_2cfc1001ad14432bb83b758409b9e5c2-1"&gt;&lt;/a&gt;input_i0 = 9223372036854775800
&lt;a id="rest_code_2cfc1001ad14432bb83b758409b9e5c2-2" name="rest_code_2cfc1001ad14432bb83b758409b9e5c2-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_2cfc1001ad14432bb83b758409b9e5c2-2"&gt;&lt;/a&gt;i1unoptimized = 9223372036854775810
&lt;a id="rest_code_2cfc1001ad14432bb83b758409b9e5c2-3" name="rest_code_2cfc1001ad14432bb83b758409b9e5c2-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_2cfc1001ad14432bb83b758409b9e5c2-3"&gt;&lt;/a&gt;i2unoptimized = 0
&lt;a id="rest_code_2cfc1001ad14432bb83b758409b9e5c2-4" name="rest_code_2cfc1001ad14432bb83b758409b9e5c2-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_2cfc1001ad14432bb83b758409b9e5c2-4"&gt;&lt;/a&gt;i1optimized = 9223372036854775810
&lt;a id="rest_code_2cfc1001ad14432bb83b758409b9e5c2-5" name="rest_code_2cfc1001ad14432bb83b758409b9e5c2-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_2cfc1001ad14432bb83b758409b9e5c2-5"&gt;&lt;/a&gt;i2optimized = 1
&lt;a id="rest_code_2cfc1001ad14432bb83b758409b9e5c2-6" name="rest_code_2cfc1001ad14432bb83b758409b9e5c2-6" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_2cfc1001ad14432bb83b758409b9e5c2-6"&gt;&lt;/a&gt;i3unoptimized = 0
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Thus demonstrating the bug. The fact that the Z3-based equivalence check also
managed to find the original motivating bug without manually translating it to
a formula is a good confirmation that the approach works.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="second-bug"&gt;
&lt;h2&gt;Second bug&lt;/h2&gt;
&lt;p&gt;So with this code I applied the Z3-based equivalence check to all our optimizer
unit tests. In addition to the bug we've been discussing the whole post, it also
found another buggy test! I had found it too by hand by staring at all the tests
in the process of writing all the Z3 infrastructure, but it was still a good
confirmation that the process worked. This bug was in the range analysis for
&lt;code class="docutils literal"&gt;int_neg&lt;/code&gt;, integer negation. It failed to account that &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;-MININT&lt;/span&gt; == MININT&lt;/code&gt;
and therefore did a mis-optimization along the following lines:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-1" name="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;__pypy__&lt;/span&gt;
&lt;a id="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-2" name="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-2"&gt;&lt;/a&gt;
&lt;a id="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-3" name="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-3"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;wrong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-4" name="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-4"&gt;&lt;/a&gt;    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;__pypy__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-5" name="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-5"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-6" name="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-6" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-6"&gt;&lt;/a&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-7" name="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-7" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-7"&gt;&lt;/a&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;a id="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-8" name="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-8" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-8"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a id="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-9" name="rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-9" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_3ed3a9ef970d4b91882a00ae8d1fa009-9"&gt;&lt;/a&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Which was wrongly optimized into:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_f2cf5d53f95b41ebb02d5772b4407de0-1" name="rest_code_f2cf5d53f95b41ebb02d5772b4407de0-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_f2cf5d53f95b41ebb02d5772b4407de0-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;__pypy__&lt;/span&gt;
&lt;a id="rest_code_f2cf5d53f95b41ebb02d5772b4407de0-2" name="rest_code_f2cf5d53f95b41ebb02d5772b4407de0-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_f2cf5d53f95b41ebb02d5772b4407de0-2"&gt;&lt;/a&gt;
&lt;a id="rest_code_f2cf5d53f95b41ebb02d5772b4407de0-3" name="rest_code_f2cf5d53f95b41ebb02d5772b4407de0-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_f2cf5d53f95b41ebb02d5772b4407de0-3"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;wrong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_f2cf5d53f95b41ebb02d5772b4407de0-4" name="rest_code_f2cf5d53f95b41ebb02d5772b4407de0-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_f2cf5d53f95b41ebb02d5772b4407de0-4"&gt;&lt;/a&gt;    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;__pypy__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_f2cf5d53f95b41ebb02d5772b4407de0-5" name="rest_code_f2cf5d53f95b41ebb02d5772b4407de0-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_f2cf5d53f95b41ebb02d5772b4407de0-5"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_f2cf5d53f95b41ebb02d5772b4407de0-6" name="rest_code_f2cf5d53f95b41ebb02d5772b4407de0-6" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_f2cf5d53f95b41ebb02d5772b4407de0-6"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;a id="rest_code_f2cf5d53f95b41ebb02d5772b4407de0-7" name="rest_code_f2cf5d53f95b41ebb02d5772b4407de0-7" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_f2cf5d53f95b41ebb02d5772b4407de0-7"&gt;&lt;/a&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is wrong precisely for &lt;code class="docutils literal"&gt;x == MININT&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="generating-random-traces"&gt;
&lt;h2&gt;Generating Random Traces&lt;/h2&gt;
&lt;p&gt;These two bugs were the only two that the Z3 checker found for existing unit
tests. To try to find some more bugs I combined PyPy's existing random trace
generator with the Z3 optimization checker. The random trace generator has so
far been mostly used to find bugs in the machine code backends, particularly
also in the register allocator. So far we haven't used it with our optimizer,
but my experiments show that we should have!&lt;/p&gt;
&lt;p&gt;I'm going to describe a little bit how the random trace generator works. It's
actually not that complicated, but there's one neat trick to it.&lt;/p&gt;
&lt;p&gt;The basic idea is straightforward, it starts out with an empty trace with a
random number of input variables. Then it adds some number of operations to the
trace, either regular operations or guards. Every operation takes already
existing variables as input.&lt;/p&gt;
&lt;p&gt;The neat trick is that our random trace generator keeps a concrete random
example value for every one of the input variables, and an example result for
every operation. In this way, it is possible to generate guards that are
consistent with the example values to ensure that running the trace to its end
is possible with at least one set of values.&lt;/p&gt;
&lt;p&gt;Here's an example random trace that is generated, together with the random
example inputs and the results of every operation at the end of every line:&lt;/p&gt;
&lt;pre class="literal-block"&gt;[i0, i1, i2, i3, i4, i5] # example values: 9, 11, -8, -95, 46, 57
i6 = int_add_ovf(i3, i0) # -86
guard_no_overflow()
i7 = int_sub(i2, -35/ci) # 27
i8 = uint_ge(i3, i5) # 1
guard_true(i8)
i9 = int_lt(i7, i8) # 0
i10 = int_mul_ovf(34/ci, i7) # 918
guard_no_overflow()
i11 = int_and(i10, 63/ci) # 22
i12 = int_rshift(i3, i11) # -1
i13 = int_is_zero(i7) # 0
i14 = int_is_true(i13) # 0
guard_false(i13)
i15 = int_lt(i8, i4) # 1
i16 = int_and(i6, i0) # 8
i17 = uint_ge(i6, -6/ci) # 0
finish()&lt;/pre&gt;
&lt;p&gt;Note how every guard generated is true for the example values.&lt;/p&gt;
&lt;p&gt;I have been running this combination of random trace generation and Z3 checking
for many nights and it has found some bugs, which I'll describe in the next
section. It should probably be run for a lot longer, but still a useful
exercise already.&lt;/p&gt;
&lt;p&gt;In this mode, I'm giving every Z3 call a time limit to make sure that the random
tests don't just take arbitrarily long. This means that asking Z3 to prove
something can have three outcomes, either it's proved, or Z3 finds a
counterexample, or Z3 times out.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="bugs-found"&gt;
&lt;h2&gt;Bugs Found&lt;/h2&gt;
&lt;p&gt;In addition to the two bugs I've already described, I'll briefly list the
additional bugs that were found by optimizing random traces and then trying to
prove the equivalence with Z3.&lt;/p&gt;
&lt;p&gt;Most of the bugs were actually identified by optimizing random traces alone, not
by the Z3 component. They manifested as assert failures in the JIT compiler.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;The JIT concluded after &lt;code class="docutils literal"&gt;12 == int_mul(x, 12)&lt;/code&gt; that &lt;code class="docutils literal"&gt;x == 1&lt;/code&gt;, which is
incorrect if overflow occurred (a counterexample is &lt;code class="docutils literal"&gt;0x8000000000000001&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An amusing bug, where from &lt;code class="docutils literal"&gt;0 == int_lshift(0x1000000000000000, x)&lt;/code&gt; with
&lt;code class="docutils literal"&gt;x &amp;lt;= 0 &amp;lt;= 15&lt;/code&gt;, the JIT concluded that &lt;code class="docutils literal"&gt;0x1000000000000000 == 0&lt;/code&gt;,
triggering an assert. This wrong conclusion was again caused by not taking the
possibility of overflow into account.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A corner case in an optimization for chained integer additions with a
constant, where in complex enough expressions, the wrong IR API was used
(which works correctly in simple cases). Again, this triggered an assert.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This shows that we should have been fuzzing our JIT optimizer already (not a
surprising  observation in hindsight, fuzz all the things!).&lt;/p&gt;
&lt;p&gt;Thankfully, there was also one further bug that really failed in the Z3
verifier. It's a bug in common subexpression elimination / arithmetic
simplification, which again does not take overflow correctly into account.&lt;/p&gt;
&lt;p&gt;The buggy trace looks like this (unfortunately it's not easily possible to show
this bug in Python code).&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code text"&gt;&lt;a id="rest_code_4157e6ed8705483a92afad6c285d4a94-1" name="rest_code_4157e6ed8705483a92afad6c285d4a94-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4157e6ed8705483a92afad6c285d4a94-1"&gt;&lt;/a&gt;[a, b]
&lt;a id="rest_code_4157e6ed8705483a92afad6c285d4a94-2" name="rest_code_4157e6ed8705483a92afad6c285d4a94-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4157e6ed8705483a92afad6c285d4a94-2"&gt;&lt;/a&gt;c = int_add(a, b)
&lt;a id="rest_code_4157e6ed8705483a92afad6c285d4a94-3" name="rest_code_4157e6ed8705483a92afad6c285d4a94-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4157e6ed8705483a92afad6c285d4a94-3"&gt;&lt;/a&gt;r = int_sub_ovf(c, b)
&lt;a id="rest_code_4157e6ed8705483a92afad6c285d4a94-4" name="rest_code_4157e6ed8705483a92afad6c285d4a94-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4157e6ed8705483a92afad6c285d4a94-4"&gt;&lt;/a&gt;guard_no_ovf()
&lt;a id="rest_code_4157e6ed8705483a92afad6c285d4a94-5" name="rest_code_4157e6ed8705483a92afad6c285d4a94-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_4157e6ed8705483a92afad6c285d4a94-5"&gt;&lt;/a&gt;finish(r)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This was optimized to:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code text"&gt;&lt;a id="rest_code_45e1051075424792bd7f55e240a7fd0b-1" name="rest_code_45e1051075424792bd7f55e240a7fd0b-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_45e1051075424792bd7f55e240a7fd0b-1"&gt;&lt;/a&gt;[a, b]
&lt;a id="rest_code_45e1051075424792bd7f55e240a7fd0b-2" name="rest_code_45e1051075424792bd7f55e240a7fd0b-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_45e1051075424792bd7f55e240a7fd0b-2"&gt;&lt;/a&gt;finish(a)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Which is incorrect, because the guard can fail given the right inputs.
But the optimizer concluded that the subtraction is safe, because its the
inverse of an earlier addition, not taking into account that this earlier
addition can have overflowed.&lt;/p&gt;
&lt;p&gt;Note that a related optimization is actually correct. Given this code:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code text"&gt;&lt;a id="rest_code_05a82e44114c40209d961464f80aee69-1" name="rest_code_05a82e44114c40209d961464f80aee69-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_05a82e44114c40209d961464f80aee69-1"&gt;&lt;/a&gt;[a, b]
&lt;a id="rest_code_05a82e44114c40209d961464f80aee69-2" name="rest_code_05a82e44114c40209d961464f80aee69-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_05a82e44114c40209d961464f80aee69-2"&gt;&lt;/a&gt;c = int_add_ovf(a, b)
&lt;a id="rest_code_05a82e44114c40209d961464f80aee69-3" name="rest_code_05a82e44114c40209d961464f80aee69-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_05a82e44114c40209d961464f80aee69-3"&gt;&lt;/a&gt;guard_no_ovf()
&lt;a id="rest_code_05a82e44114c40209d961464f80aee69-4" name="rest_code_05a82e44114c40209d961464f80aee69-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_05a82e44114c40209d961464f80aee69-4"&gt;&lt;/a&gt;r = int_sub(c, b)
&lt;a id="rest_code_05a82e44114c40209d961464f80aee69-5" name="rest_code_05a82e44114c40209d961464f80aee69-5" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_05a82e44114c40209d961464f80aee69-5"&gt;&lt;/a&gt;finish(r)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It can be optimized to:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code text"&gt;&lt;a id="rest_code_717e558ad31b40c895941f546a9f055a-1" name="rest_code_717e558ad31b40c895941f546a9f055a-1" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_717e558ad31b40c895941f546a9f055a-1"&gt;&lt;/a&gt;[a, b]
&lt;a id="rest_code_717e558ad31b40c895941f546a9f055a-2" name="rest_code_717e558ad31b40c895941f546a9f055a-2" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_717e558ad31b40c895941f546a9f055a-2"&gt;&lt;/a&gt;c = int_add_ovf(a, b)
&lt;a id="rest_code_717e558ad31b40c895941f546a9f055a-3" name="rest_code_717e558ad31b40c895941f546a9f055a-3" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_717e558ad31b40c895941f546a9f055a-3"&gt;&lt;/a&gt;guard_no_ovf()
&lt;a id="rest_code_717e558ad31b40c895941f546a9f055a-4" name="rest_code_717e558ad31b40c895941f546a9f055a-4" href="https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html#rest_code_717e558ad31b40c895941f546a9f055a-4"&gt;&lt;/a&gt;finish(a)
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="future-work-and-conclusion"&gt;
&lt;h2&gt;Future Work and Conclusion&lt;/h2&gt;
&lt;p&gt;In the current form the Z3 checker is only a start, even though it has already
been concretely useful. There are various directions into which we could extend
it. In addition to generate random tests completely from scratch, we could also
start from the existing manually written unit-tests and randomly mutate those.&lt;/p&gt;
&lt;p&gt;I also want to extend the Z3 checker with support more operations, heap
operations in particular (but it's not quite clear to me how to model garbage
collection).&lt;/p&gt;
&lt;p&gt;I also want to try to switch the code away from the Z3 API and use the more
general &lt;a class="reference external" href="https://smtlib.cs.uiowa.edu/"&gt;smtlib&lt;/a&gt; interface directly, in order to be able to use other SMT
checkers than Z3, eg &lt;a class="reference external" href="https://cvc4.github.io/"&gt;CVC4&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But all in all this was a fun and not too hard way to find a bunch of bugs in
our optimizer! And the infrastructure is now in place, which means that we run
some random test cases every time we execute our tests. This is going to be
particularly useful when we do further work on the integer reasoning of the JIT
(like Nico is doing, for example). As of time of writing of this post, all the
bugs mentioned have been fixed and the Z3 code has landed on the default branch
and runs as part of PyPy's CI infrastructure.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="acknowledgements"&gt;
&lt;h2&gt;Acknowledgements&lt;/h2&gt;
&lt;p&gt;Thanks to &lt;a class="reference external" href="http://saambarati.org/"&gt;Saam Barati&lt;/a&gt;, &lt;a class="reference external" href="https://bernsteinbear.com"&gt;Max Bernstein&lt;/a&gt;, &lt;a class="reference external" href="https://www.cs.hhu.de/lehrstuehle-und-arbeitsgruppen/softwaretechnik-und-programmiersprachen/unser-team/team/schmidt"&gt;Joshua Schmidt&lt;/a&gt; and &lt;a class="reference external" href="https://martinfriedrichberger.net/"&gt;Martin
Berger&lt;/a&gt;, for great feedback on drafts of this post!&lt;/p&gt;
&lt;/section&gt;</description><category>jit</category><category>testing</category><category>z3</category><guid>https://www.pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.html</guid><pubDate>Sun, 11 Dec 2022 18:00:00 GMT</pubDate></item></channel></rss>