<?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</title><link>https://www.pypy.org/</link><description>A Faster Python</description><atom:link href="https://www.pypy.org/rss.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:16 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>PyPy v8.0.0 release</title><link>https://www.pypy.org/posts/2026/09/pypy-v800-release.html</link><dc:creator>mattip</dc:creator><description>&lt;section id="pypy-v8-0-0-release-of-python-2-7-3-11-and-3-12-beta-released-2026-09-19"&gt;
&lt;h2&gt;PyPy v8.0.0: release of python 2.7, 3.11, and 3.12 beta released 2026-09-19&lt;/h2&gt;
&lt;p&gt;The PyPy team is proud to release version 8.0.0 of PyPy after the previous
release on May 26, 2026. This is a major new version, hence the bump to 8.0.0.
It is our first release of Python 3.12, which may still have some bugs so we
are calling it "beta" quality.&lt;/p&gt;
&lt;section id="why-the-move-to-8-0-0"&gt;
&lt;h3&gt;Why the move to 8.0.0&lt;/h3&gt;
&lt;section id="glibc2-28"&gt;
&lt;h4&gt;glibc2.28&lt;/h4&gt;
&lt;p&gt;We have updated our linux buildbots (linux64, linux32, aarch64) to use
manylinux_2_28 images based on AlmaLinux 8 and glibc 2.28. These use gcc14
instead of the gcc5 previously used.  So our compiled tarballs will require at
least glibc2.28, which should be universally supported by now (Ubuntu 24.04
uses glibc2.39). In order to prevent confusion, we felt bumping the major
version would be prudent.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="cp12-abi3-support"&gt;
&lt;h4&gt;cp12-abi3 support&lt;/h4&gt;
&lt;p&gt;PyPy's Python 3.12 support comes with a new model for the C layer &lt;code class="docutils literal"&gt;PyObject&lt;/code&gt;.
In order to link the C object to the internal RPython one, we have an extra
field in the object &lt;code class="docutils literal"&gt;ob_pypy_link&lt;/code&gt;, as described in-depth in
&lt;a class="reference external" href="https://doc.pypy.org/discussion/rawrefcount.html"&gt;rawrefcount-and-the-gc&lt;/a&gt;. In previous versions, this field was
visible in a way that makes the &lt;code class="docutils literal"&gt;PyObject&lt;/code&gt; struct different from the CPython
one. From v8.0.0, we "hide" the PyPy-only extension in a prefix before
the pointer we hand off to C-extension modules. The goal of this work is to
allow PyPy to use cp312-abi3 wheels produced for CPython 3.12 and up, using the
limited ABI. The required pieces have all been put in place:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;PyPy's C headers, including struct definitions like &lt;code class="docutils literal"&gt;PyObject&lt;/code&gt;, are
compatible with CPython's C headers when defining
&lt;code class="docutils literal"&gt;Py_LIMITED_API=0x030C0000&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PyPy no longer mangles exported function names from the limited API.
In PyPy3.11 and earlier, functions like &lt;code class="docutils literal"&gt;PyTuple_New&lt;/code&gt; were exported as
&lt;code class="docutils literal"&gt;PyPyTupleNew&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Still missing: the import machinery must be taught that abi3.so shared objects
are valid for PyPy, and the larger ecosystem (pip, uv) must also accept that
cp312-abi3 wheels are valid candidates for installation.&lt;/p&gt;
&lt;p&gt;Yes, this is a big step. We are working with Cython and PyO3 to make sure it
all will Just Work™. Hopefully this will make it easier for packages to
support PyPy.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="what-is-new-in-rpython-code-generation"&gt;
&lt;h3&gt;What is new in RPython code generation&lt;/h3&gt;
&lt;p&gt;PyPy is written in RPython, and has code generation to translate RPython into
C as part of the VM build process. We have made some improvements to code
generation in attempts to speed up the base interpreter. While the speedups
have not been that impressive, we have made some steps forward:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;We now use &lt;a class="reference external" href="https://eli.thegreenplace.net/2012/07/12/computed-goto-for-efficient-dispatch-tables"&gt;computed gotos&lt;/a&gt; and more aggressively inline code. While this
produces more compact sources, it does not boost performance as much as we
wished.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The source code includes comments mapping the source back to the RPython code
that generated the block. This is very helpful to see exactly what is going on,
and may enable further improvements.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="dropping-hpy"&gt;
&lt;h3&gt;Dropping HPy&lt;/h3&gt;
&lt;p&gt;We have dropped the internal &lt;a class="reference external" href="https://hpyproject.org/"&gt;HPy&lt;/a&gt; backend for PyPy. The HPy project's
understanding of how to use handles instead of pointers was a good prototype,
but the project did not attract enough supporters to become a new standard. The
code is still in the PyPy codebase, and can be toggled on with a &lt;a class="reference external" href="https://doc.pypy.org/config/commandline.html#pypy-python-interpreter-options"&gt;build option&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="a-revived-tool-comparing-headers-and-exported-functions"&gt;
&lt;h3&gt;A revived tool comparing headers and exported functions&lt;/h3&gt;
&lt;p&gt;We revived the &lt;a class="reference external" href="https://github.com/pypy/pypy/issues/3397#issuecomment-1872091878"&gt;clang-based pyhdrdump&lt;/a&gt; to compare PyPy's header files to
CPython's header files. See &lt;a class="reference external" href="https://github.com/pypy/pypy/tree/py3.12/pypy/tool/pyhdrdump#pyhdrdump"&gt;the README&lt;/a&gt; for more information on how it works
and how to use it.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="interpreters"&gt;
&lt;h3&gt;Interpreters&lt;/h3&gt;
&lt;p&gt;The release includes three different interpreters:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;PyPy2.7, supporting the syntax and the features of
Python 2.7 including the stdlib for CPython 2.7.18+ (the &lt;code class="docutils literal"&gt;+&lt;/code&gt; is for
backported security updates)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PyPy3.11, supporting the syntax and the features of
Python 3.11, including the stdlib for CPython 3.11.16. Barring security
issues, this will be the last release to support 3.11.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PyPy3.12, supporting the syntax and features of Python 3.12, including the
stdlib for CPython 3.12.14.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The interpreters are based on much the same codebase, thus the triple
release.&lt;/p&gt;
&lt;p&gt;We recommend updating. You can find links to download the releases here:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a class="reference external" href="https://pypy.org/download.html"&gt;https://pypy.org/download.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We would like to thank our donors for the continued support of the PyPy
project. If PyPy is not quite good enough for your needs, we are available for
&lt;a class="reference external" href="https://www.pypy.org/pypy-sponsors.html"&gt;direct consulting&lt;/a&gt; work. If PyPy is helping you out, we would love to hear
about it and encourage submissions to our &lt;a class="reference external" href="https://pypy.org/blog"&gt;blog&lt;/a&gt; via a pull request
to &lt;a class="reference external" href="https://github.com/pypy/pypy.org"&gt;https://github.com/pypy/pypy.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We would also like to thank our contributors and encourage new people to join
the project. PyPy has many layers and we need help with all of them: bug fixes,
&lt;a class="reference external" href="https://doc.pypy.org/"&gt;PyPy&lt;/a&gt; and &lt;a class="reference external" href="https://rpython.readthedocs.org"&gt;RPython&lt;/a&gt; documentation improvements, or general &lt;a class="reference external" href="https://doc.pypy.org/project-ideas.html"&gt;help&lt;/a&gt; with
making RPython's JIT even better.&lt;/p&gt;
&lt;p&gt;If you are a python library maintainer and use C-extensions, please consider
making a &lt;a class="reference external" href="https://cffi.readthedocs.io"&gt;CFFI&lt;/a&gt; version of your library that would be performant
on PyPy. Failing that, PyPy will soon support the cp312-abi3 tag for limited
ABI wheels.  In any case, &lt;a class="reference external" href="https://github.com/joerick/cibuildwheel"&gt;cibuildwheel&lt;/a&gt; supports building wheels for PyPy.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="what-is-pypy"&gt;
&lt;h3&gt;What is PyPy?&lt;/h3&gt;
&lt;p&gt;PyPy is a Python interpreter, a drop-in replacement for CPython.
It's fast (&lt;a class="reference external" href="https://speed.pypy.org"&gt;PyPy and CPython&lt;/a&gt; performance
comparison) due to its integrated tracing JIT compiler.&lt;/p&gt;
&lt;p&gt;We also welcome developers of other &lt;a class="reference external" href="https://rpython.readthedocs.io/en/latest/examples.html"&gt;dynamic languages&lt;/a&gt; to see what RPython
can do for them.&lt;/p&gt;
&lt;p&gt;We provide binary builds for:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;x86&lt;/strong&gt; machines on most common operating systems
(Linux 32/64 bits, Mac OS 64 bits, Windows 64 bits)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;64-bit &lt;strong&gt;ARM&lt;/strong&gt; machines running Linux (&lt;code class="docutils literal"&gt;aarch64&lt;/code&gt;) and macos (&lt;code class="docutils literal"&gt;macos_arm64&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PyPy supports Windows 32-bit, Linux PPC64 big- and little-endian, Linux ARM
32 bit, RISC-V RV64IMAFD Linux, and s390x Linux but does not release binaries.
Please reach out to us if you wish to sponsor binary releases for those
platforms. Downstream packagers provide binary builds for debian, Fedora,
conda, OpenBSD, FreeBSD, Gentoo, and more.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="what-else-is-new"&gt;
&lt;h3&gt;What else is new?&lt;/h3&gt;
&lt;p&gt;For more information about the 8.0.0 release, see the &lt;a class="reference external" href="https://doc.pypy.org/en/latest/release-v8.0.0.html#changelog"&gt;full changelog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please update, and continue to help us make pypy better.&lt;/p&gt;
&lt;p&gt;Cheers,
The PyPy Team&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;</description><category>release</category><guid>https://www.pypy.org/posts/2026/09/pypy-v800-release.html</guid><pubDate>Fri, 18 Sep 2026 11:00:00 GMT</pubDate></item><item><title>Moving linux builds to GLIBC==2.28</title><link>https://www.pypy.org/posts/2026/07/moving-linux-builds-to-glibc228.html</link><dc:creator>mattip</dc:creator><description>&lt;p&gt;A short note for visibility.&lt;/p&gt;
&lt;p&gt;PyPy builds tarballs of the python interpreter &lt;a href="https://downloads.python.org/pypy/"&gt;ready for
download&lt;/a&gt;. These include the latest
releases and also nightly builds, fresh from our fleet of buildbots. Over the
next couple of days, the nightly builds on linux will transition from
&lt;code&gt;manylinux2014&lt;/code&gt; based docker images &lt;a href="https://github.com/pypy/pypy-ci"&gt;to &lt;code&gt;manylinux2_28&lt;/code&gt;
images&lt;/a&gt;. The practical implication is that
nightly images, and the next releases, will require a minimum of &lt;code&gt;GLIBC&amp;gt;=2.28&lt;/code&gt;,
i.e. AlmaLinux8, amanzonlinux 2023, debian 10, ubuntu 20.04. For a good
overview of how this glibc/distro/manylinux all works, see &lt;a href="https://github.com/mayeut/pep600_compliance"&gt;the PEP 600 compliance
page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The next release will indicate this change by a new PyPy major version, 8.0.0.
It should include a Python3.12 interpreter, in which case it will be the last
release of the Python 3.11 interpreter.&lt;/p&gt;</description><guid>https://www.pypy.org/posts/2026/07/moving-linux-builds-to-glibc228.html</guid><pubDate>Sat, 18 Jul 2026 18:09:11 GMT</pubDate></item><item><title>A new benchmark runner for PyPy</title><link>https://www.pypy.org/posts/2026/06/benchmarker2-for-pypy.html</link><dc:creator>mattip</dc:creator><description>&lt;p&gt;The &lt;a href="https://speed.pypy.org"&gt;https://speed.pypy.org&lt;/a&gt; site has been running the &lt;a href="https://foss.heptapod.net/pypy/benchmarks"&gt;PyPy benchmark
suite&lt;/a&gt; since 2010. Our
first benchmarking machine was called tannit, and it faithfully ran the suite
from May 2010 to Dec 2016. For a brief period in the middle we had a machine
called speed-python, but tannit was the gold standard. In June 2016 we started
running benchmarks on our current machine, &lt;strong&gt;benchmarker&lt;/strong&gt; (Intel i7-7700). It has been graciously
sponsored by &lt;a href="https://baroquesoftware.com/"&gt;Baroque Software&lt;/a&gt;. Based on an Ubuntu xenial
chroot, the machine has been quite stable but over the years has had a few
kernel exploits blocked in firmware that changed its base performance.&lt;/p&gt;
&lt;p&gt;It is time to update. Rather than use the same machine with updated software,
we decided to opt for different hardware. Since the beginning of May we have been
running the benchmark suite on &lt;strong&gt;benchmarker2&lt;/strong&gt;: an AMD Ryzen 5 3600 machine. In
order to try to stabilize benchmarks the machine was set up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;without SMT (hyper-threading)&lt;/li&gt;
&lt;li&gt;using &lt;code&gt;cpuset&lt;/code&gt; to partition CPUs 3,4,5 off (the CPU has 2 CCD chiplets so the
  CPU sets are truly independent, the reason we chose the Zen2 architecture)
  and use them exclusively for benchmarking&lt;/li&gt;
&lt;li&gt;disable turbo speed strategy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It runs debian13 as a base operating system, and the benchmarks run in a
&lt;code&gt;manylinux2_28&lt;/code&gt; docker, which provides gcc14.&lt;/p&gt;
&lt;p&gt;In order to establish a baseline, I compiled CPython 3.11.5 with:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;./configure --prefix=/opt/cpython-3.11 --enable-optimizations \
--with-computed-gotos --enable-shared LDFLAGS='-Wl,-rpath,\$$ORIGIN/../lib'
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The difference between the two machines is striking: where the xenial image
(with GCC 5.4) benchmark comparison to CPython 3.11.9 shows a 3x improvement
when run on PyPy on benchmarker, the newer machine with the newer compiler and
a fresh baseline shows a 4.3x improvement.  I can only speculate that the major
differences between the results is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The CPython 3.11.9 run was done in June 2024. This was before some firmware
  kernel changes applied to the host machine that slowed it down. I did notice
  at the time the exploit migitagion firmware was applied that the overall
  comparison dropped from 3.3x to 3x, but felt the additional protection was
  warrented.&lt;/li&gt;
&lt;li&gt;The newer software image uses GCC 14, where the older one used GCC 5.&lt;/li&gt;
&lt;li&gt;The AMD machine has 32MB of L3 cache, the Intel machine has 8MB.&lt;/li&gt;
&lt;li&gt;The AMD machine uses RAM at 3200MHz, the Intel at 2400MHz.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The last 3 points may affect PyPy more than CPython, since PyPy's JIT is more
memory intensive and the RPython codegen may be handled better by newer compilers.&lt;/p&gt;
&lt;p&gt;This is the first step in an overhaul of PyPy's infrastructure. Other plans in
the pipeline:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Move all the buildbot builds from &lt;code&gt;manylinux_2014&lt;/code&gt; to &lt;code&gt;manylinux2_28&lt;/code&gt;-based
  images. This will match the move on benchmarker2. It will require some
  adaptations so that tests will pass on the newer compiler, see
  &lt;a href="https://github.com/pypy/pypy/pull/5488"&gt;pypy/pypy#5488&lt;/a&gt;. This will mean an ABI break,
  so the next PyPy release will leave behind the 7.3.x series.&lt;/li&gt;
&lt;li&gt;Think about &lt;a href="https://github.com/pypy/buildbot/issues/1"&gt;updating our use of buildbot 0.8.8&lt;/a&gt;,
  which is woefully out of date. Since we have a heavily customized &lt;a href="https://buildbot.pypy.org/summary?branch=py3.11"&gt;summary
  page&lt;/a&gt;, and the twistd-based
  endpoints are not supported on buildbot 0.9 and up, we set up a
  &lt;a href="https://build-summary.pypy.org/summary?branch=py3.11"&gt;build-summary&lt;/a&gt;
  alternative that is synchronized to the buildbot work.&lt;/li&gt;
&lt;li&gt;Perhaps make more use of the free GitHub actions workers to replace or
  enhance the buildbot workers. Some of that can be seen in PR 5488. The
  build-summary service is also able to ingest github action testing results.&lt;/li&gt;
&lt;li&gt;Continue to push on in CPython compatibility, performance improvements, and
  bugfixes, as well as work on a &lt;a href="https://github.com/pypy/pypy/issues?q=is%3Aissue%20state%3Aopen%20milestone%3A%22Python%203.12%22"&gt;PyPy 3.12
  version&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Help of course is welcome.&lt;/p&gt;
&lt;p&gt;Matti&lt;/p&gt;</description><category>benchmarks</category><category>infrastructure</category><category>performance</category><guid>https://www.pypy.org/posts/2026/06/benchmarker2-for-pypy.html</guid><pubDate>Sun, 14 Jun 2026 15:07:09 GMT</pubDate></item><item><title>PyPy v7.3.23 release</title><link>https://www.pypy.org/posts/2026/05/pypy-v7322-release.html</link><dc:creator>mattip</dc:creator><description>&lt;section id="pypy-v7-3-23-release-of-python-2-7-3-11"&gt;
&lt;h2&gt;PyPy v7.3.23: release of python 2.7, 3.11&lt;/h2&gt;
&lt;p&gt;The PyPy team is proud to release version 7.3.23 of PyPy after the previous
release on April 26, 2026. This is a bug-fix release that fixes an overeager
warning about unused coroutines, and some problems around multiple inheritance
in c-extensions.&lt;/p&gt;
&lt;p&gt;This version includes a change to the bytecode interpreter to use &lt;a class="reference external" href="https://github.com/python/cpython/blob/main/InternalDocs/exception_handling.md"&gt;exception tables&lt;/a&gt;
instead of dedicated opcodes. Now the PyPy disassembly will be closer to
CPython format. So far it does not impact performance.&lt;/p&gt;
&lt;p&gt;The release includes two different interpreters:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;PyPy2.7, which is an interpreter supporting the syntax and the features of
Python 2.7 including the stdlib for CPython 2.7.18+ (the &lt;code class="docutils literal"&gt;+&lt;/code&gt; is for
backported security updates)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PyPy3.11, which is an interpreter supporting the syntax and the features of
Python 3.11, including the stdlib for CPython 3.11.15.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The interpreters are based on much the same codebase, thus the double
release. This is a micro release, all APIs are compatible with the other 7.3
releases.&lt;/p&gt;
&lt;p&gt;We recommend updating. You can find links to download the releases here:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a class="reference external" href="https://pypy.org/download.html"&gt;https://pypy.org/download.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We would like to thank our donors for the continued support of the PyPy
project. If PyPy is not quite good enough for your needs, we are available for
&lt;a class="reference external" href="https://www.pypy.org/pypy-sponsors.html"&gt;direct consulting&lt;/a&gt; work. If PyPy is helping you out, we would love to hear
about it and encourage submissions to our &lt;a class="reference external" href="https://pypy.org/blog"&gt;blog&lt;/a&gt; via a pull request
to &lt;a class="reference external" href="https://github.com/pypy/pypy.org"&gt;https://github.com/pypy/pypy.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We would also like to thank our contributors and encourage new people to join
the project. PyPy has many layers and we need help with all of them: bug fixes,
&lt;a class="reference external" href="https://doc.pypy.org/"&gt;PyPy&lt;/a&gt; and &lt;a class="reference external" href="https://rpython.readthedocs.org"&gt;RPython&lt;/a&gt; documentation improvements, or general &lt;a class="reference external" href="https://doc.pypy.org/project-ideas.html"&gt;help&lt;/a&gt; with
making RPython's JIT even better.&lt;/p&gt;
&lt;p&gt;If you are a python library maintainer and use C-extensions, please consider
making a &lt;a class="reference external" href="https://hpyproject.org/"&gt;HPy&lt;/a&gt; / &lt;a class="reference external" href="https://cffi.readthedocs.io"&gt;CFFI&lt;/a&gt; / &lt;a class="reference external" href="https://cppyy.readthedocs.io"&gt;cppyy&lt;/a&gt; version of your library that would be performant
on PyPy. In any case, &lt;a class="reference external" href="https://github.com/joerick/cibuildwheel"&gt;cibuildwheel&lt;/a&gt; supports building wheels for PyPy.&lt;/p&gt;
&lt;section id="what-is-pypy"&gt;
&lt;h3&gt;What is PyPy?&lt;/h3&gt;
&lt;p&gt;PyPy is a Python interpreter, a drop-in replacement for CPython.
It's fast (&lt;a class="reference external" href="https://speed.pypy.org"&gt;PyPy and CPython&lt;/a&gt; performance
comparison) due to its integrated tracing JIT compiler.&lt;/p&gt;
&lt;p&gt;We also welcome developers of other &lt;a class="reference external" href="https://rpython.readthedocs.io/en/latest/examples.html"&gt;dynamic languages&lt;/a&gt; to see what RPython
can do for them.&lt;/p&gt;
&lt;p&gt;We provide binary builds for:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;x86&lt;/strong&gt; machines on most common operating systems
(Linux 32/64 bits, Mac OS 64 bits, Windows 64 bits)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;64-bit &lt;strong&gt;ARM&lt;/strong&gt; machines running Linux (&lt;code class="docutils literal"&gt;aarch64&lt;/code&gt;) and macos (&lt;code class="docutils literal"&gt;macos_arm64&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PyPy supports Windows 32-bit, Linux PPC64 big- and little-endian, Linux ARM
32 bit, RISC-V RV64IMAFD Linux, and s390x Linux but does not release binaries.
Please reach out to us if you wish to sponsor binary releases for those
platforms. Downstream packagers provide binary builds for debian, Fedora,
conda, OpenBSD, FreeBSD, Gentoo, and more.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="what-else-is-new"&gt;
&lt;h3&gt;What else is new?&lt;/h3&gt;
&lt;p&gt;For more information about the 7.3.23 release, see the &lt;a class="reference external" href="https://doc.pypy.org/en/latest/release-v7.3.23.html#changelog"&gt;full changelog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please update, and continue to help us make pypy better.&lt;/p&gt;
&lt;p&gt;Cheers,
The PyPy Team&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;</description><category>release</category><guid>https://www.pypy.org/posts/2026/05/pypy-v7322-release.html</guid><pubDate>Wed, 27 May 2026 10:00:00 GMT</pubDate></item><item><title>PyPy v7.3.22 release</title><link>https://www.pypy.org/posts/2026/04/pypy-v7322-release.html</link><dc:creator>mattip</dc:creator><description>&lt;section id="pypy-v7-3-22-release-of-python-2-7-3-11"&gt;
&lt;h2&gt;PyPy v7.3.22: release of python 2.7, 3.11&lt;/h2&gt;
&lt;p&gt;The PyPy team is proud to release version 7.3.22 of PyPy after the previous
release on March 13, 2026. This is a bug-fix release that fixes several issues
in the JIT. Among them, a long-standing JIT bug that started appearing when
some instance optimizations exposed it. We also cleaned
up many of the remaining stdlib test suite failures, which improves CPython
compatibility around line numbers in dis.dis, signatures and objclass
attributes for builtins, and other quality of life features.&lt;/p&gt;
&lt;p&gt;There is now an RPython &lt;code class="docutils literal"&gt;_pickle&lt;/code&gt; module that mirrors
the CPython one, greatly speeding up pickling operations. Where before PyPy was
5.7x slower than CPython on the pickle benchmark from the pyperformance
benchmark suite, now it is only 1.6x slower &lt;a class="brackets" href="https://www.pypy.org/posts/2026/04/pypy-v7322-release.html#footnote-1" id="footnote-reference-1" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;0&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt;. We also added pypy
pickler extensions to dump and load lists using list strategies, and enabled
them in the &lt;code class="docutils literal"&gt;ForkingPickler&lt;/code&gt; used by multiprocessing, speeding up cases where
such objects are passed between PyPy multiprocessing instances.&lt;/p&gt;
&lt;p&gt;We also added an RPython json encoder, speeding up json_bench from being 2.6x
slower than CPython to being 0.7x (meaning faster).&lt;/p&gt;
&lt;p&gt;The release includes two different interpreters:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;PyPy2.7, which is an interpreter supporting the syntax and the features of
Python 2.7 including the stdlib for CPython 2.7.18+ (the &lt;code class="docutils literal"&gt;+&lt;/code&gt; is for
backported security updates)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PyPy3.11, which is an interpreter supporting the syntax and the features of
Python 3.11, including the stdlib for CPython 3.11.15.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The interpreters are based on much the same codebase, thus the double
release. This is a micro release, all APIs are compatible with the other 7.3
releases.&lt;/p&gt;
&lt;p&gt;We recommend updating. You can find links to download the releases here:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a class="reference external" href="https://pypy.org/download.html"&gt;https://pypy.org/download.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We would like to thank our donors for the continued support of the PyPy
project. If PyPy is not quite good enough for your needs, we are available for
&lt;a class="reference external" href="https://www.pypy.org/pypy-sponsors.html"&gt;direct consulting&lt;/a&gt; work. If PyPy is helping you out, we would love to hear
about it and encourage submissions to our &lt;a class="reference external" href="https://pypy.org/blog"&gt;blog&lt;/a&gt; via a pull request
to &lt;a class="reference external" href="https://github.com/pypy/pypy.org"&gt;https://github.com/pypy/pypy.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We would also like to thank our contributors and encourage new people to join
the project. PyPy has many layers and we need help with all of them: bug fixes,
&lt;a class="reference external" href="https://doc.pypy.org/"&gt;PyPy&lt;/a&gt; and &lt;a class="reference external" href="https://rpython.readthedocs.org"&gt;RPython&lt;/a&gt; documentation improvements, or general &lt;a class="reference external" href="https://doc.pypy.org/project-ideas.html"&gt;help&lt;/a&gt; with
making RPython's JIT even better.&lt;/p&gt;
&lt;p&gt;If you are a python library maintainer and use C-extensions, please consider
making a &lt;a class="reference external" href="https://hpyproject.org/"&gt;HPy&lt;/a&gt; / &lt;a class="reference external" href="https://cffi.readthedocs.io"&gt;CFFI&lt;/a&gt; / &lt;a class="reference external" href="https://cppyy.readthedocs.io"&gt;cppyy&lt;/a&gt; version of your library that would be performant
on PyPy. In any case, &lt;a class="reference external" href="https://github.com/joerick/cibuildwheel"&gt;cibuildwheel&lt;/a&gt; supports building wheels for PyPy.&lt;/p&gt;
&lt;p class="rubric"&gt;Footnotes&lt;/p&gt;
&lt;aside class="footnote-list brackets"&gt;
&lt;aside class="footnote brackets" id="footnote-1" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="https://www.pypy.org/posts/2026/04/pypy-v7322-release.html#footnote-reference-1"&gt;0&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Once &lt;a class="reference external" href="https://github.com/python/pyperformance/pull/461"&gt;a PR to pyperformance&lt;/a&gt; to use the _pickle module on PyPy is accepted&lt;/p&gt;
&lt;/aside&gt;
&lt;/aside&gt;
&lt;section id="what-is-pypy"&gt;
&lt;h3&gt;What is PyPy?&lt;/h3&gt;
&lt;p&gt;PyPy is a Python interpreter, a drop-in replacement for CPython
It's fast (&lt;a class="reference external" href="https://speed.pypy.org"&gt;PyPy and CPython&lt;/a&gt; performance
comparison) due to its integrated tracing JIT compiler.&lt;/p&gt;
&lt;p&gt;We also welcome developers of other &lt;a class="reference external" href="https://rpython.readthedocs.io/en/latest/examples.html"&gt;dynamic languages&lt;/a&gt; to see what RPython
can do for them.&lt;/p&gt;
&lt;p&gt;We provide binary builds for:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;x86&lt;/strong&gt; machines on most common operating systems
(Linux 32/64 bits, Mac OS 64 bits, Windows 64 bits)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;64-bit &lt;strong&gt;ARM&lt;/strong&gt; machines running Linux (&lt;code class="docutils literal"&gt;aarch64&lt;/code&gt;) and macos (&lt;code class="docutils literal"&gt;macos_arm64&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PyPy supports Windows 32-bit, Linux PPC64 big- and little-endian, Linux ARM
32 bit, RISC-V RV64IMAFD Linux, and s390x Linux but does not release binaries.
Please reach out to us if you wish to sponsor binary releases for those
platforms. Downstream packagers provide binary builds for debian, Fedora,
conda, OpenBSD, FreeBSD, Gentoo, and more.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="what-else-is-new"&gt;
&lt;h3&gt;What else is new?&lt;/h3&gt;
&lt;p&gt;For more information about the 7.3.22 release, see the &lt;a class="reference external" href="https://doc.pypy.org/release-v7.3.22.html#changelog"&gt;full changelog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please update, and continue to help us make pypy better.&lt;/p&gt;
&lt;p&gt;Cheers,
The PyPy Team&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;</description><category>release</category><guid>https://www.pypy.org/posts/2026/04/pypy-v7322-release.html</guid><pubDate>Tue, 28 Apr 2026 10:00:00 GMT</pubDate></item><item><title>Using Claude to fix PyPy3.11 test failures securely</title><link>https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html</link><dc:creator>mattip</dc:creator><description>&lt;p&gt;I got access to Claude Max for 6 months, as a promotional move Anthropic made
to Open Source Software contributors. My main OSS impact is as a maintainer for
NumPy, but I decided to see what claude-code could to for PyPy's failing 3.11
tests. Most of these failures are edge cases: error messages that differ from
CPython, or debugging tools that fail in certain cases. I was worried about
letting an AI agent loose on my development machine. I noticed &lt;a class="reference external" href="https://patrickmccanna.net/a-better-way-to-limit-claude-code-and-other-coding-agents-access-to-secrets/"&gt;a post&lt;/a&gt; by
Patrick McCanna (thanks Patrick!) that pointed to using bubblewrap to
sandbox the agent. So I set it all up and (hopefully securely) pointed
claude-code at some tests.&lt;/p&gt;
&lt;!-- TEASER_END: Read more to find out how it went --&gt;
&lt;section id="setting-up"&gt;
&lt;h2&gt;Setting up&lt;/h2&gt;
&lt;p&gt;There were a few steps to make sure I didn't open myself up to obvious gotchas.
There are stories about agents wiping out data bases, or deleting mail boxes.&lt;/p&gt;
&lt;section id="bubblewrap"&gt;
&lt;h3&gt;Bubblewrap&lt;/h3&gt;
&lt;p&gt;First I needed to see what bubblewrap does. I followed the instructions in the
blog post to set things up with some minor variations:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_427b01cf26a0498ebcaeceaaff7bf0a9-1" name="rest_code_427b01cf26a0498ebcaeceaaff7bf0a9-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_427b01cf26a0498ebcaeceaaff7bf0a9-1"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;bubblewrap
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I couldn't run &lt;code class="docutils literal"&gt;bwrap&lt;/code&gt;. After digging around a bit, I found I needed to add
an exception for appamor on Ubuntu 24.04:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-1" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-1"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;bash&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'cat &amp;gt; /etc/apparmor.d/bwrap &amp;lt;&amp;lt; EOF&lt;/span&gt;
&lt;a id="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-2" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-2"&gt;&lt;/a&gt;&lt;span class="s1"&gt;abi &amp;lt;abi/4.0&amp;gt;,&lt;/span&gt;
&lt;a id="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-3" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-3" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-3"&gt;&lt;/a&gt;&lt;span class="s1"&gt;include &amp;lt;tunables/global&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-4" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-4" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-4"&gt;&lt;/a&gt;
&lt;a id="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-5" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-5" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-5"&gt;&lt;/a&gt;&lt;span class="s1"&gt;profile bwrap /usr/bin/bwrap flags=(unconfined) {&lt;/span&gt;
&lt;a id="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-6" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-6" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-6"&gt;&lt;/a&gt;&lt;span class="s1"&gt;  userns,&lt;/span&gt;
&lt;a id="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-7" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-7" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-7"&gt;&lt;/a&gt;&lt;span class="s1"&gt;}&lt;/span&gt;
&lt;a id="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-8" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-8" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-8"&gt;&lt;/a&gt;&lt;span class="s1"&gt;EOF'&lt;/span&gt;
&lt;a id="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-9" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-9" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-9"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apparmor_parser&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;/etc/apparmor.d/bwrap
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then &lt;code class="docutils literal"&gt;bwrap&lt;/code&gt; would run. It is all locked down by default, so I opened up some
exceptions. The arguments are pretty self-explanatory. Ubuntu spreads the
executables around the operating system, so I needed access to various
directories. I wanted a &lt;code class="docutils literal"&gt;/tmp&lt;/code&gt; for running pytest. I also wanted the prompt
to reflect the use of bubblewrap, so changed the &lt;code class="docutils literal"&gt;hostname&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-1" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-1"&gt;&lt;/a&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt; 'EOL' &amp;gt;&amp;gt; ./run_bwrap.sh&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-2" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-2"&gt;&lt;/a&gt;&lt;span class="s"&gt;  function call_bwrap() {&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-3" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-3" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-3"&gt;&lt;/a&gt;&lt;span class="s"&gt;    bwrap \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-4" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-4" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-4"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /usr /usr \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-5" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-5" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-5"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /etc /etc \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-6" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-6" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-6"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /run /run \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-7" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-7" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-7"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/lib /lib \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-8" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-8" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-8"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/lib64 /lib64 \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-9" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-9" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-9"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/bin /bin \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-10" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-10" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-10"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --proc /proc \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-11" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-11" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-11"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --dev /dev \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-12" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-12" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-12"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --bind $(pwd) $(pwd) \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-13" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-13" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-13"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --chdir $(pwd) \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-14" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-14" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-14"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --unshare-user --unshare-pid --unshare-ipc --unshare-uts --unshare-cgroup \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-15" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-15" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-15"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --die-with-parent \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-16" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-16" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-16"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --hostname bwrap \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-17" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-17" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-17"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --tmpfs /tmp \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-18" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-18" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-18"&gt;&lt;/a&gt;&lt;span class="s"&gt;      /bin/bash "$@"&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-19" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-19" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-19"&gt;&lt;/a&gt;&lt;span class="s"&gt;  }&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-20" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-20" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-20"&gt;&lt;/a&gt;&lt;span class="s"&gt;EOL&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-21" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-21" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-21"&gt;&lt;/a&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-22" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-22" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-22"&gt;&lt;/a&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./run_bwrap.sh
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-23" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-23" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-23"&gt;&lt;/a&gt;call_bwrap
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-24" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-24" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-24"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# now I am in a sandboxed bash shell&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-25" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-25" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-25"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# play around, try seeing other directories, getting sudo, or writing outside&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-26" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-26" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-26"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# the sandbox&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-27" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-27" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-27"&gt;&lt;/a&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I did not do &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;--unshare-network&lt;/span&gt;&lt;/code&gt; since, after all, I want to use claude and
that needs network access. I did add rw access to &lt;code class="docutils literal"&gt;$(pwd)&lt;/code&gt; since I want it to
edit code in the current directory, that is the whole point.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="basic-claude"&gt;
&lt;h3&gt;Basic claude&lt;/h3&gt;
&lt;p&gt;After trying out bubblewrap and convincing myself it does actually work, I
installed claude code&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_a483295e055347bd8175832b9b307666-1" name="rest_code_a483295e055347bd8175832b9b307666-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a483295e055347bd8175832b9b307666-1"&gt;&lt;/a&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-fsSL&lt;span class="w"&gt; &lt;/span&gt;https://claude.ai/install.sh&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bash
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Really Anthropic, this is the best way to install claude? No dpkg?&lt;/p&gt;
&lt;p&gt;I ran claude once (unsafely) to get logged in. It opened a webpage, and saved
the login to the &lt;code class="docutils literal"&gt;oathAccount&lt;/code&gt; field in &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;~/.claude.json&lt;/span&gt;&lt;/code&gt;. Now I changed my
bash script to this to get claude to run inside the bubblewrap sandbox:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-1" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-1"&gt;&lt;/a&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt; 'EOL' &amp;gt;&amp;gt; ./run_claude.sh&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-2" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-2"&gt;&lt;/a&gt;&lt;span class="s"&gt;  claude-safe() {&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-3" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-3" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-3"&gt;&lt;/a&gt;&lt;span class="s"&gt;    bwrap \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-4" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-4" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-4"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /usr /usr \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-5" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-5" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-5"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /etc /etc \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-6" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-6" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-6"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /run /run \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-7" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-7" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-7"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind "$HOME/.local/share/claude" "$HOME/.local/share/claude" \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-8" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-8" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-8"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/lib /lib \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-9" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-9" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-9"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/lib64 /lib64 \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-10" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-10" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-10"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/bin /bin \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-11" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-11" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-11"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink "$HOME/.local/share/claude/versions/2.1.81" "$HOME/.local/bin/claude" \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-12" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-12" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-12"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --proc /proc \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-13" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-13" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-13"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --dev /dev \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-14" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-14" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-14"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --bind $(pwd) $(pwd) \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-15" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-15" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-15"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --bind "$HOME/.claude" "$HOME/.claude" \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-16" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-16" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-16"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --bind "$HOME/.claude.json" "$HOME/.claude.json" \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-17" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-17" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-17"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --chdir $(pwd) \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-18" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-18" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-18"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --unshare-user --unshare-pid --unshare-ipc --unshare-uts --unshare-cgroup \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-19" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-19" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-19"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --die-with-parent \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-20" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-20" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-20"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --hostname bwrap \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-21" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-21" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-21"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --tmpfs /tmp \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-22" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-22" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-22"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --setenv PATH "$HOME/.local/bin:$PATH" \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-23" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-23" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-23"&gt;&lt;/a&gt;&lt;span class="s"&gt;      claude "$@"&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-24" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-24" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-24"&gt;&lt;/a&gt;&lt;span class="s"&gt;  }&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-25" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-25" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-25"&gt;&lt;/a&gt;&lt;span class="s"&gt;EOL&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-26" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-26" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-26"&gt;&lt;/a&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-27" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-27" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-27"&gt;&lt;/a&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./run_claude.sh
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-28" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-28" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-28"&gt;&lt;/a&gt;claude-safe
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now I can use claude. Note it needs some more directories in order to run. This
script hard-codes the version, in the future YMMV. I want it to be able to look
at github, and also my local checkout of cpython so it can examine differences.
I created a read-only token by clicking on my avatar in the upper right corner
of a github we page, then going to Settings → Developer settings → Personal
access tokens → Fine-grained tokens → Generate new token. Since pypy is in the
pypy org, I used "Repository owner: pypy", "Repository access: pypy (only)" and
"Permissions: Contents". Then I made doubly sure the token permissions were
read-only. And checked again. Then I copied the token to the bash script. I
also added a &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;ro-bind&lt;/span&gt;&lt;/code&gt; to the cpython checkout, so I could tell claude code
where to look for CPython implementations of missing PyPy functionality.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_912b59cd29f34fa4b72a4fef84a34ab8-1" name="rest_code_912b59cd29f34fa4b72a4fef84a34ab8-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_912b59cd29f34fa4b72a4fef84a34ab8-1"&gt;&lt;/a&gt;--ro-bind&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/oss/cpython"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/oss/cpython"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;a id="rest_code_912b59cd29f34fa4b72a4fef84a34ab8-2" name="rest_code_912b59cd29f34fa4b72a4fef84a34ab8-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_912b59cd29f34fa4b72a4fef84a34ab8-2"&gt;&lt;/a&gt;--setenv&lt;span class="w"&gt; &lt;/span&gt;GH_TOKEN&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hah, sharing my token would not have been smart"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="claude-sandbox"&gt;
&lt;h3&gt;Claude /sandbox&lt;/h3&gt;
&lt;p&gt;Claude comes with its own sandbox, configured by using the &lt;code class="docutils literal"&gt;/sandbox&lt;/code&gt; command.
I chose the defaults, which prevents malicious code in the repo from accessing
the file system and the network. I was missing some packages to get this to
work. Claude would hang until I installed them, and I needed to kill it with
&lt;code class="docutils literal"&gt;kill&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_afeda2dc07b949c5bf946c10aa09900a-1" name="rest_code_afeda2dc07b949c5bf946c10aa09900a-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_afeda2dc07b949c5bf946c10aa09900a-1"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;socat
&lt;a id="rest_code_afeda2dc07b949c5bf946c10aa09900a-2" name="rest_code_afeda2dc07b949c5bf946c10aa09900a-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_afeda2dc07b949c5bf946c10aa09900a-2"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;npm&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-g&lt;span class="w"&gt; &lt;/span&gt;@anthropic-ai/sandbox-runtime
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="final-touches"&gt;
&lt;h3&gt;Final touches&lt;/h3&gt;
&lt;p&gt;One last thing that I discovered later: I needed to give claude access to some
grepping and git tools. While git should be locked down externally so it
cannot push to the repo, I do want claude to look at other issues and pull
requests in read-only mode. So I added a local &lt;code class="docutils literal"&gt;.claude/settings.json&lt;/code&gt; file
inside the repo (see below for which directory to do this):&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code json"&gt;&lt;a id="rest_code_88fa74149a2047bdb223a5cb834c8973-1" name="rest_code_88fa74149a2047bdb223a5cb834c8973-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_88fa74149a2047bdb223a5cb834c8973-2" name="rest_code_88fa74149a2047bdb223a5cb834c8973-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-2"&gt;&lt;/a&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_88fa74149a2047bdb223a5cb834c8973-3" name="rest_code_88fa74149a2047bdb223a5cb834c8973-3" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-3"&gt;&lt;/a&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;a id="rest_code_88fa74149a2047bdb223a5cb834c8973-4" name="rest_code_88fa74149a2047bdb223a5cb834c8973-4" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-4"&gt;&lt;/a&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(sed*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_88fa74149a2047bdb223a5cb834c8973-5" name="rest_code_88fa74149a2047bdb223a5cb834c8973-5" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-5"&gt;&lt;/a&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(grep*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_88fa74149a2047bdb223a5cb834c8973-6" name="rest_code_88fa74149a2047bdb223a5cb834c8973-6" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-6"&gt;&lt;/a&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(cat*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_88fa74149a2047bdb223a5cb834c8973-7" name="rest_code_88fa74149a2047bdb223a5cb834c8973-7" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-7"&gt;&lt;/a&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(find*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_88fa74149a2047bdb223a5cb834c8973-8" name="rest_code_88fa74149a2047bdb223a5cb834c8973-8" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-8"&gt;&lt;/a&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(rg*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_88fa74149a2047bdb223a5cb834c8973-9" name="rest_code_88fa74149a2047bdb223a5cb834c8973-9" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-9"&gt;&lt;/a&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(python*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_88fa74149a2047bdb223a5cb834c8973-10" name="rest_code_88fa74149a2047bdb223a5cb834c8973-10" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-10"&gt;&lt;/a&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(pytest*)"&lt;/span&gt;
&lt;a id="rest_code_88fa74149a2047bdb223a5cb834c8973-11" name="rest_code_88fa74149a2047bdb223a5cb834c8973-11" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-11"&gt;&lt;/a&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a id="rest_code_88fa74149a2047bdb223a5cb834c8973-12" name="rest_code_88fa74149a2047bdb223a5cb834c8973-12" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-12"&gt;&lt;/a&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;a id="rest_code_88fa74149a2047bdb223a5cb834c8973-13" name="rest_code_88fa74149a2047bdb223a5cb834c8973-13" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-13"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then I made git ignore it, even when doing a &lt;code class="docutils literal"&gt;git clean&lt;/code&gt;, in a local (not part
of the repo) configuration&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_5349ae38b1c743a49bb36c9b966a5932-1" name="rest_code_5349ae38b1c743a49bb36c9b966a5932-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5349ae38b1c743a49bb36c9b966a5932-1"&gt;&lt;/a&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;.claude&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;~/.config/git/ignore
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="what-about-git-push"&gt;
&lt;h3&gt;What about &lt;code class="docutils literal"&gt;git push&lt;/code&gt;?&lt;/h3&gt;
&lt;p&gt;I don't want claude messing around with the upstream repo, only read access. But
I did not actively prevent &lt;code class="docutils literal"&gt;git push&lt;/code&gt;. So instead of using my actual pypy
repo, I cloned it to a separate directory and did not add a remote pointing to
github.com.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="fixing-tests-easy"&gt;
&lt;h2&gt;Fixing tests - easy&lt;/h2&gt;
&lt;p&gt;Now that everything is set up (I hope I remembered everything), I could start
asking questions. The technique I chose was to feed claude the whole test
failure from the buildbot. So starting from the &lt;a class="reference external" href="https://buildbot.pypy.org/summary?branch=py3.11"&gt;buildbot py3.11 summary&lt;/a&gt;,
click on one of the &lt;code class="docutils literal"&gt;F&lt;/code&gt; links and copy-paste all that into the claude prompt.
It didn't take long for claude to come up with solutions for the long-standing
&lt;a class="reference external" href="https://github.com/pypy/pypy/commit/9e8e121b545dbea3f26ca436ae8a797617904306#diff-ab042b3dd16bf22b7e3d8595f182ad39d3823d76b414da7debe96081a884d16bR64-R330"&gt;ctype error missing exception&lt;/a&gt; which turned out to be due to an missing error
trap when already handling an error.&lt;/p&gt;
&lt;p&gt;Also a &lt;a class="reference external" href="https://github.com/pypy/pypy/commit/9e8e121b545dbea3f26ca436ae8a797617904306#diff-ab042b3dd16bf22b7e3d8595f182ad39d3823d76b414da7debe96081a884d16bR64-R53"&gt;CTYPES_MAX_ARGCOUNT check&lt;/a&gt; was
missing. At first, claude wanted to change the ctypes code from CPython's stdlib,
and so I had to make it clear that claude was not to touch the files in
&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;lib-python&lt;/span&gt;&lt;/code&gt;. They are copied verbatim from CPython and should not be
modified without really good reasons.&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://github.com/pypy/pypy/commit/39ca7a1def272742e8aafd2a649ed4f8fed7038d"&gt;fix to raise&lt;/a&gt; &lt;code class="docutils literal"&gt;TypeError&lt;/code&gt; rather
than &lt;code class="docutils literal"&gt;Attribute Error&lt;/code&gt; for deleting ctype object's &lt;code class="docutils literal"&gt;value&lt;/code&gt; was maybe a little
trickier: claude needed to create its own &lt;code class="docutils literal"&gt;property&lt;/code&gt; class and use it in
assignments.&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://github.com/pypy/pypy/commit/e0e401699c20a92d8db657879183c68ea44246b4"&gt;fix for a failing test&lt;/a&gt; for a correct &lt;code class="docutils literal"&gt;repr&lt;/code&gt; of a ctypes array was a
little more involved.  Claude needed to figure out that &lt;code class="docutils literal"&gt;newmemoryview&lt;/code&gt; was
raising an exception, dive into the RPython implementation and fix the problem,
and then also fix a pure-python &lt;code class="docutils literal"&gt;__buffer__&lt;/code&gt; shape edge case error.&lt;/p&gt;
&lt;p&gt;There were more, but you get the idea. With a little bit of coaching, and by showing
claude where the CPython implementation was, more tests are now passing.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="fixing-tests-harder"&gt;
&lt;h2&gt;Fixing tests - harder&lt;/h2&gt;
&lt;p&gt;PyPy has a HPy backend. There were some test failures that were
easy to fix (a handle not being closed, an annotation warning). But the big one
was a problem with the context tracking before and after ffi function calls. In
debug mode there is a check that the ffi call is done using the correct HPy
context. It turns out to be tricky to hang on to a reference to a context in
RPython since the context RPython object is pre-built. The solution, which took
quite a few tokens and translation cycles to work out, was to assign the
context on the C level, and have a getter to fish it out in RPython.&lt;/p&gt;
&lt;section id="conclusion"&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;I started this journey not more than 24 hours ago, after some successful
sessions using claude to refactor some web sites off hosting platforms and make
them static pages. I was impressed enough to try coding with it from the
terminal. It helps that I was given a generous budget to use Anthropic's tool.&lt;/p&gt;
&lt;p&gt;Claude seems capable of understanding the layers of PyPy: from the pure python
stdlib to RPython and into the small amount of C code. I even asked it to
examine a &lt;a class="reference external" href="https://github.com/pypy/pypy/issues/5398"&gt;segfault&lt;/a&gt; in the recently released PyPy7.3.21, and it seems to have
found the general area where there was a latent bug in the JIT.&lt;/p&gt;
&lt;p&gt;Like any tool, agentic programming must be used carefully to make sure it
cannot do damage. I hope I closed the most obvious foot-guns, if you have other
ideas of things I should do to protect myself while using an agent like this, I
would love to hear about them.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;</description><category>AI</category><guid>https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html</guid><pubDate>Mon, 23 Mar 2026 10:27:55 GMT</pubDate></item><item><title>PyPy v7.3.21 release</title><link>https://www.pypy.org/posts/2026/03/pypy-v7321-release.html</link><dc:creator>mattip</dc:creator><description>&lt;section id="pypy-v7-3-21-release-of-python-2-7-3-11"&gt;
&lt;h2&gt;PyPy v7.3.21: release of python 2.7, 3.11&lt;/h2&gt;
&lt;aside class="admonition warning"&gt;
&lt;p class="admonition-title"&gt;Warning&lt;/p&gt;
&lt;p&gt;This release has some known crashes. We recommend you use a different version&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;The PyPy team is proud to release version 7.3.21 of PyPy after the previous
release on July 4, 2025. This is a bug-fix release that also updates to Python
3.11.15.&lt;/p&gt;
&lt;p&gt;The release includes two different interpreters:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;PyPy2.7, which is an interpreter supporting the syntax and the features of
Python 2.7 including the stdlib for CPython 2.7.18+ (the &lt;code class="docutils literal"&gt;+&lt;/code&gt; is for
backported security updates)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PyPy3.11, which is an interpreter supporting the syntax and the features of
Python 3.11, including the stdlib for CPython 3.11.15.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The interpreters are based on much the same codebase, thus the double
release. This is a micro release, all APIs are compatible with the other 7.3
releases.&lt;/p&gt;
&lt;p&gt;We recommend updating. You can find links to download the releases here:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a class="reference external" href="https://pypy.org/download.html"&gt;https://pypy.org/download.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We would like to thank our donors for the continued support of the PyPy
project. If PyPy is not quite good enough for your needs, we are available for
&lt;a class="reference external" href="https://www.pypy.org/pypy-sponsors.html"&gt;direct consulting&lt;/a&gt; work. If PyPy is helping you out, we would love to hear
about it and encourage submissions to our &lt;a class="reference external" href="https://pypy.org/blog"&gt;blog&lt;/a&gt; via a pull request
to &lt;a class="reference external" href="https://github.com/pypy/pypy.org"&gt;https://github.com/pypy/pypy.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We would also like to thank our contributors and encourage new people to join
the project. PyPy has many layers and we need help with all of them: bug fixes,
&lt;a class="reference external" href="https://doc.pypy.org/"&gt;PyPy&lt;/a&gt; and &lt;a class="reference external" href="https://rpython.readthedocs.org"&gt;RPython&lt;/a&gt; documentation improvements, or general &lt;a class="reference external" href="https://doc.pypy.org/project-ideas.html"&gt;help&lt;/a&gt; with
making RPython's JIT even better.&lt;/p&gt;
&lt;p&gt;If you are a python library maintainer and use C-extensions, please consider
making a &lt;a class="reference external" href="https://hpyproject.org/"&gt;HPy&lt;/a&gt; / &lt;a class="reference external" href="https://cffi.readthedocs.io"&gt;CFFI&lt;/a&gt; / &lt;a class="reference external" href="https://cppyy.readthedocs.io"&gt;cppyy&lt;/a&gt; version of your library that would be performant
on PyPy. In any case, &lt;a class="reference external" href="https://github.com/joerick/cibuildwheel"&gt;cibuildwheel&lt;/a&gt; supports building wheels for PyPy.&lt;/p&gt;
&lt;section id="what-is-pypy"&gt;
&lt;h3&gt;What is PyPy?&lt;/h3&gt;
&lt;p&gt;PyPy is a Python interpreter, a drop-in replacement for CPython
It's fast (&lt;a class="reference external" href="https://speed.pypy.org"&gt;PyPy and CPython&lt;/a&gt; performance
comparison) due to its integrated tracing JIT compiler.&lt;/p&gt;
&lt;p&gt;We also welcome developers of other &lt;a class="reference external" href="https://rpython.readthedocs.io/en/latest/examples.html"&gt;dynamic languages&lt;/a&gt; to see what RPython
can do for them.&lt;/p&gt;
&lt;p&gt;We provide binary builds for:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;x86&lt;/strong&gt; machines on most common operating systems
(Linux 32/64 bits, Mac OS 64 bits, Windows 64 bits)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;64-bit &lt;strong&gt;ARM&lt;/strong&gt; machines running Linux (&lt;code class="docutils literal"&gt;aarch64&lt;/code&gt;) and macos (&lt;code class="docutils literal"&gt;macos_arm64&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PyPy supports Windows 32-bit, Linux PPC64 big- and little-endian, Linux ARM
32 bit, RISC-V RV64IMAFD Linux, and s390x Linux but does not release binaries.
Please reach out to us if you wish to sponsor binary releases for those
platforms. Downstream packagers provide binary builds for debian, Fedora,
conda, OpenBSD, FreeBSD, Gentoo, and more.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="what-else-is-new"&gt;
&lt;h3&gt;What else is new?&lt;/h3&gt;
&lt;p&gt;For more information about the 7.3.21 release, see the &lt;a class="reference external" href="https://doc.pypy.org/release-v7.3.21.html#changelog"&gt;full changelog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please update, and continue to help us make pypy better.&lt;/p&gt;
&lt;p&gt;Cheers,
The PyPy Team&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;</description><category>release</category><guid>https://www.pypy.org/posts/2026/03/pypy-v7321-release.html</guid><pubDate>Fri, 13 Mar 2026 10:00:00 GMT</pubDate></item><item><title>Load and store forwarding in the Toy Optimizer</title><link>https://www.pypy.org/posts/2025/12/toy-load-store.html</link><dc:creator>Max Bernstein</dc:creator><description>&lt;p&gt;This is a &lt;a href="https://bernsteinbear.com/blog/toy-load-store/" rel="canonical"&gt;cross-post&lt;/a&gt; from Max Bernstein from his blog where he writes
about programming languages, compilers, optimizations, virtual machines.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;A long, long time ago (two years!) &lt;a href="https://cfbolz.de/"&gt;CF Bolz-Tereick&lt;/a&gt; and I made a &lt;a href="https://www.youtube.com/watch?v=w-UHg0yOPSE"&gt;video
about load/store forwarding&lt;/a&gt; and an accompanying &lt;a href="https://gist.github.com/tekknolagi/4e3fa26d350f6d3b39ede40d372b97fe"&gt;GitHub Gist&lt;/a&gt;
about load/store forwarding (also called load elimination) in the Toy Optimizer. I
said I would write a blog post about it, but never found the time—it got lost
amid a sea of large life changes.&lt;/p&gt;
&lt;p&gt;It's a neat idea: do an abstract interpretation over the trace, modeling the
heap at compile-time, eliminating redundant loads and stores. That means it's
possible to optimize traces like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;v0&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="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;v1&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="nb"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&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;v2&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;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;v3&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="nb"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&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;v4&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="nb"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&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;v5&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;do_something&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;into traces like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;v0&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="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;v1&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="nb"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&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;v2&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;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;v5&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;do_something&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(where &lt;code&gt;load(v0, 5)&lt;/code&gt; is equivalent to &lt;code&gt;*(v0+5)&lt;/code&gt; in C syntax and &lt;code&gt;store(v0, 6,
123)&lt;/code&gt; is equvialent to &lt;code&gt;*(v0+6)=123&lt;/code&gt; in C syntax)&lt;/p&gt;
&lt;p&gt;This indicates that we were able to eliminate two redundant loads by keeping
around information about previous loads and stores. Let's get to work making
this possible.&lt;/p&gt;
&lt;h3 id="the-usual-infrastructure"&gt;The usual infrastructure&lt;/h3&gt;
&lt;p&gt;We'll start off with the usual infrastructure from the &lt;a href="https://pypy.org/categories/toy-optimizer.html"&gt;Toy
Optimizer series&lt;/a&gt;: a very stringly-typed representation of a
&lt;a href="https://gist.github.com/tekknolagi/4e3fa26d350f6d3b39ede40d372b97fe#file-port-py-L4-L112"&gt;trace-based SSA IR&lt;/a&gt; and a union-find rewrite mechanism.&lt;/p&gt;
&lt;p&gt;This means we can start writing some new optimization pass and our first 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;optimize_load_store&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="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="c1"&gt;# TODO: copy an optimized version of bb into opt_bb&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;opt_bb&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_two_loads&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;load&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="mi"&gt;0&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;load&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="mi"&gt;0&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;escape&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;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;escape&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;opt_bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;optimize_load_store&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="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;var0 = getarg(0)&lt;/span&gt;
&lt;span class="s2"&gt;var1 = load(var0, 0)&lt;/span&gt;
&lt;span class="s2"&gt;var2 = escape(var1)&lt;/span&gt;
&lt;span class="s2"&gt;var3 = escape(var1)"""&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This test is asserting that we can remove duplicate loads. Why load twice if we
can cache the result? Let's make that happen.&lt;/p&gt;
&lt;h3 id="caching-loads"&gt;Caching loads&lt;/h3&gt;
&lt;p&gt;To do this, we'll model the the heap at compile-time. When I say "model", I
mean that we will have an imprecise but correct abstract representation of the
heap: we don't (and can't) have knowledge of every value, but we can know for
sure that some addresses have certain values.&lt;/p&gt;
&lt;p&gt;For example, if we have observed a load from object &lt;em&gt;O&lt;/em&gt; at offset &lt;em&gt;8&lt;/em&gt; &lt;code&gt;v0 =
load(O, 8)&lt;/code&gt;, we know that the SSA value &lt;code&gt;v0&lt;/code&gt; is at &lt;code&gt;heap[(O, 8)]&lt;/code&gt;. That sounds
tautological, but it's not. Future loads can make use of this information.&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;get_num&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="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&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;assert&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="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;index&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;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;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;value&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;optimize_load_store&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="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="c1"&gt;# Stores things we know about the heap at... compile-time.&lt;/span&gt;
    &lt;span class="c1"&gt;# Key: an object and an offset pair acting as a heap address&lt;/span&gt;
    &lt;span class="c1"&gt;# Value: a previous SSA value we know exists at that address&lt;/span&gt;
    &lt;span class="n"&gt;compile_time_heap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Tuple&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="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="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="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;"load"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;obj&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;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="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_num&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;span class="n"&gt;load_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compile_time_heap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;load_info&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&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;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;previous&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="n"&gt;compile_time_heap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;load_info&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;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;This pass records information about loads and uses the result of a previous
cached load operation if available. We treat the pair of (SSA value, offset) as
an address into our abstract heap.&lt;/p&gt;
&lt;p&gt;That's great! If you run our simple test, it should now pass. But what happens
if we store into that address before the second load? Oops...&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_store_to_same_object_offset_invalidates_load&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;load&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="mi"&gt;0&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;store&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="mi"&gt;0&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="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;load&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="mi"&gt;0&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;escape&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;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;escape&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;optimize_load_store&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="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;var0 = getarg(0)&lt;/span&gt;
&lt;span class="s2"&gt;var1 = load(var0, 0)&lt;/span&gt;
&lt;span class="s2"&gt;var2 = store(var0, 0, 5)&lt;/span&gt;
&lt;span class="s2"&gt;var3 = load(var0, 0)&lt;/span&gt;
&lt;span class="s2"&gt;var4 = escape(var1)&lt;/span&gt;
&lt;span class="s2"&gt;var5 = escape(var3)"""&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This test fails because we are incorrectly keeping around &lt;code&gt;var1&lt;/code&gt; in our
abstract heap. We need to get rid of it and not replace &lt;code&gt;var3&lt;/code&gt; with &lt;code&gt;var1&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="invalidating-cached-loads"&gt;Invalidating cached loads&lt;/h3&gt;
&lt;p&gt;So it turns out we have to also model stores in order to cache loads correctly.
One valid, albeit aggressive, way to do that is to throw away all the
information we know at each store operation:&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;optimize_load_store&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="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="n"&gt;compile_time_heap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Tuple&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="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="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="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;"store"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;compile_time_heap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&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;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;"load"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&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;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;That makes our test pass—yay!—but at great cost. It means any store
operation mucks up redundant loads. In our world where we frequently read from
and write to objects, this is what we call a huge bummer.&lt;/p&gt;
&lt;p&gt;For example, a store to offset 4 on some object should never interfere with a
load from a different offset on the same object&lt;sup id="fnref:size"&gt;&lt;a class="footnote-ref" href="https://www.pypy.org/posts/2025/12/toy-load-store.html#fn:size"&gt;1&lt;/a&gt;&lt;/sup&gt;. We should be able to
keep our load from offset 0 cached here:&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_store_to_same_object_different_offset_does_not_invalidate_load&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;load&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="mi"&gt;0&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;store&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="mi"&gt;4&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="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;load&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="mi"&gt;0&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;escape&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;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;escape&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;optimize_load_store&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="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;var0 = getarg(0)&lt;/span&gt;
&lt;span class="s2"&gt;var1 = load(var0, 0)&lt;/span&gt;
&lt;span class="s2"&gt;var2 = store(var0, 4, 5)&lt;/span&gt;
&lt;span class="s2"&gt;var3 = escape(var1)&lt;/span&gt;
&lt;span class="s2"&gt;var4 = escape(var1)"""&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We could try instead checking if our specific (object, offset) pair is in the
heap and only removing cached information about that offset and that object.
That would definitely help!&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;optimize_load_store&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="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="n"&gt;compile_time_heap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Tuple&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="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="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="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;"store"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;load_info&lt;/span&gt; &lt;span class="o"&gt;=&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="n"&gt;get_num&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;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;load_info&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;compile_time_heap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;compile_time_heap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;load_info&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;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;"load"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&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;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;It makes our test pass, too, which is great news.&lt;/p&gt;
&lt;p&gt;Unfortunately, this runs into problems due to aliasing: it's entirely possible
that our compile-time heap could contain a pair &lt;code&gt;(v0, 0)&lt;/code&gt; and a pair &lt;code&gt;(v1, 0)&lt;/code&gt; where &lt;code&gt;v0&lt;/code&gt;
and &lt;code&gt;v1&lt;/code&gt; are the same object (but not known to the optimizer). Then we might
run into a situation where we incorrectly cache loads because the optimizer
doesn't know our abstract addresses &lt;code&gt;(v0, 0)&lt;/code&gt; and &lt;code&gt;(v1, 0)&lt;/code&gt; are actually the
same pointer at run-time.&lt;/p&gt;
&lt;p&gt;This means that we are breaking abstract interpretation rules: our abstract
interpreter has to correctly model &lt;em&gt;all&lt;/em&gt; possible outcomes at run-time. This
means to me that we should instead pick some tactic in-between clearing all
information (correct but over-eager) and clearing only exact matches of
object+offset (incorrect).&lt;/p&gt;
&lt;p&gt;The term that will help us here is called an &lt;em&gt;alias class&lt;/em&gt;. It is a name for a
way to efficiently partition objects in your abstract heap into completely
disjoint sets. Writes to any object in one class never affect objects in
another class.&lt;/p&gt;
&lt;p&gt;Our very scrappy alias classes will be just based on the offset: each offset is
a different alias class. If we write to any object at offset K, we have to
invalidate all of our compile-time offset K knowledge—even if it's for
another object. This is a nice middle ground, and it's possible because our
(made up) object system guarantees that distinct objects do not overlap, and
also that we are not writing out-of-bounds.&lt;sup id="fnref:tbaa"&gt;&lt;a class="footnote-ref" href="https://www.pypy.org/posts/2025/12/toy-load-store.html#fn:tbaa"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;So let's remove all of the entries from &lt;code&gt;compile_time_heap&lt;/code&gt; where the offset
matches the offset in the current &lt;code&gt;store&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;optimize_load_store&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="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="n"&gt;compile_time_heap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Tuple&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="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="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="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;"store"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_num&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;span class="n"&gt;compile_time_heap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;load_info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;load_info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;compile_time_heap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&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;load_info&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="n"&gt;offset&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;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;"load"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&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;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;Great! Now our test passes.&lt;/p&gt;
&lt;p&gt;This concludes the load optimization section of the post. We have modeled
enough of loads and stores that we can eliminate redundant loads. Very cool.
But we can go further.&lt;/p&gt;
&lt;h3 id="caching-stores"&gt;Caching stores&lt;/h3&gt;
&lt;p&gt;Stores don't just invalidate information. They also give us new information!
Any time we see an operation of the form &lt;code&gt;v1 = store(v0, 8, 5)&lt;/code&gt; we also learn
that &lt;code&gt;load(v0, 8) == 5&lt;/code&gt;! Until it gets invalidated, anyway.&lt;/p&gt;
&lt;p&gt;For example, in this test, we can eliminate the load from &lt;code&gt;var0&lt;/code&gt; at offset 0:&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_load_after_store_removed&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;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&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="mi"&gt;0&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="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;load&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="mi"&gt;0&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;load&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="mi"&gt;1&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;escape&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;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;escape&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;opt_bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;optimize_load_store&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="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;var0 = getarg(0)&lt;/span&gt;
&lt;span class="s2"&gt;var1 = store(var0, 0, 5)&lt;/span&gt;
&lt;span class="s2"&gt;var2 = load(var0, 1)&lt;/span&gt;
&lt;span class="s2"&gt;var3 = escape(5)&lt;/span&gt;
&lt;span class="s2"&gt;var4 = escape(var2)"""&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Making that work is thankfully not very hard; we need only add that new
information to the compile-time heap after removing all the
potentially-aliased info:&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;optimize_load_store&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="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="n"&gt;compile_time_heap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Tuple&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="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="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="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;"store"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_num&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;span class="n"&gt;compile_time_heap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="c1"&gt;# ... as before ...&lt;/span&gt;
            &lt;span class="n"&gt;obj&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;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="n"&gt;new_value&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;arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;compile_time_heap&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_value&lt;/span&gt;  &lt;span class="c1"&gt;# NEW!&lt;/span&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;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"load"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&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;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;This makes the test pass. It makes another test fail, but only
because—oops—we now know more. You can delete the old test because the new
test supersedes it.&lt;/p&gt;
&lt;p&gt;Now, note that we are not removing the store. This is because we have nothing
in our optimizer that keeps track of what might have observed the side-effects
of the store. What if the object got &lt;code&gt;escape&lt;/code&gt;d? Or someone did a load later on?
We would only be able to remove the store (&lt;code&gt;continue&lt;/code&gt;) if we could guarantee it
was not observable.&lt;/p&gt;
&lt;p&gt;In our current framework, this only happens in one case: someone is doing a
store of the exact same value that already exists in our compile-time heap.
That is, either the same constant, or the same SSA value. If we see this, then
we can completely skip the second store instruction.&lt;/p&gt;
&lt;p&gt;Here's a test case for that, where we have gained information from the load
instruction that we can then use to get rid of the store instruction:&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_load_then_store&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;arg1&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;load&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="mi"&gt;0&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;store&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="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="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;escape&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;opt_bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;optimize_load_store&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="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;var0 = getarg(0)&lt;/span&gt;
&lt;span class="s2"&gt;var1 = load(var0, 0)&lt;/span&gt;
&lt;span class="s2"&gt;var2 = escape(var1)"""&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let's make it pass. To do that, first we'll make an equality function that
works for both constants and operations. Constants are equal if their values
are equal, and operations are equal if they are the identical (by
address/pointer) operation.&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;eq_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Value&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="n"&gt;right&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;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&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;left&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="ow"&gt;and&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;right&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;left&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a partial equality: if two operations are not equal under &lt;code&gt;eq_value&lt;/code&gt;,
it doesn't mean that they are different, only that we don't know that they are
the same.&lt;/p&gt;
&lt;p&gt;Then, after that, we need only check if the current value in the compile-time
heap is the same as the value being stored in. If it is, wonderful. No need to
store. &lt;code&gt;continue&lt;/code&gt; and don't append the operation to &lt;code&gt;opt_bb&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;optimize_load_store&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="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="n"&gt;compile_time_heap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Tuple&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="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="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="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;"store"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;obj&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;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="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_num&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;span class="n"&gt;store_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;current_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compile_time_heap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store_info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;new_value&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;arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;eq_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="c1"&gt;# NEW!&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="n"&gt;compile_time_heap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="c1"&gt;# ... as before ...&lt;/span&gt;
            &lt;span class="c1"&gt;# ...&lt;/span&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;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"load"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;load_info&lt;/span&gt; &lt;span class="o"&gt;=&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="n"&gt;get_num&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;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;load_info&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;compile_time_heap&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;compile_time_heap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;load_info&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="n"&gt;compile_time_heap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;load_info&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;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;This makes our load-then-store pass and it also makes other tests pass too,
like eliminating a store after another store!&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_store_after_store&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;arg1&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;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&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="mi"&gt;0&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="n"&gt;bb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&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="mi"&gt;0&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="n"&gt;opt_bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;optimize_load_store&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="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;var0 = getarg(0)&lt;/span&gt;
&lt;span class="s2"&gt;var1 = store(var0, 0, 5)"""&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Unfortunately, this only works if the values—constants or SSA values—are
known to be the same. If we store &lt;em&gt;different&lt;/em&gt; values, we can't optimize. In the
live stream, we left this an exercise for the viewer:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xfail&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_exercise_for_the_reader&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;arg0&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;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;store&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="mi"&gt;0&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="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;store&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&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;load&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="mi"&gt;0&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;escape&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;opt_bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;optimize_load_store&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="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;var0 = getarg(0)&lt;/span&gt;
&lt;span class="s2"&gt;var1 = store(var0, 0, 7)&lt;/span&gt;
&lt;span class="s2"&gt;var2 = escape(7)"""&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We would only be able to optimize this away if we had some notion of a store
being &lt;em&gt;dead&lt;/em&gt;. In this case, that is a store in which the value is never read
before being overwritten.&lt;/p&gt;
&lt;h3 id="removing-dead-stores"&gt;Removing dead stores&lt;/h3&gt;
&lt;p&gt;TODO, I suppose. I have not gotten this far yet. If I get around to it, I will
come back and update the post.&lt;/p&gt;
&lt;h3 id="in-the-real-world"&gt;In the real world&lt;/h3&gt;
&lt;p&gt;This small optimization pass may seem silly or fiddly—when would we ever see
something like this in a real IR?—but it's pretty useful. Here's the Ruby
code that got me thinking about it again some years later for ZJIT:&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;C&lt;/span&gt;
&lt;span class="w"&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;initialize&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="vi"&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="w"&gt;    &lt;/span&gt;&lt;span class="vi"&gt;@b&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;2&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="vi"&gt;@c&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;3&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;CRuby has a shape system and ZJIT makes use of it, so we end up optimizing this
code (if it's monomorphic) into a series of shape checks and stores. The HIR
might end up looking something like the mess below, where I've annotated the
shape guards (can be thought of as loads) and stores with asterisks:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="cp"&gt;# ...&lt;/span&gt;
&lt;span class="n"&gt;bb2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v6&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;BasicObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;v10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Fixnum&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="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Value&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="nl"&gt;v31&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;HeapBasicObject&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;GuardType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HeapBasicObject&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v32&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;HeapBasicObject&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;GuardShape&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x400000&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StoreField&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v32&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="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="mh"&gt;@0x10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v10&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;WriteBarrier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v10&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;v35&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;CShape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;0x40008e&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="n"&gt;Const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CShape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x40008e&lt;/span&gt;&lt;span class="p"&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;StoreField&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v32&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="n"&gt;_shape_id&lt;/span&gt;&lt;span class="mh"&gt;@0x4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v35&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;v16&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Fixnum&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="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;Const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;v37&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;HeapBasicObject&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;GuardType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HeapBasicObject&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v38&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;HeapBasicObject&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;GuardShape&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x40008e&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StoreField&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v38&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="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="mh"&gt;@0x18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v16&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;WriteBarrier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v16&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;v41&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;CShape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;0x40008f&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="n"&gt;Const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CShape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x40008f&lt;/span&gt;&lt;span class="p"&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;StoreField&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v38&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="n"&gt;_shape_id&lt;/span&gt;&lt;span class="mh"&gt;@0x4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v41&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;v22&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Fixnum&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&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="n"&gt;Const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;v43&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;HeapBasicObject&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;GuardType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HeapBasicObject&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v44&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;HeapBasicObject&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;GuardShape&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v43&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x40008f&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StoreField&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v44&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="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="mh"&gt;@0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v22&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;WriteBarrier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v44&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v22&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;v47&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;CShape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;0x400090&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="n"&gt;Const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CShape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x400090&lt;/span&gt;&lt;span class="p"&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;StoreField&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v44&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="n"&gt;_shape_id&lt;/span&gt;&lt;span class="mh"&gt;@0x4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v47&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;CheckInterrupts&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v22&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we had store-load forwarding in ZJIT, we could get rid of the intermediate
shape guards; they would know the shape from the previous &lt;code&gt;StoreField&lt;/code&gt;
instruction. If we had dead store elimination, we could get rid of the
intermediate shape writes; they are never read. (And the repeated type guards
to check if it's a heap object still are just silly and need to get removed
eventually.)&lt;/p&gt;
&lt;p&gt;This is on the roadmap and will make object initialization even faster than it
is right now.&lt;/p&gt;
&lt;h3 id="wrapping-up"&gt;Wrapping up&lt;/h3&gt;
&lt;p&gt;Thanks for reading the text version of the video that CF and I made a while
back. Now you know how to do load/store elimination on traces.&lt;/p&gt;
&lt;p&gt;I think this does not need too much extra work to get it going on full CFGs; a
block is pretty much the same as a trace, so you can do a block-local version
without much fuss. If you want to go global, you need dominator information and
gen-kill sets.&lt;/p&gt;
&lt;p&gt;Maybe I will touch on this in a future post...&lt;/p&gt;
&lt;h3 id="thank-you"&gt;Thank you&lt;/h3&gt;
&lt;p&gt;Thank you to CF, who walked me through this live on a stream two years ago!
This blog post wouldn't be possible without you.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:size"&gt;
&lt;p&gt;In this toy optimizer example, we are assuming that all reads and writes
are the same size and different offsets don't overlap at all. This is often
the case for managed runtimes, where object fields are pointer-sized and
all reads/writes are pointed aligned. &lt;a class="footnote-backref" href="https://www.pypy.org/posts/2025/12/toy-load-store.html#fnref:size" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:tbaa"&gt;
&lt;p&gt;We could do better. If we had type information, we could also use that
to make alias classes. Writes to a List will never overlap with writes to a
Map, for example. This requires your compiler to have strict aliasing—if
you can freely cast between types, as in C, then this tactic goes out the
window.&lt;/p&gt;
&lt;p&gt;This is called &lt;a href="https://www.pypy.org/assets/img/tbaa.pdf"&gt;Type-based alias analysis&lt;/a&gt; (PDF). &lt;a class="footnote-backref" href="https://www.pypy.org/posts/2025/12/toy-load-store.html#fnref:tbaa" 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><guid>https://www.pypy.org/posts/2025/12/toy-load-store.html</guid><pubDate>Wed, 24 Dec 2025 23:00:00 GMT</pubDate></item><item><title>PyPy v7.3.20 release</title><link>https://www.pypy.org/posts/2025/07/pypy-v7320-release.html</link><dc:creator>mattip</dc:creator><description>&lt;section id="pypy-v7-3-20-release-of-python-2-7-3-11"&gt;
&lt;h2&gt;PyPy v7.3.20: release of python 2.7, 3.11&lt;/h2&gt;
&lt;p&gt;The PyPy team is proud to release version 7.3.20 of PyPy after the previous
release on Feb 26, 2025. The release fixes some subtle bugs in ctypes and
&lt;code class="docutils literal"&gt;OrderedDict&lt;/code&gt; and makes PyPy3.11 compatible with an upcoming release of
Cython.&lt;/p&gt;
&lt;p&gt;The release includes two different interpreters:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;PyPy2.7, which is an interpreter supporting the syntax and the features of
Python 2.7 including the stdlib for CPython 2.7.18+ (the &lt;code class="docutils literal"&gt;+&lt;/code&gt; is for
backported security updates)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PyPy3.11, which is an interpreter supporting the syntax and the features of
Python 3.11, including the stdlib for CPython 3.11.13.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The interpreters are based on much the same codebase, thus the double
release. This is a micro release, all APIs are compatible with the other 7.3
releases.&lt;/p&gt;
&lt;p&gt;We recommend updating. You can find links to download the releases here:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a class="reference external" href="https://pypy.org/download.html"&gt;https://pypy.org/download.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We would like to thank our donors for the continued support of the PyPy
project. If PyPy is not quite good enough for your needs, we are available for
&lt;a class="reference external" href="https://www.pypy.org/pypy-sponsors.html"&gt;direct consulting&lt;/a&gt; work. If PyPy is helping you out, we would love to hear
about it and encourage submissions to our &lt;a class="reference external" href="https://pypy.org/blog"&gt;blog&lt;/a&gt; via a pull request
to &lt;a class="reference external" href="https://github.com/pypy/pypy.org"&gt;https://github.com/pypy/pypy.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We would also like to thank our contributors and encourage new people to join
the project. PyPy has many layers and we need help with all of them: bug fixes,
&lt;a class="reference external" href="https://doc.pypy.org/"&gt;PyPy&lt;/a&gt; and &lt;a class="reference external" href="https://rpython.readthedocs.org"&gt;RPython&lt;/a&gt; documentation improvements, or general &lt;a class="reference external" href="https://doc.pypy.org/project-ideas.html"&gt;help&lt;/a&gt; with
making RPython's JIT even better.&lt;/p&gt;
&lt;p&gt;If you are a python library maintainer and use C-extensions, please consider
making a &lt;a class="reference external" href="https://hpyproject.org/"&gt;HPy&lt;/a&gt; / &lt;a class="reference external" href="https://cffi.readthedocs.io"&gt;CFFI&lt;/a&gt; / &lt;a class="reference external" href="https://cppyy.readthedocs.io"&gt;cppyy&lt;/a&gt; version of your library that would be performant
on PyPy. In any case, &lt;a class="reference external" href="https://github.com/joerick/cibuildwheel"&gt;cibuildwheel&lt;/a&gt; supports building wheels for PyPy.&lt;/p&gt;
&lt;section id="what-is-pypy"&gt;
&lt;h3&gt;What is PyPy?&lt;/h3&gt;
&lt;p&gt;PyPy is a Python interpreter, a drop-in replacement for CPython
It's fast (&lt;a class="reference external" href="https://speed.pypy.org"&gt;PyPy and CPython&lt;/a&gt; performance
comparison) due to its integrated tracing JIT compiler.&lt;/p&gt;
&lt;p&gt;We also welcome developers of other &lt;a class="reference external" href="https://rpython.readthedocs.io/en/latest/examples.html"&gt;dynamic languages&lt;/a&gt; to see what RPython
can do for them.&lt;/p&gt;
&lt;p&gt;We provide binary builds for:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;x86&lt;/strong&gt; machines on most common operating systems
(Linux 32/64 bits, Mac OS 64 bits, Windows 64 bits)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;64-bit &lt;strong&gt;ARM&lt;/strong&gt; machines running Linux (&lt;code class="docutils literal"&gt;aarch64&lt;/code&gt;) and macos (&lt;code class="docutils literal"&gt;macos_arm64&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PyPy supports Windows 32-bit, Linux PPC64 big- and little-endian, Linux ARM
32 bit, RISC-V RV64IMAFD Linux, and s390x Linux but does not release binaries.
Please reach out to us if you wish to sponsor binary releases for those
platforms. Downstream packagers provide binary builds for debian, Fedora,
conda, OpenBSD, FreeBSD, Gentoo, and more.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="what-else-is-new"&gt;
&lt;h3&gt;What else is new?&lt;/h3&gt;
&lt;p&gt;For more information about the 7.3.20 release, see the &lt;a class="reference external" href="https://doc.pypy.org/release-v7.3.20.html#changelog"&gt;full changelog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please update, and continue to help us make pypy better.&lt;/p&gt;
&lt;p&gt;Cheers,
The PyPy Team&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;</description><category>release</category><guid>https://www.pypy.org/posts/2025/07/pypy-v7320-release.html</guid><pubDate>Fri, 04 Jul 2025 12:00:00 GMT</pubDate></item><item><title>How fast can the RPython GC allocate?</title><link>https://www.pypy.org/posts/2025/06/rpython-gc-allocation-speed.html</link><dc:creator>CF Bolz-Tereick</dc:creator><description>&lt;p&gt;While working on a paper about &lt;a href="https://pypy.org/posts/2025/02/pypy-gc-sampling.html"&gt;allocation profiling in
VMProf&lt;/a&gt; I got curious
about how quickly the RPython GC can allocate an object. I wrote a small
RPython benchmark program to get an idea of the order of magnitude.&lt;/p&gt;
&lt;p&gt;The basic idea is to just allocate an instance in a tight loop:&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;A&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&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;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# preliminary idea, see below&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;loops&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The RPython type inference will find out that instances of &lt;code&gt;A&lt;/code&gt; have a single
&lt;code&gt;i&lt;/code&gt; field, which is an integer. In addition to that field, every RPython object
needs one word of GC meta-information. Therefore one instance of &lt;code&gt;A&lt;/code&gt; needs 16
bytes on a 64-bit architecture.&lt;/p&gt;
&lt;p&gt;However, measuring like this is not good enough, because the RPython static
optimizer would remove the allocation since the object isn't used. But we can
confuse the escape analysis sufficiently by always keeping two instances alive
at the same time:&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;A&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&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;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prev&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;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;loops&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&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;prev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# print the instances at the end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(I confirmed that the allocation isn't being removed by looking at the C code
that the RPython compiler generates from this.)&lt;/p&gt;
&lt;p&gt;This is doing a little bit more work than needed, because of the &lt;code&gt;a.i = i&lt;/code&gt;
instance attribute write. We can also (optionally) leave the field
uninitialized.&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;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialize_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&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;initialize_field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prev&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;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;loops&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
            &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&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;prev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# make sure always two objects are alive&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;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prev&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;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;loops&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
            &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A&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;prev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&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;t2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'s'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;object_size_in_words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="c1"&gt;# GC header, one integer field&lt;/span&gt;
    &lt;span class="n"&gt;mem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loops&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;object_size_in_words&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;1024.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;1024.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;1024.0&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;mem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'GB'&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;mem&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'GB/s'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then we need to add some RPython scaffolding:&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;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;loops&lt;/span&gt; &lt;span class="o"&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;argv&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;with_init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bool&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;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;with_init&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;"with initialization"&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="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"without initialization"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;with_init&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&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;target&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To build a binary:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="go"&gt;pypy rpython/bin/rpython targetallocatealot.py&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which will turn the RPython code into C code and use a C compiler to turn that
into a binary, containing both our code above as well as the RPython garbage
collector.&lt;/p&gt;
&lt;p&gt;Then we can run it (all results again from my AMD Ryzen 7 PRO 7840U, running
Ubuntu Linux 24.04.2):&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;./targetallocatealot-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1000000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="go"&gt;without initialization&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;A object at 0x7c71ad84cf60&amp;gt; &amp;lt;A object at 0x7c71ad84cf70&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;0.433825 s&lt;/span&gt;
&lt;span class="go"&gt;14.901161 GB&lt;/span&gt;
&lt;span class="go"&gt;34.348322 GB/s&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;./targetallocatealot-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1000000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="go"&gt;with initialization&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;A object at 0x71b41c82cf60&amp;gt; &amp;lt;A object at 0x71b41c82cf70&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;0.501856 s&lt;/span&gt;
&lt;span class="go"&gt;14.901161 GB&lt;/span&gt;
&lt;span class="go"&gt;29.692100 GB/s&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let's compare it with the Boehm GC:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;pypy&lt;span class="w"&gt; &lt;/span&gt;rpython/bin/rpython&lt;span class="w"&gt; &lt;/span&gt;--gc&lt;span class="o"&gt;=&lt;/span&gt;boehm&lt;span class="w"&gt; &lt;/span&gt;--output&lt;span class="o"&gt;=&lt;/span&gt;targetallocatealot-c-boehm&lt;span class="w"&gt; &lt;/span&gt;targetallocatealot.py&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="go"&gt;...&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;./targetallocatealot-c-boehm&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1000000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="go"&gt;without initialization&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;A object at 0xffff8bd058a6e3af&amp;gt; &amp;lt;A object at 0xffff8bd058a6e3bf&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;9.722585 s&lt;/span&gt;
&lt;span class="go"&gt;14.901161 GB&lt;/span&gt;
&lt;span class="go"&gt;1.532634 GB/s&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;./targetallocatealot-c-boehm&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1000000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="go"&gt;with initialization&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;A object at 0xffff88e1132983af&amp;gt; &amp;lt;A object at 0xffff88e1132983bf&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;9.684149 s&lt;/span&gt;
&lt;span class="go"&gt;14.901161 GB&lt;/span&gt;
&lt;span class="go"&gt;1.538717 GB/s&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is not a fair comparison, because the Boehm GC uses conservative stack
scanning, therefore it cannot move objects, which requires much more
complicated allocation.&lt;/p&gt;
&lt;h3 id="lets-look-at-perf-stats"&gt;Let's look at &lt;code&gt;perf stats&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;We can use &lt;code&gt;perf&lt;/code&gt; to get some statistics about the executions:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;perf&lt;span class="w"&gt; &lt;/span&gt;stat&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;cache-references,cache-misses,cycles,instructions,branches,faults,migrations&lt;span class="w"&gt; &lt;/span&gt;./targetallocatealot-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10000000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="go"&gt;without initialization&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;A object at 0x7aa260e35980&amp;gt; &amp;lt;A object at 0x7aa260e35990&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;4.301442 s&lt;/span&gt;
&lt;span class="go"&gt;149.011612 GB&lt;/span&gt;
&lt;span class="go"&gt;34.642245 GB/s&lt;/span&gt;

&lt;span class="go"&gt; Performance counter stats for './targetallocatealot-c 10000000000 0':&lt;/span&gt;

&lt;span class="go"&gt;     7,244,117,828      cache-references                                                      &lt;/span&gt;
&lt;span class="go"&gt;        23,446,661      cache-misses                     #    0.32% of all cache refs         &lt;/span&gt;
&lt;span class="go"&gt;    21,074,240,395      cycles                                                                &lt;/span&gt;
&lt;span class="go"&gt;   110,116,790,943      instructions                     #    5.23  insn per cycle            &lt;/span&gt;
&lt;span class="go"&gt;    20,024,347,488      branches                                                              &lt;/span&gt;
&lt;span class="go"&gt;             1,287      faults                                                                &lt;/span&gt;
&lt;span class="go"&gt;                24      migrations                                                            &lt;/span&gt;

&lt;span class="go"&gt;       4.303071693 seconds time elapsed&lt;/span&gt;

&lt;span class="go"&gt;       4.297557000 seconds user&lt;/span&gt;
&lt;span class="go"&gt;       0.003998000 seconds sys&lt;/span&gt;

&lt;span class="gp"&gt;$ &lt;/span&gt;perf&lt;span class="w"&gt; &lt;/span&gt;stat&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;cache-references,cache-misses,cycles,instructions,branches,faults,migrations&lt;span class="w"&gt; &lt;/span&gt;./targetallocatealot-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10000000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="go"&gt;with initialization&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;A object at 0x77ceb0235980&amp;gt; &amp;lt;A object at 0x77ceb0235990&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;5.016772 s&lt;/span&gt;
&lt;span class="go"&gt;149.011612 GB&lt;/span&gt;
&lt;span class="go"&gt;29.702688 GB/s&lt;/span&gt;

&lt;span class="go"&gt; Performance counter stats for './targetallocatealot-c 10000000000 1':&lt;/span&gt;

&lt;span class="go"&gt;     7,571,461,470      cache-references                                                      &lt;/span&gt;
&lt;span class="go"&gt;       241,915,266      cache-misses                     #    3.20% of all cache refs         &lt;/span&gt;
&lt;span class="go"&gt;    24,503,497,532      cycles                                                                &lt;/span&gt;
&lt;span class="go"&gt;   130,126,387,460      instructions                     #    5.31  insn per cycle            &lt;/span&gt;
&lt;span class="go"&gt;    20,026,280,693      branches                                                              &lt;/span&gt;
&lt;span class="go"&gt;             1,285      faults                                                                &lt;/span&gt;
&lt;span class="go"&gt;                21      migrations                                                            &lt;/span&gt;

&lt;span class="go"&gt;       5.019444749 seconds time elapsed&lt;/span&gt;

&lt;span class="go"&gt;       5.012924000 seconds user&lt;/span&gt;
&lt;span class="go"&gt;       0.005999000 seconds sys&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is pretty cool, we can run this loop with &amp;gt;5 instructions per cycle. Every
allocation takes &lt;code&gt;110116790943 / 10000000000 ≈ 11&lt;/code&gt; instructions and
&lt;code&gt;21074240395 / 10000000000 ≈ 2.1&lt;/code&gt; cycles, including the loop around it.&lt;/p&gt;
&lt;h3 id="how-often-does-the-gc-run"&gt;How often does the GC run?&lt;/h3&gt;
&lt;p&gt;The RPython GC queries the L2 cache size to determine the size of the nursery.
We can find out what it is by turning on PYPYLOG, selecting the proper logging
categories, and printing to &lt;code&gt;stdout&lt;/code&gt; via &lt;code&gt;:-&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nv"&gt;PYPYLOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gc-set-nursery-size,gc-hardware:-&lt;span class="w"&gt; &lt;/span&gt;./targetallocatealot-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="go"&gt;[f3e6970465723] {gc-set-nursery-size&lt;/span&gt;
&lt;span class="go"&gt;nursery size: 270336&lt;/span&gt;
&lt;span class="go"&gt;[f3e69704758f3] gc-set-nursery-size}&lt;/span&gt;
&lt;span class="go"&gt;[f3e697047b9a1] {gc-hardware&lt;/span&gt;
&lt;span class="go"&gt;L2cache = 1048576&lt;/span&gt;
&lt;span class="go"&gt;[f3e69705ced19] gc-hardware}&lt;/span&gt;
&lt;span class="go"&gt;[f3e69705d11b5] {gc-hardware&lt;/span&gt;
&lt;span class="go"&gt;memtotal = 32274210816.000000&lt;/span&gt;
&lt;span class="go"&gt;[f3e69705f4948] gc-hardware}&lt;/span&gt;
&lt;span class="go"&gt;[f3e6970615f78] {gc-set-nursery-size&lt;/span&gt;
&lt;span class="go"&gt;nursery size: 4194304&lt;/span&gt;
&lt;span class="go"&gt;[f3e697061ecc0] gc-set-nursery-size}&lt;/span&gt;
&lt;span class="go"&gt;with initialization&lt;/span&gt;
&lt;span class="go"&gt;NULL &amp;lt;A object at 0x7fa7b1434020&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;0.000008 s&lt;/span&gt;
&lt;span class="go"&gt;0.000000 GB&lt;/span&gt;
&lt;span class="go"&gt;0.001894 GB/s&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So the nursery is 4 MiB. This means that when we allocate 14.9 GiB the GC needs to perform &lt;code&gt;10000000000 * 16 / 4194304 ≈ 38146&lt;/code&gt; minor collections. Let's confirm that:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nv"&gt;PYPYLOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gc-minor:out&lt;span class="w"&gt; &lt;/span&gt;./targetallocatealot-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10000000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="go"&gt;with initialization&lt;/span&gt;
&lt;span class="go"&gt;w&amp;lt;A object at 0x7991e3835980&amp;gt; &amp;lt;A object at 0x7991e3835990&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;5.315511 s&lt;/span&gt;
&lt;span class="go"&gt;149.011612 GB&lt;/span&gt;
&lt;span class="go"&gt;28.033356 GB/s&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;head&lt;span class="w"&gt; &lt;/span&gt;out
&lt;span class="go"&gt;[f3ee482f4cd97] {gc-minor&lt;/span&gt;
&lt;span class="go"&gt;[f3ee482f53874] {gc-minor-walkroots&lt;/span&gt;
&lt;span class="go"&gt;[f3ee482f54117] gc-minor-walkroots}&lt;/span&gt;
&lt;span class="go"&gt;minor collect, total memory used: 0&lt;/span&gt;
&lt;span class="go"&gt;number of pinned objects: 0&lt;/span&gt;
&lt;span class="go"&gt;total size of surviving objects: 0&lt;/span&gt;
&lt;span class="go"&gt;time taken: 0.000029&lt;/span&gt;
&lt;span class="go"&gt;[f3ee482f67b7e] gc-minor}&lt;/span&gt;
&lt;span class="go"&gt;[f3ee4838097c5] {gc-minor&lt;/span&gt;
&lt;span class="go"&gt;[f3ee48380c945] {gc-minor-walkroots&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{gc-minor-walkroots"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;out&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;wc&lt;span class="w"&gt; &lt;/span&gt;-l
&lt;span class="go"&gt;38147&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Each minor collection is very quick, because a minor collection is
O(surviving objects), and in this program only one object survive each time
(the other instance is in the process of being allocated).
Also, the GC root shadow stack is only one entry, so walking that is super
quick as well. The time the minor collections take is logged to the out file:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"time taken"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;out&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tail
&lt;span class="go"&gt;time taken: 0.000002&lt;/span&gt;
&lt;span class="go"&gt;time taken: 0.000002&lt;/span&gt;
&lt;span class="go"&gt;time taken: 0.000002&lt;/span&gt;
&lt;span class="go"&gt;time taken: 0.000002&lt;/span&gt;
&lt;span class="go"&gt;time taken: 0.000002&lt;/span&gt;
&lt;span class="go"&gt;time taken: 0.000002&lt;/span&gt;
&lt;span class="go"&gt;time taken: 0.000002&lt;/span&gt;
&lt;span class="go"&gt;time taken: 0.000003&lt;/span&gt;
&lt;span class="go"&gt;time taken: 0.000002&lt;/span&gt;
&lt;span class="go"&gt;time taken: 0.000002&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"time taken"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;out&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;numsum
&lt;span class="go"&gt;0.0988160000000011&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(This number is super approximate due to float formatting rounding.)&lt;/p&gt;
&lt;p&gt;that means that &lt;code&gt;0.0988160000000011 / 5.315511 ≈ 2%&lt;/code&gt; of the time is spent in the GC.&lt;/p&gt;
&lt;h3 id="what-does-the-generated-machine-code-look-like"&gt;What does the generated machine code look like?&lt;/h3&gt;
&lt;p&gt;The allocation fast path of the RPython GC is a simple bump pointer, in Python
pseudo-code it would look roughly like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nursery_free&lt;/span&gt;
&lt;span class="c1"&gt;# Move nursery_free pointer forward by totalsize&lt;/span&gt;
&lt;span class="n"&gt;gc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nursery_free&lt;/span&gt; &lt;span class="o"&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;totalsize&lt;/span&gt;
&lt;span class="c1"&gt;# Check if this allocation would exceed the nursery&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;gc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nursery_free&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;gc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nursery_top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# If it does =&amp;gt; collect the nursery and al&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;collect_and_reserve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;totalsize&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;hdr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GC&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So we can disassemble the compiled binary &lt;code&gt;targetallocatealot-c&lt;/code&gt; and try to
find the equivalent logic in machine code. I'm super bad at reading machine
code, but I tried to annotate what I think is the core loop (the version
without initializing the &lt;code&gt;i&lt;/code&gt; field) below:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cb68&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rbx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rdi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cb6b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rdx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rbx&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;# initialize object header of object allocated in previous iteration&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cb6e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;movq&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;$0x4c8&lt;/span&gt;&lt;span class="p"&gt;,(&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rbx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;# loop termination check&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cb75&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rbp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;r12&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cb78&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;je&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;ccb8&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;# load nursery_free&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cb7e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mh"&gt;0x33c13&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rip&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rdx&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;# increment loop counter&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cb85&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;$0x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rbp&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;# add 16 (size of object) to nursery_free&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cb89&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;lea&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mh"&gt;0x10&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rdx&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rax&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;# compare nursery_top with new nursery_free&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cb8d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x33c24&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;# store new nursery_free&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cb94&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x33bfd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;# if new nursery_free exceeds nursery_top, fall through to slow path, if not, start at top&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cb9b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;jae&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;cb68&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;# slow path from here on:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;# save live object from last iteration to GC shadow stack&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cb9d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rbx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;-0x8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cba1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;r13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;rdi&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cba4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;$0x10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;esi&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;# do minor collection&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cba9&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;20800&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;pypy_g_IncrementalMiniMarkGC_collect_and_reserve&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="running-the-benchmark-as-regular-python-code"&gt;Running the benchmark as regular Python code&lt;/h3&gt;
&lt;p&gt;So far we ran this code as &lt;em&gt;RPython&lt;/em&gt;, i.e. type inference is performed and the
program is translated to a C binary. We can also run it on top of PyPy, as a
regular Python3 program. However, an instance of a user-defined class in regular
Python when run on PyPy is actually a much larger object, due to &lt;a href="https://pypy.org/posts/2010/11/efficiently-implementing-python-objects-3838329944323946932.html"&gt;dynamic
typing&lt;/a&gt;.
It's at least 7 words, which is 56 bytes.&lt;/p&gt;
&lt;p&gt;However, we can simply use &lt;code&gt;int&lt;/code&gt; objects instead. Integers are allocated on the
heap and consist of two words, one for the GC and one with the
machine-word-sized integer value, if the integer fits into a signed 64-bit
representation (otherwise a less compact different representation is used,
which can represent arbitrarily large integers).&lt;/p&gt;
&lt;p&gt;Therefore, we can simply use this kind of code:&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;sys&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;time&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;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prev&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;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;loops&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&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;prev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# make sure always two objects are alive&lt;/span&gt;
    &lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;object_size_in_words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="c1"&gt;# GC header, one integer field&lt;/span&gt;
    &lt;span class="n"&gt;mem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loops&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;1024.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;1024.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;1024.0&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;mem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'GB'&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;mem&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'GB/s'&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;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;loops&lt;/span&gt; &lt;span class="o"&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;argv&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;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'__main__'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this case we can't really leave the value uninitialized though.&lt;/p&gt;
&lt;p&gt;We can run this both with and without the JIT:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;pypy3&lt;span class="w"&gt; &lt;/span&gt;allocatealot.py&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1000000000&lt;/span&gt;
&lt;span class="go"&gt;999999998 999999999&lt;/span&gt;
&lt;span class="go"&gt;14.901161193847656 GB&lt;/span&gt;
&lt;span class="go"&gt;17.857494904899553 GB/s&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;pypy3&lt;span class="w"&gt; &lt;/span&gt;--jit&lt;span class="w"&gt; &lt;/span&gt;off&lt;span class="w"&gt; &lt;/span&gt;allocatealot.py&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1000000000&lt;/span&gt;
&lt;span class="go"&gt;999999998 999999999&lt;/span&gt;
&lt;span class="go"&gt;14.901161193847656 GB&lt;/span&gt;
&lt;span class="go"&gt;0.8275382375297171 GB/s&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is obviously much less efficient than the C code, the PyPy JIT generates
much less efficient machine code than GCC. Still, "only" twice as slow is kind
of cool anyway.&lt;/p&gt;
&lt;p&gt;(Running it with CPython doesn't really make sense for this measurements, since
CPython ints are bigger – &lt;code&gt;sys.getsizeof(5)&lt;/code&gt; reports 28 bytes.)&lt;/p&gt;
&lt;h3 id="the-machine-code-that-the-jit-generates"&gt;The machine code that the JIT generates&lt;/h3&gt;
&lt;p&gt;Unfortunately it's a bit of a journey to show the machine code that PyPy's JIT generates for this. First we need to run with all jit logging categories:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nv"&gt;PYPYLOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jit:out&lt;span class="w"&gt; &lt;/span&gt;pypy3&lt;span class="w"&gt; &lt;/span&gt;allocatealot.py&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1000000000&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then we can read the log file to find the trace IR for the loop under the logging category &lt;code&gt;jit-log-opt&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;532&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i44&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;descr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;TargetToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;137358545605472&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;debug_merge_point&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="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="s1"&gt;'run;/home/cfbolz/projects/gitpypy/allocatealot.py:6-9~#24 FOR_ITER'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# are we at the end of the loop&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;552&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i45&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;int_lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i44&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i35&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;555&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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;i45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;descr&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Guard0x7ced4756a160&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i44&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i34&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;561&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i47&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;int_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i44&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="n"&gt;debug_merge_point&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="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="s1"&gt;'run;/home/cfbolz/projects/gitpypy/allocatealot.py:6-9~#26 STORE_FAST'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;debug_merge_point&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="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="s1"&gt;'run;/home/cfbolz/projects/gitpypy/allocatealot.py:6-10~#28 LOAD_FAST'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;debug_merge_point&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="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="s1"&gt;'run;/home/cfbolz/projects/gitpypy/allocatealot.py:6-10~#30 STORE_FAST'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;debug_merge_point&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="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="s1"&gt;'run;/home/cfbolz/projects/gitpypy/allocatealot.py:6-11~#32 LOAD_FAST'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;debug_merge_point&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="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="s1"&gt;'run;/home/cfbolz/projects/gitpypy/allocatealot.py:6-11~#34 STORE_FAST'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;debug_merge_point&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="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="s1"&gt;'run;/home/cfbolz/projects/gitpypy/allocatealot.py:6-11~#36 JUMP_ABSOLUTE'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# update iterator object&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;565&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;setfield_gc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i47&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;descr&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FieldS&lt;/span&gt;&lt;span class="w"&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;module&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__builtin__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;functional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;W_IntRangeIterator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inst_current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;&amp;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;569&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;guard_not_invalidated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descr&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Guard0x7ced4756a1b0&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i44&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i34&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# check for signals&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;569&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i49&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;getfield_raw_i&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;137358624889824&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;descr&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FieldS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pypysig_long_struct_inner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;&amp;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;582&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i51&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;int_lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i49&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="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;586&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;guard_false&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i51&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;descr&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Guard0x7ced4754db78&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i44&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i34&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;debug_merge_point&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="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="s1"&gt;'run;/home/cfbolz/projects/gitpypy/allocatealot.py:6-9~#24 FOR_ITER'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# allocate the integer (allocation sunk to the end of the trace)&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;592&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p52&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;new_with_vtable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descr&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SizeDescr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="o"&gt;&amp;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;630&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;setfield_gc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p52&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;descr&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FieldS&lt;/span&gt;&lt;span class="w"&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;objspace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intobject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;W_IntObject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inst_intval&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pure&lt;/span&gt;&lt;span class="o"&gt;&amp;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;634&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i44&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p52&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i47&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;descr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;TargetToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;137358545605472&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To find the machine code address of the trace, we need to search for this line:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nx"&gt;Loop&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;cfbolz&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;projects&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;gitpypy&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;allocatealot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;FOR_ITER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;has&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x7ced473ffa0b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x7ced473ffbb0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bootstrap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x7ced473ff980&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then we can use a script in the PyPy repo to disassemble the generated machine code:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;pypy&lt;span class="w"&gt; &lt;/span&gt;rpython/jit/backend/tool/viewcode.py&lt;span class="w"&gt; &lt;/span&gt;out
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will dump all the machine code to stdout, and open a &lt;a href="https://pypy.org/posts/2021/04/ways-pypy-graphviz.html"&gt;pygame-based
graphviz cfg&lt;/a&gt;. In there
we can search for the address and see this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Graphviz based visualization of the machine code the JIT generates" src="https://www.pypy.org/images/2025-allocatealot-machine-code.png"&gt;&lt;/p&gt;
&lt;p&gt;Here's an annotated version with what I think this code does:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="x"&gt;# increment the profile counter&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb40:   48 ff 04 25 20 9e 33    incq   0x38339e20&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb47:   38 &lt;/span&gt;

&lt;span class="x"&gt;# check whether the loop is done&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb48:   4c 39 fe                cmp    %r15,%rsi&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb4b:   0f 8d 76 01 00 00       jge    0x7ced473ffcc7&lt;/span&gt;

&lt;span class="x"&gt;# increment iteration variable&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb51:   4c 8d 66 01             lea    0x1(%rsi),%r12&lt;/span&gt;

&lt;span class="x"&gt;# update iterator object&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb55:   4d 89 61 08             mov    %r12,0x8(%r9)&lt;/span&gt;

&lt;span class="x"&gt;# check for ctrl-c/thread switch&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb59:   49 bb e0 1b 0b 4c ed    movabs $0x7ced4c0b1be0,%r11&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb60:   7c 00 00 &lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb63:   49 8b 0b                mov    (%r11),%rcx&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb66:   48 83 f9 00             cmp    $0x0,%rcx&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb6a:   0f 8c 8f 01 00 00       jl     0x7ced473ffcff&lt;/span&gt;

&lt;span class="x"&gt;# load nursery_free pointer&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb70:   49 8b 8b d8 30 f6 fe    mov    -0x109cf28(%r11),%rcx&lt;/span&gt;

&lt;span class="x"&gt;# add size (16)&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb77:   48 8d 51 10             lea    0x10(%rcx),%rdx&lt;/span&gt;

&lt;span class="x"&gt;# compare against nursery top&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb7b:   49 3b 93 f8 30 f6 fe    cmp    -0x109cf08(%r11),%rdx&lt;/span&gt;

&lt;span class="x"&gt;# jump to slow path if nursery is full&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb82:   0f 87 41 00 00 00       ja     0x7ced473ffbc9&lt;/span&gt;

&lt;span class="x"&gt;# store new value of nursery free&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb88:   49 89 93 d8 30 f6 fe    mov    %rdx,-0x109cf28(%r11)&lt;/span&gt;

&lt;span class="x"&gt;# initialize GC header&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb8f:   48 c7 01 30 11 00 00    movq   $0x1130,(%rcx)&lt;/span&gt;

&lt;span class="x"&gt;# initialize integer field&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb96:   48 89 41 08             mov    %rax,0x8(%rcx)&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb9a:   48 89 f0                mov    %rsi,%rax&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffb9d:   48 89 8d 60 01 00 00    mov    %rcx,0x160(%rbp)&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffba4:   4c 89 e6                mov    %r12,%rsi&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffba7:   e9 94 ff ff ff          jmp    0x7ced473ffb40&lt;/span&gt;
&lt;span class="x"&gt;7ced473ffbac:   0f 1f 40 00             nopl   0x0(%rax)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="conclusion"&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;The careful design of the RPython GC's allocation fast path gives pretty good
allocation rates. This technique isn't really new, it's a pretty typical way to
design a GC. Apart from that, my main conclusion would be that computers are
fast or something? Indeed, when we ran the same code on my colleague's
two-year-old AMD, we got quite a bit worse results, so a lot of the speed seems
to be due to the hard work of CPU architects.&lt;/p&gt;</description><category>benchmarking</category><category>gc</category><category>rpython</category><guid>https://www.pypy.org/posts/2025/06/rpython-gc-allocation-speed.html</guid><pubDate>Sun, 15 Jun 2025 13:48:30 GMT</pubDate></item></channel></rss>