<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://www.cs.cornell.edu/~asampson//</id>
  <title>Adrian Sampson</title>
  <updated>2026-06-06T13:08:32+00:00</updated>
  <link rel="alternate" href="https://www.cs.cornell.edu/~asampson/"/>
  <link rel="self" href="https://www.cs.cornell.edu/~asampson/blog.xml"/>
  <icon>https://www.cs.cornell.edu/~asampson/media/icon/favicon.ico</icon>
  <author>
    <name>Adrian Sampson</name>
    <uri>https://www.cs.cornell.edu/~asampson/</uri>
  </author>

  
  <entry>
    
    <id>https://www.cs.cornell.edu/~asampson/blog/buildingblocks.html</id>
    
    <title type="html">Back to the Building Blocks’ Building Blocks</title>
    <updated>2026-05-26T00:00:00+00:00</updated>
    <link rel="alternate" href="https://www.cs.cornell.edu/~asampson/blog/buildingblocks.html"/>
    <content type="html">&lt;aside&gt;
This post is based on a keynote I gave at &lt;a href=&quot;https://www.dagstuhl.de/26042/&quot;&gt;Dagstuhl Seminar #26042, &lt;i&gt;Trustworthy System Architectures for the Age of Custom Silicon&lt;/i&gt;&lt;/a&gt;. Many thanks to the seminar&apos;s organizers and to the Dagstuhl staff for a fun and enlightening workshop.
&lt;/aside&gt;

&lt;p&gt;If there are hardware engineers who love Verilog, I haven&amp;rsquo;t met them.
Almost universally, the attitude toward Verilog seems to be that it&amp;rsquo;s frustrating, ridiculous, error-prone, and the only pragmatic choice.&lt;/p&gt;

&lt;p&gt;Verilog is inescapable because it is the input format to essentially every EDA tool.
Its centrality means that it is a &lt;i&gt;de facto&lt;/i&gt; intermediate representation implementing for every other HDL:
even if you prefer &lt;a href=&quot;https://github.com/B-Lang-org/bsc&quot;&gt;Bluespec&lt;/a&gt;, &lt;a href=&quot;https://www.chisel-lang.org&quot;&gt;Chisel&lt;/a&gt;, &lt;a href=&quot;https://amaranth-lang.org/&quot;&gt;Amaranth&lt;/a&gt;, or &lt;a href=&quot;https://spade-lang.org&quot;&gt;Spade&lt;/a&gt;, they usually compile to Verilog to interact with the rest of the hardware world.&lt;sup id=&quot;fnref:amaranth&quot;&gt;&lt;a href=&quot;#fn:amaranth&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;I am worried that Verilog&amp;rsquo;s flaws will be the cause of a new wave of hardware bugs.
There is an analogy to the problems with C and C++:
as designing custom hardware becomes more popular, we risk allowing a dangerous HDL to proliferate and fester in the same way that memory-unsafe programming languages have in software.&lt;/p&gt;

&lt;p&gt;I don&amp;rsquo;t know yet what the analog of memory safety is for hardware bugs, or even if there will be a single dominant defect category.
Footguns abound in Verilog, though, so there are many good candidates.
This post makes the case that we should invest in better understanding the problems with Verilog so that future HDLs can avoid them.&lt;/p&gt;

&lt;h2 id=&quot;on-building-blocks&quot;&gt;On Building Blocks&lt;/h2&gt;

&lt;figure style=&quot;max-width: 256px&quot;&gt;
&lt;a href=&quot;https://bidenwhitehouse.archives.gov/wp-content/uploads/2024/02/Final-ONCD-Technical-Report.pdf&quot;&gt;&lt;img src=&quot;/~asampson/media/buildingblocks.png&quot; /&gt;&lt;/a&gt;
&lt;/figure&gt;

&lt;p&gt;As an American programming languages nerd, I believe that
&lt;a href=&quot;https://bidenwhitehouse.archives.gov/wp-content/uploads/2024/02/Final-ONCD-Technical-Report.pdf&quot;&gt;&amp;ldquo;Back to the Building Blocks&amp;rdquo;&lt;/a&gt; was one of the most exciting developments of the last decade.
It&amp;rsquo;s a 2024 report from the White House Office of the National Cyber Director that makes the case for memory safety.
It calls for critical infrastructure to move on from memory-unsafe languages like C and C++, and it even mentions &lt;a href=&quot;https://rust-lang.org&quot;&gt;Rust&lt;/a&gt; by name as a promising alternative.&lt;/p&gt;

&lt;p&gt;&amp;ldquo;Back to the Building Blocks&amp;rdquo; didn&amp;rsquo;t break new ground:
by 2024, it was obvious to all reasonable people that memory safety was a huge problem.
It was exciting because &lt;em&gt;Joe Biden&lt;/em&gt; was saying the same things that we had all been saying for years.&lt;sup id=&quot;fnref:biden&quot;&gt;&lt;a href=&quot;#fn:biden&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;
I&amp;rsquo;m not a very patriotic person, but my heart soars like a majestic bald eagle when I read stuff like this in a government report:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Despite rigorous code reviews as well as other preventive and detective controls, up to 70 percent of security vulnerabilities in memory unsafe languages patched and assigned a CVE designation are due to memory safety issues.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And I can hear the national anthem playing when the report says:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;For new products, choosing to build in a memory safe programming language is an early architecture decision that can deliver significant security benefits. Even for existing codebases, where a complete rewrite of code is more challenging, there are still paths toward adopting memory safe programming languages by taking a hybrid approach.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;God bless America.
&amp;ldquo;Back to the Building Blocks&amp;rdquo; distills a hard-to-refute syllogism along these lines:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Correctness matters.&lt;/li&gt;
  &lt;li&gt;There exist large classes of bugs with similar root causes.&lt;/li&gt;
  &lt;li&gt;Some languages lead to a higher frequency of these bug classes than other languages.&lt;/li&gt;
  &lt;li&gt;We should therefore see these bugs as the &amp;ldquo;fault&amp;rdquo; of the language, not the programmer.&lt;/li&gt;
  &lt;li&gt;Languages that are harder to use but dramatically reduce the frequency of these bugs may be worth it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the original report, this &amp;ldquo;Building Blocks&amp;rdquo; argument was about C.
But I believe the same reasoning applies to Verilog.&lt;/p&gt;

&lt;h2 id=&quot;the-claim-weak-and-strong-forms&quot;&gt;The Claim (Weak and Strong Forms)&lt;/h2&gt;

&lt;p&gt;I&amp;rsquo;ll state my thesis in a weak form and a strong form; you can pick which level you want to consider.
The weak form is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Verilog causes lots of bugs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And the strong form is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The next &amp;ldquo;Building Blocks&amp;rdquo; crisis will happen in hardware, and it will be Verilog&amp;rsquo;s fault.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words: at the same moment that we&amp;rsquo;re beginning to get a handle on memory safety,
we&amp;rsquo;re producing a lot more Verilog code.
So the next scourge of avoidable bugs could occur in hardware.
Better HDLs could dramatically reduce the frequency of these bugs.&lt;/p&gt;

&lt;p&gt;Verilog&amp;rsquo;s problems are not new, but until now, hardware design methodologies have attenuated their harm.
The traditional way to develop hardware&amp;mdash;the kind of process that big CPU vendors use&amp;mdash;mitigates Verilog&amp;rsquo;s problems in part by expending a ridiculous amount of resources on verification.
This observation is hard to justify with concrete evidence, but consider &lt;a href=&quot;https://resources.sw.siemens.com/en-US/white-paper-2022-wilson-research-group-functional-verification-study-ic-asic-functional-verification-trend-report/&quot;&gt;this somewhat dubious report from an industry consortium&lt;/a&gt; that claims that, in CPU design projects,
the ratio of verification engineers to design engineers is 5:1.
A terrible HDL matters less when you have a safety net like that.&lt;/p&gt;

&lt;p&gt;But today, more people want to design custom, application-specific hardware.
Specialized, lower-volume hardware projects will not (and should not) use the same engineering process as Apple&amp;rsquo;s next iPhone SoC.
The emerging long tail of cheaper, lighter-weight hardware design projects will be more vulnerable to Verilog&amp;rsquo;s problems.&lt;/p&gt;

&lt;h2 id=&quot;some-cheap-shots-at-verilog&quot;&gt;Some Cheap Shots at Verilog&lt;/h2&gt;

&lt;p&gt;The main point of this post is to call for systematic study of Verilog&amp;rsquo;s implications for hardware correctness, not to pick on specific flaws I personally love to hate.
But I can&amp;rsquo;t resist shooting a few fish in this barrel.&lt;/p&gt;

&lt;p&gt;The root of Verilog&amp;rsquo;s problems is that it was not designed for implementing hardware.
It was &lt;a href=&quot;https://dl.acm.org/doi/10.1145/3386337&quot;&gt;originally developed&lt;/a&gt; as a DSL for writing event-based &lt;em&gt;simulators&lt;/em&gt; of digital logic.
Later, logic synthesis tools repurposed Verilog for generating real netlists.
Many problems with Verilog stem from the confusing boundaries between simulation and implementation:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;There is an ill-defined &amp;ldquo;synthesizable&amp;rdquo; subset.&lt;/strong&gt; Tools can&amp;rsquo;t agree on what this subset is, but we can all agree that not all of Verilog can be sensibly translated into hardware.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Verilog requires load-bearing linters.&lt;/strong&gt; Serious hardware design shops pay for extremely expensive commercial tools that keep their engineers within the Verilog subset that their toolchain can handle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get concrete, let&amp;rsquo;s look at three specific footguns in Verilog:
inferred latches, the semantics of the &amp;ldquo;don&amp;rsquo;t care&amp;rdquo; value, and the absence of cycle-level timing information.
(As a warning, the latter is a self-serving complaint that motivates &lt;a href=&quot;https://filamenthdl.com&quot;&gt;some research I co-authored&lt;/a&gt;.)&lt;/p&gt;

&lt;h3 id=&quot;inferred-latches&quot;&gt;Inferred Latches&lt;/h3&gt;

&lt;p&gt;Verilog, people say, uses the register-transfer level (RTL) abstraction.
If this was the first thing I knew about Verilog, I would assume it works like this:
the language has special, built-in constructs for &lt;em&gt;state elements&lt;/em&gt; like latches, registers, and memories.
You write a Verilog program by describing how to compute the values of all state elements on cycle &lt;em&gt;n+1&lt;/em&gt; based on their values on cycle &lt;em&gt;n&lt;/em&gt;.
In an RTL language, it seems obvious that all the registers should be explicit and clearly separated from stateless combinational logic.&lt;/p&gt;

&lt;p&gt;Because of Verilog&amp;rsquo;s roots in event-based simulation, that&amp;rsquo;s not how it works.
Instead, variables &lt;em&gt;might&lt;/em&gt; be stateful and &lt;em&gt;might&lt;/em&gt; be stateless depending on how they are used.
For example, here&amp;rsquo;s one way to write a 32-bit latch in Verilog:&lt;/p&gt;

&lt;div class=&quot;language-verilog highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;latch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;input&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;wire&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;en&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;input&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;wire&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data_in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;reg&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data_out&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;always&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;en&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;data_out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data_in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;endmodule&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There&amp;rsquo;s an input data port, an output data port, and a 1-bit &lt;em&gt;enable&lt;/em&gt; signal that decides whether the latch should take on a new value.
The Verilog semantics say that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data_out&lt;/code&gt; must be stateful &lt;em&gt;because it is assigned conditionally&lt;/em&gt;.
That is, because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data_out&lt;/code&gt; doesn&amp;rsquo;t get assigned on cycles when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;en == 0&lt;/code&gt;, it keeps its old value&amp;mdash;implying that it requires a stateful circuit for its implementation.
Verilog engineers call this an &amp;ldquo;inferred latch.&amp;rdquo;&lt;sup id=&quot;fnref:reg&quot;&gt;&lt;a href=&quot;#fn:reg&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The footgun here is that it&amp;rsquo;s disturbingly easy to accidentally create a latch.
Consider this contrived implementation of an XOR gate:&lt;/p&gt;

&lt;div class=&quot;language-verilog highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;funny_xor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;input&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;wire&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in_bits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;output&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;reg&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;always&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_bits&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mb&quot;&gt;2&apos;b00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mb&quot;&gt;1&apos;b0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_bits&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mb&quot;&gt;2&apos;b01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mb&quot;&gt;1&apos;b1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_bits&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mb&quot;&gt;2&apos;b10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mb&quot;&gt;1&apos;b1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_bits&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mb&quot;&gt;2&apos;b11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mb&quot;&gt;1&apos;b0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;endmodule&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This module takes its two inputs on a single 2-bit port, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;in_bits&lt;/code&gt;.
This circuit is stateless because our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;else if&lt;/code&gt; cascade covers all the cases:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;out&lt;/code&gt; is assigned in every situation.&lt;/p&gt;

&lt;p&gt;But what happens if you forget one of these cases?
For example, let&amp;rsquo;s delete these two lines:&lt;/p&gt;

&lt;div class=&quot;language-verilog highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_bits&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mb&quot;&gt;2&apos;b10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mb&quot;&gt;1&apos;b1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The module is now stateful, and the hardware implementation requires a latch circuit.
Spooky.&lt;/p&gt;

&lt;h3 id=&quot;nonsense-semantics-for-x&quot;&gt;Nonsense Semantics for X&lt;/h3&gt;

&lt;p&gt;HDLs typically need a way to represent &lt;em&gt;don&amp;rsquo;t-care&lt;/em&gt; values, usually written as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X&lt;/code&gt;.
A little like undefined behavior (&lt;a href=&quot;https://www.ralfj.de/blog/2021/11/18/ub-good-idea.html&quot;&gt;in a good way&lt;/a&gt;), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X&lt;/code&gt; lets you convey to the optimizer that your specification only covers certain cases, and that it&amp;rsquo;s free to do what it wants in others.&lt;/p&gt;

&lt;p&gt;While &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X&lt;/code&gt; is a good and useful idea, Verilog&amp;rsquo;s semantics for it don&amp;rsquo;t make sense.
Here&amp;rsquo;s how it &lt;em&gt;should&lt;/em&gt; work: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X&lt;/code&gt; represents a bit that &lt;em&gt;might&lt;/em&gt; be 0 or 1, and we don&amp;rsquo;t know which.&lt;sup id=&quot;fnref:top&quot;&gt;&lt;a href=&quot;#fn:top&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;
By that definition, it would make sense that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X&lt;/code&gt; would propagate through Verilog&amp;rsquo;s math operators, like this:&lt;/p&gt;

&lt;div class=&quot;language-verilog highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optimism1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;reg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;reg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;initial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mb&quot;&gt;32&apos;bx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;in  = %d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;out = %d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;endmodule&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And indeed, simulating this module shows that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;out&lt;/code&gt; is also a &lt;em&gt;don&amp;rsquo;t care&lt;/em&gt; value, just like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;in&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ iverilog optimism1.v &amp;amp;&amp;amp; ./a.out
in  =          x
out =          x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All is right with the world.
Verilog also has a ternary operator, and we can try using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X&lt;/code&gt; in its condition:&lt;/p&gt;

&lt;div class=&quot;language-verilog highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;mb&quot;&gt;32&apos;b1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mb&quot;&gt;32&apos;b0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Gratefully, the result of an undefined condition is itself &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ iverilog optimism2.v &amp;amp;&amp;amp; ./a.out
in  =          x
out =          X
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;out&lt;/code&gt; might be 1 and it might be 0, it&amp;rsquo;s sensible that our simulator reports is value is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X&lt;/code&gt;.
Let&amp;rsquo;s go one step farther and rewrite that ternary operator using the long-form &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; equivalent:&lt;/p&gt;

&lt;div class=&quot;language-verilog highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mb&quot;&gt;32&apos;b1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mb&quot;&gt;32&apos;b0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And try simulating again:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ iverilog optimism3.v &amp;amp;&amp;amp; ./a.out
in  =          x
out =          0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://www.destroyallsoftware.com/talks/wat&quot;&gt;Wat.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This footgun has a name: &lt;em&gt;X-optimism&lt;/em&gt;.
That&amp;rsquo;s the standard behavior for Verilog: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; treats &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X&lt;/code&gt; as false, which &lt;em&gt;incorrectly&lt;/em&gt; makes conditional assignments appear defined when they are actually unknown.
Tragically, this behavior was &lt;a href=&quot;https://dl.acm.org/doi/10.1145/3386337&quot;&gt;apparently intentional&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This optimistic behavior of if-else was a deliberate decision by Moorby. He realized that in such a procedural context, evaluating both sides of the if-else expression could be enormously complicated. Reducing optimism requires execution time in the simulator.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Feel free to call me a PL nerd for saying this, but I don&amp;rsquo;t think simulation performance is a good justification for broken semantics.&lt;/p&gt;

&lt;p&gt;Unsurprisingly, researchers have explored how X optimism can lead to &lt;a href=&quot;https://dl.acm.org/doi/10.1145/3061639.3062328&quot;&gt;sneaky security problems&lt;/a&gt; because your simulator lies to you about what how final circuit will behave.&lt;/p&gt;

&lt;h3 id=&quot;timing-trouble&quot;&gt;Timing Trouble&lt;/h3&gt;

&lt;p&gt;This last footgun is a little different because it&amp;rsquo;s the problem we have tried to address in &lt;a href=&quot;https://dl.acm.org/doi/10.1145/3591234&quot;&gt;some of our recent research on safer HDLs&lt;/a&gt; (and it&amp;rsquo;s borrowed from that paper).
It&amp;rsquo;s not just a Verilog thing; it&amp;rsquo;s a danger in every major HDL I know of.
It&amp;rsquo;s also my best guess at this point for a bug category that&amp;rsquo;s analogous to memory safety problems in software.&lt;/p&gt;

&lt;p&gt;Let&amp;rsquo;s say you already have a multiplier and an adder:&lt;/p&gt;

&lt;div class=&quot;language-verilog highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mul32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;input&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;wire&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in_a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;input&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;wire&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in_b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;reg&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;endmodule&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Add32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;input&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;wire&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in_a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;input&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;wire&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in_b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;reg&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;endmodule&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Suppose you want to combine these two functional units into an ALU that can both add and multiply.
We&amp;rsquo;ll add a one-bit signal, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;op&lt;/code&gt;, that picks which FU to use:&lt;/p&gt;

&lt;div class=&quot;language-verilog highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alu&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;input&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;wire&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in_a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;input&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;wire&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in_b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;input&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;wire&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;reg&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;wire&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;wire&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mul_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;Add32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;Mul32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mul&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mul_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;always&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mb&quot;&gt;1&apos;b0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add_out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mul_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;endmodule&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This module instantiates an adder &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add&lt;/code&gt; and a multiplier &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mul&lt;/code&gt;, and it uses Verilog&amp;rsquo;s ternary operator to multiplex the output based on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;op&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This implementation works fine if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mul&lt;/code&gt; are both combinational.
More realistically, though, a 32-bit multiplier will probably be sequential and pipelined.
If that&amp;rsquo;s the case, this code is probably incorrect:
it has implicitly assumed that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Add32&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mul32&lt;/code&gt; have the same timing behavior.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s the problem: &lt;em&gt;there is nothing in the module signatures for the functional units that tells us that&amp;rsquo;s the case&lt;/em&gt;.
The interfaces for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Add32&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mul32&lt;/code&gt; are identical because they can only describe the physical shape of the ports, not the timing.
Even if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Add32&lt;/code&gt; is combinational and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mul32&lt;/code&gt; takes 3 cycles, their Verilog signatures would remain identical.
The only way to know how to use a Verilog module correctly is to read the comments (or to look directly at the source code).&lt;/p&gt;

&lt;p&gt;If you&amp;rsquo;re curious about this particular problem, please take a look at &lt;a href=&quot;https://dl.acm.org/doi/10.1145/3591234&quot;&gt;our paper about Filament&lt;/a&gt;, an HDL with a type system that enforces timing safety.&lt;sup id=&quot;fnref:rachit&quot;&gt;&lt;a href=&quot;#fn:rachit&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-building-blocks-building-blocks&quot;&gt;The Building Blocks&amp;rsquo; Building Blocks&lt;/h2&gt;

&lt;p&gt;If low-level systems like compilers are the building blocks for the software industry, then
hardware design tools are the building blocks for the building blocks.
The tower of system abstractions needs a strong foundation,
and Verilog is a cracked and crumbling concrete slab.&lt;/p&gt;

&lt;p&gt;We must work toward a post-Verilog future.
We need to invest more in modern HDLs, and we need toolchains that can support these superior languages without going through Verilog as the least common denominator.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:amaranth&quot;&gt;
      &lt;p&gt;Thanks to &lt;a href=&quot;https://social.treehouse.systems/@whitequark&quot;&gt;@whitequark&lt;/a&gt;, Amaranth&amp;rsquo;s maintainer, for pointing out that Amaranth can skip Verilog when targeting &lt;a href=&quot;https://yosyshq.net/yosys/&quot;&gt;Yosys&lt;/a&gt; by generating &lt;a href=&quot;https://yosyshq.readthedocs.io/projects/yosys/en/stable/yosys_internals/formats/rtlil_rep.html&quot;&gt;RTLIL&lt;/a&gt; directly. Awesome!&amp;nbsp;&lt;a href=&quot;#fnref:amaranth&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:biden&quot;&gt;
      &lt;p&gt;I choose to believe that Biden wrote &amp;ldquo;Back to the Building Blocks&amp;rdquo; all by himself, and I am not interested in evidence to the contrary.&amp;nbsp;&lt;a href=&quot;#fnref:biden&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:reg&quot;&gt;
      &lt;p&gt;It would be reasonable to guess that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reg&lt;/code&gt; keyword means that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data_out&lt;/code&gt; is stored in a register. That&amp;rsquo;s not the case. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reg&lt;/code&gt; declares mutable variables that can be either combinational or stateful, depending on how they&amp;rsquo;re used.&amp;nbsp;&lt;a href=&quot;#fnref:reg&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:top&quot;&gt;
      &lt;p&gt;In lattice terms, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X&lt;/code&gt; should work like a &lt;em&gt;top&lt;/em&gt; element.&amp;nbsp;&lt;a href=&quot;#fnref:top&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:rachit&quot;&gt;
      &lt;p&gt;Please direct all Filament fan mail to &lt;a href=&quot;https://people.csail.mit.edu/rachit/&quot;&gt;Rachit&lt;/a&gt;, who led that work.&amp;nbsp;&lt;a href=&quot;#fnref:rachit&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
    <summary type="html">&lt;p&gt;Verilog is the foundation of all hardware design, and it is fatally flawed. We should all be worried about a glut of hardware bugs caused by Verilog&amp;rsquo;s unpredictable semantics and simplistic type system.&lt;/p&gt;
</summary>
  </entry>
  
  <entry>
    
    <id>https://www.cs.cornell.edu/~asampson/blog/gator.html</id>
    
    <title type="html">Geometry Bugs and Geometry Types</title>
    <updated>2024-08-08T00:00:00+00:00</updated>
    <link rel="alternate" href="https://www.cs.cornell.edu/~asampson/blog/gator.html"/>
    <content type="html">&lt;figure style=&quot;width: 350px&quot;&gt;
  &lt;canvas width=&quot;350&quot; height=&quot;350&quot; id=&quot;diffuse-correct&quot;&gt;&lt;/canvas&gt;
  &lt;figcaption&gt;The diffuse component of the classic &lt;a href=&quot;https://en.wikipedia.org/wiki/Phong_reflection_model&quot;&gt;Phong lighting model&lt;/a&gt;, implemented (probably) correctly.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Phong_reflection_model&quot;&gt;Phong lighting model&lt;/a&gt; is the &amp;ldquo;hello world&amp;rdquo; of 3D rasterization effects.
Implementing it seems like an appropriate level of challenge for me, a total graphics neophyte.
Even a textbook rendering effect, however, entails some geometric thinking that in turn creates the possibility for its own special category of bug.
Let&amp;rsquo;s follow our nose and run into some geometry bugs.&lt;/p&gt;

&lt;p&gt;Phong lighting has three parts:
some uniform &lt;em&gt;ambient&lt;/em&gt; lighting,
a &lt;em&gt;diffuse&lt;/em&gt; component that looks the same from any angle,
and a &lt;em&gt;specular&lt;/em&gt; component that adds shiny highlights for direct reflections.
To make things even easier, let&amp;rsquo;s try to implement just the diffuse component.
It&amp;rsquo;s supposed to look like this bunny here.&lt;/p&gt;

&lt;p&gt;The idea is to compute the &lt;a href=&quot;https://en.wikipedia.org/wiki/Lambertian_reflectance&quot;&gt;Lambertian reflectance&lt;/a&gt;.
At every point on the surface of the object, it&amp;rsquo;s the dot product between the surface&amp;rsquo;s normal vector and the direction from that point to the light source:&lt;/p&gt;

&lt;div class=&quot;kdmath&quot;&gt;$$
\mathit{lambertian} = \mathsf{max}(\mathit{lightDir}\cdot\mathit{fragNorm}, 0)
$$&lt;/div&gt;

&lt;p&gt;That $\mathsf{max}$ helps us ignore light coming from behind the rabbit&amp;rsquo;s surface.
We&amp;rsquo;re implementing this in a &lt;a href=&quot;https://www.khronos.org/opengl/wiki/Fragment_Shader&quot;&gt;fragment shader&lt;/a&gt;, so that
$\mathit{fragNorm}$ is the normal vector for a given point on the triangle that we&amp;rsquo;re rendering.
We can get $\mathit{lightDir}$ by normalizing the difference between the current fragment position and some absolute position of the light source:&lt;/p&gt;

&lt;div class=&quot;kdmath&quot;&gt;$$
\mathit{lightDir} = \mathsf{normalize}(\mathit{lightPos} - \mathit{fragPos})
$$&lt;/div&gt;

&lt;p&gt;Here&amp;rsquo;s a direct translation of all that into &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Games/Techniques/3D_on_the_web/GLSL_Shaders&quot;&gt;GLSL&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-glsl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;vec3&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lightDir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uLightPos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lambertian&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lightDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vNormal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;gl_FragColor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;vec4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lambertian&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uDiffColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We&amp;rsquo;ve set this fragment shader up so it gets its inputs, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uLightPos&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vPosition&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vNormal&lt;/code&gt;, from the CPU.
To finish it off, we multiply the Lambertian reflectance magnitude by an RGB color we&amp;rsquo;ve chosen for our light and, finally, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vec4(..., 1.0)&lt;/code&gt; to set the &lt;a href=&quot;https://en.wikipedia.org/wiki/Alpha_compositing&quot;&gt;alpha channel&lt;/a&gt; to 1.&lt;/p&gt;

&lt;figure style=&quot;width: 350px&quot;&gt;
  &lt;canvas width=&quot;350&quot; height=&quot;350&quot; id=&quot;diffuse-naive&quot;&gt;&lt;/canvas&gt;
  &lt;figcaption&gt;What happens if you try to implement that diffuse lighting by translating the math 1-1 into GLSL.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;This shader is incorrect.
The bunny looks like this: the shading seems mostly right, but the invisible light seems to be rotating along with the model instead of staying put.&lt;/p&gt;

&lt;p&gt;The math is right, but the code has a &lt;em&gt;geometry bug&lt;/em&gt;.
The problem is that, when you translate geometric math into rendering code, you have to represent the abstract vectors as concrete tuples of floating-point numbers.
When the math&amp;rsquo;s $\mathit{fragPos}$ becomes the shader&amp;rsquo;s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vPosition&lt;/code&gt;, we need to decide on its &lt;em&gt;reference frame:&lt;/em&gt;
will it be local coordinates relative to the bunny itself,
relative to the camera,
or using some absolute coordinates for the whole scene?
We also need to pick a coordinate system, like
ordinary Cartesian coordinates, polar coordinates, or the more graphics-specific system of &lt;a href=&quot;https://en.wikipedia.org/wiki/Homogeneous_coordinates&quot;&gt;homogeneous coordinates&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The real problem here is that none of these choices show up in the type system.
In GLSL, all our vectors are &lt;a href=&quot;https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vec3&lt;/code&gt;s&lt;/a&gt;.
There are several different reference frames at work here, but none of them show up in the programming language.
GLSL is perfectly happy to add and subtract vectors that use completely different representations, yielding meaningless results.&lt;/p&gt;

&lt;p&gt;This post will walk through fixing the implementation of this math.
It&amp;rsquo;s written for people who don&amp;rsquo;t know much about graphics or geometry, i.e., people like me.
The goal here is to convince you that this kind of programming needs a type system.&lt;/p&gt;

&lt;h2 id=&quot;managing-reference-frames&quot;&gt;Managing Reference Frames&lt;/h2&gt;

&lt;figure style=&quot;max-width: 300px&quot;&gt;
  &lt;img src=&quot;/~asampson/media/gator/spaces.svg&quot; class=&quot;bonw&quot; alt=&quot;A 2D artist&apos;s interpretation of the model, world, and view reference frames in graphics rendering.&quot; /&gt;
  &lt;figcaption&gt;Rendering a whole scene usually involves several &lt;em&gt;model&lt;/em&gt; reference frames, a fixed &lt;em&gt;world&lt;/em&gt; frame, and a &lt;em&gt;view&lt;/em&gt; frame for the camera&apos;s perspective.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The first thing that&amp;rsquo;s wrong is that our vectors are in different reference frames.
It&amp;rsquo;s useful to imagine a single, fixed &lt;em&gt;world&lt;/em&gt; frame that contains the entire scene:
here, both our bunny and our light source.
The geometric data describing individual objects arrives each object&amp;rsquo;s own individual &lt;em&gt;model&lt;/em&gt; reference frame.
When you download the &lt;a href=&quot;https://en.wikipedia.org/wiki/Wavefront_.obj_file&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.obj&lt;/code&gt; file&lt;/a&gt; for the &lt;a href=&quot;https://faculty.cc.gatech.edu/~turk/bunny/bunny.html&quot;&gt;Stanford bunny&lt;/a&gt;, it doesn&amp;rsquo;t know about your renderer&amp;rsquo;s world frame:
the vertex positions and surface normals have to come represented in an intrinsic bunny-specific space.&lt;/p&gt;

&lt;p&gt;In our little shader, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vPosition&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vNormal&lt;/code&gt; are vectors in the bunny&amp;rsquo;s model frame while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uLightPos&lt;/code&gt; is a point in world space.
So the subtraction &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uLightPos - vPosition&lt;/code&gt; doesn&amp;rsquo;t yield a geometrically meaningful vector,
and we should probably be careful about that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dot(lightDir, vNormal)&lt;/code&gt; too.&lt;/p&gt;

&lt;p&gt;Renderers use transformation matrices to convert between reference frames.
Let&amp;rsquo;s suppose that we have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uModel&lt;/code&gt; matrix that transforms bunny space to world space.
Then the GLSL we need should look something like this:&lt;/p&gt;

&lt;div class=&quot;language-glsl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;vec3&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lightDir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uLightPos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uModel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lambertian&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lightDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uModel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vNormal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With all our vectors converted to the common (world) reference frame, these operations should mean something!&lt;/p&gt;

&lt;p&gt;However, in realistic renderers, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uModel&lt;/code&gt; transformation will actually be a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mat4&lt;/code&gt;, not a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mat3&lt;/code&gt;.
The reason has to do with affine transformations and coordinate systems&amp;mdash;we&amp;rsquo;ll address that next.&lt;/p&gt;

&lt;h2 id=&quot;converting-coordinate-systems&quot;&gt;Converting Coordinate Systems&lt;/h2&gt;

&lt;p&gt;In 3-dimensional space, a square 3&amp;times;3 matrix is a nice way to represent &lt;em&gt;linear&lt;/em&gt; transformations:
rotations, scaling, shearing, all that.
But renderers typically also want to do translation, which requires generalizing to &lt;em&gt;affine&lt;/em&gt; transformations.
You can&amp;rsquo;t represent those in a 3&amp;times;3 matrix, so what can we do?&lt;/p&gt;

&lt;p&gt;The usual way is to use &lt;a href=&quot;https://en.wikipedia.org/wiki/Homogeneous_coordinates&quot;&gt;homogeneous coordinates&lt;/a&gt;.
Where ordinary Cartesian coordinates represent a point in 3-space using a GLSL &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vec3&lt;/code&gt;, homogeneous coordinates use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vec4&lt;/code&gt;.
The extra coordinate is a scaling factor, so the 4-tuple $[x, y, z, w]$ represents the point at $[x/w, y/w, z/w]$.
Transformation matrices are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mat4&lt;/code&gt;s that can represent the full range of affine transformations in 3-dimensional space.&lt;/p&gt;

&lt;p&gt;In our example (and in any typical renderer setup),
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vPosition&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vNormal&lt;/code&gt; come to us in Cartesian coordinates (i.e., GLSL &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vec3&lt;/code&gt;s)
and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uModel&lt;/code&gt; transformation matrix comes in homogeneous coordinates (i.e., a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mat4&lt;/code&gt;).
To fix our transformation expression &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uModel * vPosition&lt;/code&gt;, we&amp;rsquo;ll need something like this:&lt;/p&gt;

&lt;div class=&quot;language-glsl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;vec4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posWorldHom&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uModel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;vec4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;vec3&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posWorldCart&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;vec3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posWorldHom&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posWorldHom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure style=&quot;width: 350px&quot;&gt;
  &lt;canvas width=&quot;350&quot; height=&quot;350&quot; id=&quot;diffuse-alltrans&quot;&gt;&lt;/canvas&gt;
  &lt;figcaption&gt;The same shader after applying the same coordinate-system juggling to both input vectors. (Particularly mysterious in dark mode.)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;We convert &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vPosition&lt;/code&gt; to homogeneous coordinates by tacking on a scaling factor $w=1$,
transform with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uModel&lt;/code&gt;,
and then convert back to Cartesian coordinates by dividing by $w$ again.&lt;/p&gt;

&lt;p&gt;With this, we finally have our vertex position in world reference frame.
Let&amp;rsquo;s try repeating exactly the same process with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vNormal&lt;/code&gt;, the other model-space vector involved in our computation.
Sadly, something&amp;rsquo;s still very wrong&amp;mdash;in fact, the bunny somehow looks every worse than it did before.
&lt;span class=&quot;dark-only&quot;&gt;
If you&amp;rsquo;re viewing this page in dark mode, it may not be visible at all.
&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;The problem now is that, while the code we wrote to juggle homogeneous coordinates for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vPosition&lt;/code&gt; is correct,
the same treatment doesn&amp;rsquo;t work for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vNormal&lt;/code&gt;.
The reason has to do with the different kinds of geometric objects that these variables represent.&lt;/p&gt;

&lt;h2 id=&quot;kinds-of-geometric-objects&quot;&gt;Kinds of Geometric Objects&lt;/h2&gt;

&lt;p&gt;Yet again, we&amp;rsquo;ve been bitten by a geometric concept that remains invisible in the GLSL code.
There&amp;rsquo;s an important difference between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vPosition&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vNormal&lt;/code&gt;: both are 3-dimensional Cartesian vectors,
but one represents a &lt;em&gt;position&lt;/em&gt; while the other is just a &lt;em&gt;direction&lt;/em&gt;.
These might seem like the same thing: what is a direction other than a unit-magnitude position?&lt;/p&gt;

&lt;figure style=&quot;max-width: 350px&quot;&gt;
  &lt;img src=&quot;/~asampson/media/gator/translation.svg&quot; class=&quot;bonw&quot; alt=&quot;Two reference frames, one a translation of the other. There is a point and a direction in each. The point gets translated; the direction stays the same.&quot; /&gt;
  &lt;figcaption&gt;We want reference-frame translations to affect positions but not directions.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The distinction matters when we convert between reference frames.
Remember that our homogeneous-coordinates transformation matrix &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uModel&lt;/code&gt; can encode a translation (in addition to rotation and scaling and all that).
We absolutely want to translate positions when going from the model frame to the world frame,
but &lt;em&gt;we do not want to translate directions&lt;/em&gt;.
When you shift a reference frame over some distance, all the points should move, but directions stay pointing in the same direction&amp;mdash;they should not get shifted over by the same amount.&lt;/p&gt;

&lt;p&gt;Homogeneous coordinates have a trick to deal with positions.
If you set their scaling factor, $w$, to zero, then transformation matrices will treat them correctly: they&amp;rsquo;ll apply all the linear transformations and none of the (affine) translation.
Then, to convert back to Cartesian coordinates, we have to ignore $w$ to avoid dividing by zero.
Here&amp;rsquo;s how it looks in GLSL:&lt;/p&gt;

&lt;div class=&quot;language-glsl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;vec3&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normWorld&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;vec3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uModel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;vec4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vNormal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The key thing to notice is that Cartesian/homogeneous conversion needs to work differently for positions and directions.
So programmers have to keep track of the different kinds of geometric objects they&amp;rsquo;re dealing with in their heads.&lt;/p&gt;

&lt;h2 id=&quot;the-correct-shader&quot;&gt;The Correct Shader&lt;/h2&gt;

&lt;p&gt;Here&amp;rsquo;s a complete version of the correct shader:&lt;/p&gt;

&lt;div class=&quot;language-glsl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// lightDir = normalize(lightPos - fragPos)&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;vec4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posWorldHom&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uModel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;vec4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;vec3&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posWorldCart&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;vec3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posWorldHom&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posWorldHom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;vec3&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lightDir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uLightPos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posWorldCart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// lambertian = max(dot(lightDir, fragNorm), 0)&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;vec3&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normWorld&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;vec3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uModel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;vec4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vNormal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lambertian&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lightDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normWorld&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;gl_FragColor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;vec4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lambertian&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uDiffColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This shader produces the bunny at the top of this post.&lt;/p&gt;

&lt;h2 id=&quot;geometry-types&quot;&gt;Geometry Types&lt;/h2&gt;

&lt;p&gt;At &lt;a href=&quot;https://2020.splashcon.org/track/splash-2020-oopsla&quot;&gt;OOPSLA 2020&lt;/a&gt;, &lt;a href=&quot;https://www.cs.cornell.edu/~dgeisler/&quot;&gt;Prof. Dietrich Geisler&lt;/a&gt; published &lt;a href=&quot;https://dl.acm.org/doi/10.1145/3428241&quot;&gt;a paper&lt;/a&gt; about geometry bugs
and a type system that can catch them.
The idea hasn&amp;rsquo;t exactly taken over the world, and I wish it would.
The paper&amp;rsquo;s core insight is that, to do a good job with this kind of type system, you need your types to encode three pieces of information:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;the reference frame (like model, world, or view space)&lt;/li&gt;
  &lt;li&gt;the coordinate scheme (like Cartesian, homogeneous, or polar coordinates)&lt;/li&gt;
  &lt;li&gt;the geometric object (like positions and directions)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Dietrich&amp;rsquo;s language, these types are spelled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scheme&amp;lt;frame&amp;gt;.object&lt;/code&gt;.
Dietrich implemented these types in a language called &lt;a href=&quot;https://github.com/cucapra/gator&quot;&gt;Gator&lt;/a&gt; with help from &lt;a href=&quot;https://www.cis.upenn.edu/~euisuny/&quot;&gt;Irene Yoon&lt;/a&gt;, &lt;a href=&quot;https://aditink.github.io&quot;&gt;Aditi Kabra&lt;/a&gt;, &lt;a href=&quot;https://horace.io&quot;&gt;Horace He&lt;/a&gt;, and Yinnon Sanders.
With a few helper functions, you can get Gator to help you catch all the geometric pitfalls we saw in this post.
Here&amp;rsquo;s a version of our shader in Gator:&lt;/p&gt;

&lt;div class=&quot;language-glsl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cart3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;world&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posWorld&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hom_reduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uModel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;homify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cart3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;world&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lightDir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uLightPos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posWorld&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;cart3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;world&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normalWorld&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hom_reduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uModel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;homify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vNormal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;scalar&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lambertian&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lightDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normalWorld&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The standard library comes with overloaded &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;homify&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hom_reduce&lt;/code&gt; functions that do the right thing when converting a given geometric object between coordinate systems.
Gator also distinguishes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vector&lt;/code&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;direction&lt;/code&gt;, which is a subtype that is guaranteed to have unit magnitude.
If you forget a transformation or conversion, Gator will report a type error.&lt;/p&gt;

&lt;p&gt;With geometry baked into the type system, we can also go one step farther and automatically generate the transformation code.
Gator supports an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;in&lt;/code&gt; expression that searches for a transformation from one reference frame or coordinate system to another.
For example, if we mark &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uModel&lt;/code&gt; as the &lt;em&gt;canonical&lt;/em&gt; transformation from model space to world space, then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;in world&lt;/code&gt; suffices to manage both the reference-frame change and the detour through homogeneous coordinates:&lt;/p&gt;

&lt;div class=&quot;language-glsl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lightDir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uLightPos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vPosition&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;world&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;scalar&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lambertian&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lightDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vNormal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;world&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We at last have a version of the shader that looks kind of like the math.&lt;/p&gt;

&lt;p&gt;I know the world of &lt;a href=&quot;https://en.wikipedia.org/wiki/Shading_language&quot;&gt;shading languages&lt;/a&gt; is not exactly a hotbed of rapid innovation these days.
Even so, I think geometry types are a pretty good idea and I hope that some future generation of rendering systems borrows an idea or two from Gator.&lt;/p&gt;

</content>
    <summary type="html">&lt;p&gt;A special kind of bug exists in code that has to deal with geometric concepts like positions, directions, coordinate systems, and all that.
Long ago, in &lt;a href=&quot;https://dl.acm.org/doi/10.1145/3428241&quot;&gt;an OOPSLA 2020 paper&lt;/a&gt;, we defined &lt;em&gt;geometry bugs&lt;/em&gt; and designed a &lt;a href=&quot;https://github.com/cucapra/gator&quot;&gt;type system&lt;/a&gt; to catch them.
This post demonstrates the idea through some buggy &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Games/Techniques/3D_on_the_web/GLSL_Shaders&quot;&gt;GLSL&lt;/a&gt; shaders.&lt;/p&gt;

</summary>
  </entry>
  
  <entry>
    
    <id>https://www.cs.cornell.edu/~asampson/blog/bril.html</id>
    
    <title type="html">Bril: An Intermediate Language for Teaching Compilers</title>
    <updated>2024-07-26T00:00:00+00:00</updated>
    <link rel="alternate" href="https://www.cs.cornell.edu/~asampson/blog/bril.html"/>
    <content type="html">&lt;p&gt;When I started a new &lt;a href=&quot;https://www.cs.cornell.edu/courses/cs6120/2023fa/&quot;&gt;PhD-level compilers course&lt;/a&gt; a few years ago,
I thought it was important to use a &amp;ldquo;hands-on&amp;rdquo; structure.
There is a big difference between understanding an algorithm on a whiteboard and implementing it, inevitably running into bugs when your implementation encounters real programs.
At the same time, I wanted students to get started quickly, without learning the overwhelming APIs that come with industrial-strength compilers.&lt;/p&gt;

&lt;p&gt;I created &lt;a href=&quot;https://capra.cs.cornell.edu/bril/&quot;&gt;Bril&lt;/a&gt;, the Big Red Intermediate Language, to support the class&amp;rsquo;s implementation projects.
Bril isn&amp;rsquo;t very interesting from a compiler engineering perspective, but
I think it&amp;rsquo;s pretty good for the specific use case of teaching compilers classes.
Here&amp;rsquo;s a factorial program:&lt;/p&gt;

&lt;div class=&quot;language-bril highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;@main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;@fact&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;@fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cond&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;le&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;br&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cond&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;.then&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;.else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;.then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;.else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;decr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;@fact&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;decr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;prod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mul&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;prod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Bril is the only compiler IL I know of that is specifically designed for education.
Focusing on teaching means that Bril prioritizes these goals:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It is fast to get started working with the IL.&lt;/li&gt;
  &lt;li&gt;It is easy to mix and match components that work with the IL, including things that fellow students write.&lt;/li&gt;
  &lt;li&gt;The semantics are simple, without too many distractions.&lt;/li&gt;
  &lt;li&gt;The syntax is ruthlessly regular.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bril is different from other ILs because it prioritizes those goals above other, more typical ones:
code size, compiler speed, and performance of the generated code.&lt;/p&gt;

&lt;p&gt;Aside from that inversion of priorities, Bril looks a lot like any other modern compiler IL.
It&amp;rsquo;s an instruction-based, assembly-like, typed, &lt;a href=&quot;https://en.wikipedia.org/wiki/A-normal_form&quot;&gt;ANF&lt;/a&gt; language.
There&amp;rsquo;s a quote from &lt;a href=&quot;https://en.wikipedia.org/wiki/Why_the_lucky_stiff&quot;&gt;why the lucky stiff&lt;/a&gt; where he introduces &lt;a href=&quot;https://camping.github.io/camping.io/&quot;&gt;Camping&lt;/a&gt;, the original web microframework, as &amp;ldquo;a little white blood cell in the vein of Rails.&amp;rdquo;
If LLVM is an entire circulatory system, Bril is a single blood cell.&lt;/p&gt;

&lt;h2 id=&quot;bril-is-json&quot;&gt;Bril is JSON&lt;/h2&gt;

&lt;p&gt;Bril programs are JSON documents.
Here&amp;rsquo;s how students get started working with Bril code using Python:&lt;/p&gt;

&lt;div class=&quot;language-py highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;prog&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I&amp;rsquo;m obviously being a little silly here.
But seriously, the JSON-as-syntax idea is in service of the &lt;em&gt;fast to get started&lt;/em&gt; and &lt;em&gt;easy to mix and match components&lt;/em&gt; goals above.
I wanted Bril to do these things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Let students use any programming language they want.&lt;/strong&gt;
I wanted my compilers course to be accessible to lots of PhD students, including people with only tangential interest in compilers.
Letting them use the languages they&amp;rsquo;re comfortable with is a great way to avoid any ramp-up phase where students must learn some specific &amp;ldquo;realistic&amp;rdquo; compiler implementation language if they don&amp;rsquo;t already know it.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;No framework is required to get started.&lt;/strong&gt;
For the first offering of CS 6120, no libraries existed, and I needed to run the course somehow.
Beyond that practical matter, this constraint is valuable as a complexity limiter:
students can get started with simple stuff without learning any APIs.
These days, Bril does come with libraries that are great for avoiding JSON-handling frustrations when you scale up:
for &lt;a href=&quot;https://github.com/sampsyo/bril/tree/main/bril-rs&quot;&gt;Rust&lt;/a&gt;, &lt;a href=&quot;https://github.com/sampsyo/bril/tree/main/bril-ocaml&quot;&gt;OCaml&lt;/a&gt;, &lt;a href=&quot;https://github.com/sampsyo/bril/tree/main/bril-swift&quot;&gt;Swift&lt;/a&gt;, and &lt;a href=&quot;https://github.com/sampsyo/bril/tree/main/bril-ts&quot;&gt;TypeScript&lt;/a&gt;.
But the fact that they&amp;rsquo;re not really &lt;em&gt;required&lt;/em&gt; keeps the onramps gentle.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Compose small pieces with Unix pipelines.&lt;/strong&gt;
You can wire up Bril workflows with shell pipelines, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat code.json | my_opt | my_friends_opt | brilck&lt;/code&gt;.
I want students in CS 6120 to freely share code with each other and to borrow bits of functionality I wrote.
For a PhD-level class, this trust-based &amp;ldquo;open-source&amp;rdquo; course setup makes way more sense to me than a typical undergrad-style class where collaboration is forbidden.
Piping JSON from one tool to the next is a great vehicle for sharing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, JSON is the canonical form for Bril code.
Here&amp;rsquo;s a complete Bril program:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;functions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;main&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;instrs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;op&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;const&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;int&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;dest&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;v0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;op&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;const&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;int&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;dest&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;v1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;op&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;add&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;int&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;dest&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;v2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;v0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;v1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;op&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;print&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;v2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This program has one function, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt;, with no arguments and 4 instructions:
two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const&lt;/code&gt; instructions, an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add&lt;/code&gt;, and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Even though Bril is JSON, it also has a text form.
I will, however, die on the following hill:
&lt;em&gt;the text form is only a second-class convenience&lt;/em&gt;, and it is not the language itself.
The text syntax exists solely to cater to our foibles as humans for whom reading JSON directly is annoying.
Bril itself is the JSON format you see above.
But as a concession to our foibles, among Bril&amp;rsquo;s many tools are a &lt;a href=&quot;https://github.com/sampsyo/bril/blob/main/bril-txt/briltxt.py&quot;&gt;parser and pretty-printer&lt;/a&gt;.
Here&amp;rsquo;s the text form of the program above:&lt;/p&gt;

&lt;div class=&quot;language-bril highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;@main&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As a consequence, working with Bril means typing commands like this a lot:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ bril2json &amp;lt; program.bril | do_something | bril2txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It can get tedious to constantly convert to and from JSON,
and it&amp;rsquo;s wasteful to serialize and deserialize programs at each stage in a long pipeline.
But the trade-off is that the Bril ecosystem comprises a large number of small pieces, loosely joined and infinitely remixable on the command line.&lt;/p&gt;

&lt;h2 id=&quot;language-design-good-bad-and-ugly&quot;&gt;Language Design: Good, Bad, and Ugly&lt;/h2&gt;

&lt;p&gt;There are a few design decisions in the language itself that reflect Bril&amp;rsquo;s education-over-practicality priorities.
For instance, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print&lt;/code&gt; is a &lt;a href=&quot;https://capra.cs.cornell.edu/bril/lang/core.html&quot;&gt;core opcode&lt;/a&gt; in Bril; I don&amp;rsquo;t think this would be a good idea in most compilers, but it makes it easy to write small examples.&lt;/p&gt;

&lt;p&gt;Another quirk is that Bril is &lt;em&gt;extremely&lt;/em&gt; &lt;a href=&quot;https://en.wikipedia.org/wiki/A-normal_form&quot;&gt;A-normal form&lt;/a&gt;, to the point that constants always have to go in their own instructions and get their own names.
To increment an integer, for example, you can&amp;rsquo;t do this:&lt;/p&gt;

&lt;div class=&quot;language-bril highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;incr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead, Bril code is full of one-off constant variables, like this:&lt;/p&gt;

&lt;div class=&quot;language-bril highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;incr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This more-ANF-than-ANF approach to constants is verbose to the point of silliness.
But it simplifies the way you write some basic IL traversals because you don&amp;rsquo;t have to worry about whether operands come from variables or constants.
For many use cases, you get to handle constants the same way you do any other instruction.
For teaching, I think the regularity is worth the silliness.&lt;/p&gt;

&lt;p&gt;Bril is extensible, in a loosey-goosey way.
The string-heavy JSON syntax means it&amp;rsquo;s trivial to add new opcodes and data types.
Beyond the &lt;a href=&quot;https://capra.cs.cornell.edu/bril/lang/core.html&quot;&gt;core language&lt;/a&gt;, there are &amp;ldquo;official&amp;rdquo; extensions for &lt;a href=&quot;https://capra.cs.cornell.edu/bril/lang/memory.html&quot;&gt;manually managed memory&lt;/a&gt;, &lt;a href=&quot;https://capra.cs.cornell.edu/bril/lang/float.html&quot;&gt;floating-point numbers&lt;/a&gt;, a funky form of &lt;a href=&quot;https://capra.cs.cornell.edu/bril/lang/spec.html&quot;&gt;speculation&lt;/a&gt; I use for teaching JIT principles, &lt;a href=&quot;https://capra.cs.cornell.edu/bril/lang/import.html&quot;&gt;module imports&lt;/a&gt;, and &lt;a href=&quot;https://capra.cs.cornell.edu/bril/lang/char.html&quot;&gt;characters&lt;/a&gt;.
While a &lt;em&gt;laissez faire&lt;/em&gt; approach to extensions has worked so far, it&amp;rsquo;s also a mess:
there&amp;rsquo;s no systematic way to tell which extensions a given program uses or which language features a given tool supports.
&lt;a href=&quot;https://github.com/sampsyo/bril/issues/38&quot;&gt;A more explicit approach to extensibility&lt;/a&gt; would make the growing ecosystem easier to manage.&lt;/p&gt;

&lt;p&gt;Finally, Bril does not require SSA.
There is &lt;a href=&quot;https://capra.cs.cornell.edu/bril/lang/ssa.html&quot;&gt;an SSA form&lt;/a&gt; that includes a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;phi&lt;/code&gt; instruction, but the language itself has mutable variables.
I wouldn&amp;rsquo;t recommend this strategy for any other IL, but it&amp;rsquo;s helpful for teaching for three big reasons:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;I want students to feel the pain of working with non-SSA programs before the course introduces SSA. This frustration can help motivate why SSA is the modern consensus.&lt;/li&gt;
  &lt;li&gt;The course includes a task where students &lt;a href=&quot;https://www.cs.cornell.edu/courses/cs6120/2023fa/lesson/6/#tasks&quot;&gt;implement into-SSA and out-of-SSA transformations&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;It&amp;rsquo;s really easy to generate Bril code from frontend languages that have mutable variables. The alternative would be LLVM&amp;rsquo;s &lt;a href=&quot;https://llvm.org/doxygen/Mem2Reg_8cpp_source.html&quot;&gt;mem2reg&lt;/a&gt;/&amp;rdquo;just put all the frontend variables in memory&amp;rdquo; trick, but Bril avoids building memory into the core language for simplicity.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Unfortunately, this aftermarket SSA retrofit has been a huge headache.
It has caused &lt;a href=&quot;https://github.com/sampsyo/bril/issues/108&quot;&gt;persistent problems with undefinedness&lt;/a&gt; and &lt;a href=&quot;https://github.com/sampsyo/bril/issues/330&quot;&gt;classic correctness problems when translating out of SSA&lt;/a&gt;.
I think my original design is fundamentally flawed;
it was a mistake to treat &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;phi&lt;/code&gt; semantically as &amp;ldquo;just another instruction&amp;rdquo; instead of a more invasive change to the language.
Bril&amp;rsquo;s SSA form needs a full rework, probably including an actual language extension along the lines of &lt;a href=&quot;https://mlir.llvm.org/docs/Rationale/Rationale/#block-arguments-vs-phi-nodes&quot;&gt;MLIR&amp;rsquo;s basic block arguments&lt;/a&gt;.
It has been an interesting lesson for me that SSA comes with subtle design implications that are difficult to retrofit onto an existing mutation-oriented IL.&lt;/p&gt;

&lt;h2 id=&quot;the-bril-ecosystem&quot;&gt;The Bril Ecosystem&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/~asampson/media/bril/ecosystem.svg&quot; class=&quot;img-responsive bonw&quot; style=&quot;max-width: 450px;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I cobbled together the first version of Bril in a hurry in the weeks before the fall 2019 semester began.
Since then, via the &amp;ldquo;open-source class&amp;rdquo; nature of &lt;a href=&quot;https://www.cs.cornell.edu/courses/cs6120/2023fa/&quot;&gt;CS 6120&lt;/a&gt;, students have contributed a host of tools for working with the language.
The diagram above shows a sampling of what is in &lt;a href=&quot;https://github.com/sampsyo/bril&quot;&gt;the monorepo&lt;/a&gt;;
empty boxes are things I made and shaded boxes are things students contributed.
One &lt;a href=&quot;https://agentcooper.io&quot;&gt;satisfied CS 6120 customer&lt;/a&gt;
built a snazzy &lt;a href=&quot;https://agentcooper.github.io/bril-playground/&quot;&gt;web playground&lt;/a&gt; that I find super impressive.
You can find many more random tools by &lt;a href=&quot;https://github.com/search?q=bril+compiler&amp;amp;type=repositories&quot;&gt;searching on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Most of the language extensions I mentioned were contributed by CS 6120 students.
In the run-up to the first semester, I was low on time and left memory, function calls, and floating-point numbers as &amp;ldquo;exercises for the reader.&amp;rdquo;
You can read 2019 blog posts &lt;a href=&quot;https://www.cs.cornell.edu/courses/cs6120/2019fa/blog/manually-managed-memory/&quot;&gt;by Drew Zagieboylo &amp;amp; Ryan Doenges about the memory extension&lt;/a&gt;,
&lt;a href=&quot;https://www.cs.cornell.edu/courses/cs6120/2019fa/blog/function-calls/&quot;&gt;by Alexa VanHattum &amp;amp; Gregory Yauney about designing function calls&lt;/a&gt;,
and &lt;a href=&quot;https://www.cs.cornell.edu/courses/cs6120/2019fa/blog/floats-static-arrays/&quot;&gt;by Dietrich Geisler about floats&lt;/a&gt;.
Laziness can pay off.&lt;/p&gt;

&lt;p&gt;Please &lt;a href=&quot;https://discuss.systems/@adrian&quot;&gt;get in touch&lt;/a&gt; if you&amp;rsquo;re using Bril for something unconventional!
I love learning about the weird places it has gone.&lt;/p&gt;

</content>
    <summary type="html">&lt;p&gt;I created a new intermediate language, called &lt;a href=&quot;https://capra.cs.cornell.edu/bril/&quot;&gt;Bril&lt;/a&gt;, for teaching my funky open-source, hands-on compilers course.
Because it&amp;rsquo;s for education, Bril prioritizes simplicity and regularity over more typical compiler priorities like performance and concision.
This is an overview of Bril&amp;rsquo;s design, its quirks, and the ecosystem that has grown up around it since 2019.&lt;/p&gt;

</summary>
  </entry>
  
  <entry>
    
    <id>https://www.cs.cornell.edu/~asampson/blog/autoreduction.html</id>
    
    <title type="html">Automated Test-Case Reduction</title>
    <updated>2024-07-15T00:00:00+00:00</updated>
    <link rel="alternate" href="https://www.cs.cornell.edu/~asampson/blog/autoreduction.html"/>
    <content type="html">&lt;aside&gt;
This post is the second in a series on &lt;em&gt;research skills&lt;/em&gt;.
The plan is to demonstrate techniques that &amp;ldquo;everyone knows&amp;rdquo; because everyone, in fact, does not already know them.
&lt;/aside&gt;

&lt;p&gt;&lt;a href=&quot;/~asampson/blog/reduction.html&quot;&gt;Last time&lt;/a&gt;, we saw how deleting stuff from a test case can be an easy and fun route to the root cause of a bug.
It&amp;rsquo;s less easy and less fun when the test cases get big.
The inner loop of test-case reduction can get old quickly:
delete stuff, run the special command, check the output to decide whether to backtrack or proceed.
It&amp;rsquo;s rote, mechanical, and annoyingly error prone.&lt;/p&gt;

&lt;p&gt;Let&amp;rsquo;s make the computer do it instead.
&lt;em&gt;Automated test-case reducers&lt;/em&gt; in the &lt;a href=&quot;https://github.com/csmith-project/creduce&quot;&gt;C-Reduce&lt;/a&gt; mold follow essentially the same &amp;ldquo;algorithm&amp;rdquo; we saw last time.
They obviously don&amp;rsquo;t know your bug like you do, so they can&amp;rsquo;t apply the intuition you might bring to deciding which things to delete when.
In return, automation can blindly try stuff much faster than a human can, potentially even in parallel.
So the trade-off is often worthwhile.&lt;/p&gt;

&lt;h2 id=&quot;automating-the-reduction-process&quot;&gt;Automating the Reduction Process&lt;/h2&gt;

&lt;p&gt;In the &lt;a href=&quot;/~asampson/blog/reduction.html&quot;&gt;manual test-case reduction &amp;ldquo;algorithm,&amp;rdquo;&lt;/a&gt; there are really only three parts that entailed any human judgment:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Picking a part of the test case to delete.&lt;/li&gt;
  &lt;li&gt;Looking at the command&amp;rsquo;s output to decide whether we need to backtrack:
that is, detecting when a given deletion either removed the bug-triggering code or broke the program entirely.&lt;/li&gt;
  &lt;li&gt;Deciding when to give up: when we probably can&amp;rsquo;t reduce any farther without ruining the test case.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Everything else&amp;mdash;running the command after every edit, hitting &amp;ldquo;undo&amp;rdquo; after we decide to backtrack&amp;mdash;was pretty clearly mechanical.
Automated reducers take control of #1 and #3.
Picking the code to delete (#1) is just guesswork anyway; because we rely on step #2 to detect cases where a deletion went awry, it suffices to guess wildly using a pile of heuristics that have &lt;em&gt;some&lt;/em&gt; probability of finding a useful deletion.
To decide when to stop (#3), reducers detect a fixed point:
they give up when the heuristics fail to find any more code they can safely delete.&lt;/p&gt;

&lt;p&gt;That leaves us with #2: deciding whether a given version of the test case still works to reproduce the bug you&amp;rsquo;re interested in.
Test-case reducers call this the &lt;em&gt;interestingness test&lt;/em&gt; (in the &lt;a href=&quot;https://github.com/csmith-project/creduce&quot;&gt;C-Reduce&lt;/a&gt; tradition), and they typically want you to write it down as a shell script.
To use a reducer, then, you usually just need two ingredients:
the original test case you want to reduce and the interestingness test script.&lt;/p&gt;

&lt;h2 id=&quot;writing-an-interestingness-test&quot;&gt;Writing an Interestingness Test&lt;/h2&gt;

&lt;p&gt;This post will demonstrate how to use the excellent &lt;a href=&quot;https://github.com/DRMacIver/shrinkray&quot;&gt;Shrinkray&lt;/a&gt; reducer to debug &lt;a href=&quot;/~asampson/blog/reduction.html&quot;&gt;the same interpreter bug as last time&lt;/a&gt;.
Shrinkray is language-neutral&amp;ndash;and, in any case, no reducer knows about &lt;a href=&quot;https://capra.cs.cornell.edu/bril/&quot;&gt;Bril&lt;/a&gt;.
I think it&amp;rsquo;s pretty cool that reducers can work well even if they don&amp;rsquo;t know anything about the syntax or semantics of the language they&amp;rsquo;re working with.&lt;/p&gt;

&lt;p&gt;Like any automated reducer, Shrinkray needs an interestingness test:
a little program that checks whether the bug we care about currently exists.
To make one, &amp;ldquo;all we need to do&amp;rdquo; is take the commands we were running manually to check for the bug and put them in a shell script.&lt;/p&gt;

&lt;p&gt;The problem, as we&amp;rsquo;ll discover, is that Shrinkray is a little like &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Sorcerer&apos;s_Apprentice&quot;&gt;the sorcerer&amp;rsquo;s apprentice&lt;/a&gt;.
It will do exactly what we tell it to do, and it will do it with great enthusiasm.
Even tiny gaps in our instructions can lead to surprising results.
So, in my experience, writing a good interestingness test requires (a) a small bag of tricks that may not be intuitive your first time around, and (b) some trial and error.&lt;/p&gt;

&lt;p&gt;I recorded a video of my own trial-end-error process, and I&amp;rsquo;ve also written it out in prose below.
Here&amp;rsquo;s the video:&lt;/p&gt;

&lt;div class=&quot;embed&quot;&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube-nocookie.com/embed/J06BU6Fj6Qs?si=92qpmGfvC56p206E&amp;amp;start=86&amp;amp;end=101&amp;amp;rel=0&quot; allow=&quot;picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Hang on; I&amp;rsquo;m being told that this is the wrong video.
Let&amp;rsquo;s try that again:&lt;/p&gt;

&lt;div class=&quot;embed&quot;&gt;
  &lt;iframe src=&quot;https://cdnapisec.kaltura.com/p/520801/sp/52080100/embedIframeJs/uiconf_id/31230141/partner_id/520801?iframeembed=true&amp;amp;entry_id=1_w0bwzism&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;walkthrough&quot;&gt;Walkthrough&lt;/h2&gt;

&lt;p&gt;In essence, an interestingness test is a script that automates the
commands we ran repetitively (with the &amp;ldquo;up arrow&amp;rdquo; at the shell prompt) during
&lt;a href=&quot;/~asampson/blog/reduction.html&quot;&gt;manual reduction&lt;/a&gt;.
Here&amp;rsquo;s the main command we repeatedly ran then:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ bril2json &amp;lt; problem.bril | cargo run -- -p false false
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I started by just putting this command into a shell script, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;interesting.sh&lt;/code&gt;.
&lt;a href=&quot;https://github.com/DRMacIver/shrinkray&quot;&gt;Shrinkray&lt;/a&gt; wants our script to work from any directory, so I used an absolute
path to the executable:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/bin/sh
bril2json &amp;lt; $1 | /Users/fabian/Documents/cu/bril/brilirs/target/debug/brilirs -p false false
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I also used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$1&lt;/code&gt; for the filename; Shrinkray passes the current version of
the test file as an argument there.
Following the &lt;a href=&quot;https://github.com/csmith-project/creduce&quot;&gt;C-Reduce&lt;/a&gt; tradition, interestingness tests use a
counter-intuitive (but extremely useful) convention:
the script must exit with status 0 when the test is &lt;em&gt;interesting&lt;/em&gt; (exhibits
the bug) and nonzero when it&amp;rsquo;s not (e.g., it&amp;rsquo;s bug-free or ill-formed).
So far, our command crashes with a nonzero status&amp;mdash;specifically, a Rust
panic&amp;mdash;when it &lt;em&gt;is&lt;/em&gt; interesting, which is the opposite of what we want.&lt;/p&gt;

&lt;h3 id=&quot;trick-1-shell-negation&quot;&gt;Trick 1: Shell Negation&lt;/h3&gt;

&lt;p&gt;So here&amp;rsquo;s a trick you can use in interestingness tests in general:
try the
&lt;a href=&quot;https://www.gnu.org/software/bash/manual/html_node/Pipelines.html&quot;&gt;shell&amp;rsquo;s negation operator, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!&lt;/code&gt;,&lt;/a&gt; to invert the sense of the exit status.
Like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/bin/sh
! ( bril2json &amp;lt; $1 | /Users/fabian/Documents/cu/bril/brilirs/target/debug/brilirs -p false false )
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now our tests says the input is interesting (status 0) when the interpreter
&lt;em&gt;does&lt;/em&gt; crash.
Let&amp;rsquo;s try it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ shrinkray interesting.sh problem.bril
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With this script, Shrinkray immediately reduces our file down to nothing.
And it&amp;rsquo;s not wrong: a zero-byte file does elicit an error from our
interpreter!
As usual, the sorcerer&amp;rsquo;s apprentice did exactly what we said, not what
we wanted, and did it with wondrous alacrity.&lt;/p&gt;

&lt;h3 id=&quot;trick-2-the-not-bogus-check-and-set--e&quot;&gt;Trick 2: The &amp;ldquo;Not Bogus&amp;rdquo; Check and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set -e&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;This leads us to our second trick for writing interestingness tests:
before you try to expose the bug, include a &amp;ldquo;not bogus&amp;rdquo; check.
You want to make sure your test is well-formed in some sense:
it parses successfully,
or it doesn&amp;rsquo;t throw an exception,
or whatever else.&lt;/p&gt;

&lt;p&gt;In our case, we&amp;rsquo;re debugging a crashy interpreter, but we also have access to
a &lt;a href=&quot;https://capra.cs.cornell.edu/bril/tools/interp.html&quot;&gt;reference interpreter&lt;/a&gt; that doesn&amp;rsquo;t have the same bug.
So we can add a &amp;ldquo;not bogus&amp;rdquo; check that requires any well-formed test to
succeed in that reference interpreter.
Here&amp;rsquo;s a new script that includes that check:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/bin/sh
set -e
bril2json &amp;lt; problem.bril | brili false false
! ( bril2json &amp;lt; $1 | /Users/fabian/Documents/cu/bril/brilirs/target/debug/brilirs -p false false )
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Check out that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set -e&lt;/code&gt; line, which is super useful for interestingness
scripts and &amp;ldquo;not bogus&amp;rdquo; checks in particular.
&lt;a href=&quot;https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html&quot;&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-e&lt;/code&gt; option&lt;/a&gt; makes a shell script immediately abort with an
error (signalling &amp;ldquo;uninteresting&amp;rdquo;) when any line fails.
In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-e&lt;/code&gt; mode, you&amp;rsquo;re free to write a bunch of commands that should succeed in
the normal case but might fail when the test really goes off the rails:
as soon as any command fails, the script will bail out and indicate that we&amp;rsquo;re
on the wrong path.
So in my script here, we&amp;rsquo;re requiring the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brili&lt;/code&gt; (reference interpreter)
invocation to succeed before we even bother trying to expose the
bug.&lt;/p&gt;

&lt;p&gt;Let&amp;rsquo;s restore our test input from the backup Shrinkray saved for us and try
again:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cp problem.bril.bak problem.bril
$ shrinkray interesting.sh problem.bril
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Shrinkray will again sorcerer&amp;rsquo;s-apprentice its way into a much too small test
case.
Not zero bytes this time, but too small to be useful.
To see what went wrong, we can run our two commands (&amp;ldquo;not bogus&amp;rdquo; and bug
check) manually:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ bril2json &amp;lt; problem.bril | brili false false
$ bril2json &amp;lt; problem.bril | ./target/debug/brilirs -p false false
error: Expected a primitive type like int or bool, found l
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Shrinkray has isolated for us a &lt;em&gt;different error&lt;/em&gt; with the same shape, i.e.,
the reference interpreter accepts the program but the buggy interpreter
rejects it.
This one is not really a bug: it&amp;rsquo;s just a case where the second interpreter is
more strict than the first.
And even if this misalignment is curious, it&amp;rsquo;s not the bug we were looking
for.&lt;/p&gt;

&lt;h3 id=&quot;trick-3-grep-for-your-error-message&quot;&gt;Trick 3: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; for Your Error Message&lt;/h3&gt;

&lt;p&gt;So here&amp;rsquo;s trick number three for writing interestingness tests:
use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; to check for the error message we actually want.
In our case, the program panics with an &amp;ldquo;out of bounds&amp;rdquo; message.
Let&amp;rsquo;s restore from backup and grep for that in both stdout and stderr like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cp problem.bril.bak problem.bril
$ bril2json &amp;lt; problem.bril | ./target/debug/brilirs -p false false 2&amp;gt;&amp;amp;1 | grep &apos;out of bounds&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Conveniently, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; does exactly what you want for an interestingness test:
its exit status is 0 when it finds the string (interesting because this is the
bug we are looking for)
and 1 when it fails to find the string (some other error).
So here&amp;rsquo;s our new script:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/bin/sh
set -e
bril2json &amp;lt; problem.bril | brili false false
bril2json &amp;lt; $1 | /Users/fabian/Documents/cu/bril/brilirs/target/debug/brilirs -p false false 2&amp;gt;&amp;amp;1 | grep &apos;out of bounds&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With this script, Shrinkray does a great job and reduces quite a bit of code,
which is awesome.
One thing it can&amp;rsquo;t reduce, however, is the way the program uses arguments.
It&amp;rsquo;s actually hopeless for Shrinkray to remove the arguments because it would
require changing the interestingness test script to remove those &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false false&lt;/code&gt;
command-line arguments we keep on passing.&lt;/p&gt;

&lt;h3 id=&quot;trick-4-occasional-manual-help&quot;&gt;Trick 4: Occasional Manual Help&lt;/h3&gt;

&lt;p&gt;We can help Shrinkray out a bit by turning those inputs into constants in the code.
We can replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt;&amp;rsquo;s arguments with two new Bril instructions:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;b0: bool = const false;
b1: bool = const false;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Accordingly, we need to change our interestingness test to pass zero arguments
to both interpreter invocations:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/bin/sh
set -e
bril2json &amp;lt; problem.bril | brili
bril2json &amp;lt; $1 | /Users/fabian/Documents/cu/bril/brilirs/target/debug/brilirs -p 2&amp;gt;&amp;amp;1 | grep &apos;out of bounds&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this point, it&amp;rsquo;s probably a good idea to do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./interesting.sh problem.bril&lt;/code&gt;
to manually check that everything&amp;rsquo;s in order.&lt;/p&gt;

&lt;h3 id=&quot;success&quot;&gt;Success!&lt;/h3&gt;

&lt;p&gt;Everything looks good in this case, so we can fire up Shrinkray again.
It gives us this reduced test case:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@main{
    jmp .A;
    .A:ret;
    .A:jmp  .A;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is not quite the same as the reduced version we arrived at with &lt;a href=&quot;/~asampson/blog/reduction.html&quot;&gt;manual
reduction&lt;/a&gt;.
The one we wrote found last time actually an infinite loop&amp;mdash;so it wouldn&amp;rsquo;t
pass our interestingness test!
So this one is actually a little more useful in that sense: you can run it
through the reference interpreter to immediately see what it&amp;rsquo;s &lt;em&gt;supposed&lt;/em&gt; to
do.
It&amp;rsquo;s sort of weird that the reduced test has a multiply defined label, but
apparently neither interpreter cares about that&amp;hellip;
if we wanted to, we could imagine avoiding this by adding a second &amp;ldquo;not bogus&amp;rdquo;
check based on some static error checking.&lt;/p&gt;

</content>
    <summary type="html">&lt;p&gt;In a &lt;a href=&quot;{{site.base}}/blog/reduction.html&quot;&gt;previous post&lt;/a&gt;, I used a simple interpreter bug to demonstrate the research skill of manually reducing test cases.
This time, I show off the excellent &lt;a href=&quot;https://github.com/DRMacIver/shrinkray&quot;&gt;Shrinkray&lt;/a&gt; reducer to see how it can automate the same process.
The tricky part when using automated test-case reducers is writing an &lt;em&gt;interestingness test&lt;/em&gt; that actually does what you want.
I list a few tricks that help me write good interestingness tests.&lt;/p&gt;

</summary>
  </entry>
  
  <entry>
    
    <id>https://www.cs.cornell.edu/~asampson/blog/flatgfa.html</id>
    
    <title type="html">One Weird Trick for Efficient Pangenomic Variation Graphs (and File Formats for Free)</title>
    <updated>2024-05-28T00:00:00+00:00</updated>
    <link rel="alternate" href="https://www.cs.cornell.edu/~asampson/blog/flatgfa.html"/>
    <content type="html">&lt;p&gt;We built an efficient binary representation for &lt;a href=&quot;/~asampson/blog/mygfa.html&quot;&gt;pangenomic variation graphs&lt;/a&gt; that is equivalent to the standard &lt;a href=&quot;https://github.com/GFA-spec/GFA-spec&quot;&gt;GFA text format&lt;/a&gt;.
Our approach isn&amp;rsquo;t at all novel, but it illustrates a fun way that you can start with a naïve representation and transform it into a fast in-memory representation while also getting an on-disk file format for free.
In a shamelessly cherry-picked scenario, our tool is 1,331&amp;times; faster than &lt;a href=&quot;https://odgi.readthedocs.io/en/latest/&quot;&gt;an existing, optimized pangenomics toolkit&lt;/a&gt; that already uses an efficient binary representation.&lt;/p&gt;

&lt;h2 id=&quot;pangenome-recap&quot;&gt;Pangenome Recap&lt;/h2&gt;

&lt;p&gt;Let&amp;rsquo;s return to that pangenome data structure &lt;a href=&quot;/~asampson/blog/mygfa.html&quot;&gt;from last time&lt;/a&gt;. Here&amp;rsquo;s a simplified part of the data model, in fake Rust:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/~asampson/media/flatgfa/pointery.svg&quot; class=&quot;img-responsive bonw&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Graph&lt;/code&gt; owns sequences of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Segment&lt;/code&gt;s and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Path&lt;/code&gt;s, and the latter contains a sequence of &lt;em&gt;steps&lt;/em&gt;.
Each step is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Handle&lt;/code&gt; that encapsulates a traversal of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Segment&lt;/code&gt; in either the forward to the backward direction.
In the &lt;a href=&quot;https://github.com/GFA-spec/GFA-spec&quot;&gt;GFA&lt;/a&gt; text format, here are three &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Segment&lt;/code&gt;s and one &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Path&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;S	1	CAAATAAG
S	2	AAATTTTCTGGAGTTCTAT
S	4	CCAACTCTCTG
P	x	1+,2+,4-	*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The three &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;S&lt;/code&gt; lines are segments, which contain short nucleotide sequences.
That &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;P&lt;/code&gt; line is a path with three steps:
it traverses segment 1 and 2 in the forward (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt;) direction and then segment 4 in the backward (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt;) direction.
It looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/~asampson/media/flatgfa/xpath.svg&quot; class=&quot;img-responsive bonw&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This data structure seems pretty pointery.
&lt;a href=&quot;/~asampson/blog/mygfa.html&quot;&gt;Last time&lt;/a&gt;, I showed off a straightforward &lt;a href=&quot;https://cucapra.github.io/pollen/mygfa/&quot;&gt;Python library&lt;/a&gt; we made that embraces that pointeriness.
It&amp;rsquo;s slow but clear.&lt;/p&gt;

&lt;p&gt;This post is about implementing an efficient representation.
Other efficient data representations exist&amp;mdash;prominently, &lt;a href=&quot;https://odgi.readthedocs.io/en/latest/&quot;&gt;odgi&lt;/a&gt;, which is by some collaborators.
But they get performance by combining many different representation tricks.
I want to understand the basics here by using a single principle and see how far it gets us.&lt;/p&gt;

&lt;h2 id=&quot;flattening-the-data-structures&quot;&gt;Flattening the Data Structures&lt;/h2&gt;

&lt;p&gt;The central principle we&amp;rsquo;ll use is &lt;a href=&quot;/~asampson/blog/flattening.html&quot;&gt;flattening&lt;/a&gt;, a.k.a. arena allocation, a.k.a. just cramming everything into arrays and using indices instead of pointers.
In the fake Rust declarations above, I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ref&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&lt;/code&gt; to suggest ordinary pointer-based references to one and many elements.
In the flattened version, we&amp;rsquo;ll replace all of those those with plain &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u32&lt;/code&gt;s:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/~asampson/media/flatgfa/indexy.svg&quot; class=&quot;img-responsive bonw&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now the central &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Graph&lt;/code&gt; struct has three &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt; arenas that own all the segments, paths, and the steps within the paths.
Instead of a direct reference to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Segment&lt;/code&gt;, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Handle&lt;/code&gt; struct has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u32&lt;/code&gt; index into the segment arena.
And each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Path&lt;/code&gt; refers to a contiguous range in the step arena with start/end indices.
In &lt;a href=&quot;https://github.com/cucapra/pollen/blob/main/flatgfa/src/flatgfa.rs&quot;&gt;the real thing&lt;/a&gt;, even &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Path::name&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Segment::sequence&lt;/code&gt; get the same treatment:
there are two &lt;em&gt;giant&lt;/em&gt; byte strings in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Graph&lt;/code&gt; struct that act as arenas;
every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Path&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Segment&lt;/code&gt; just has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(u32, u32)&lt;/code&gt; pair to refer to its name or sequence as a chunk within a given string.&lt;/p&gt;

&lt;p&gt;The result is that, outside of the arenas, all the types involved are fixed-size, smallish, pointer-free structs.
It might be helpful to visualize the memory layout:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/~asampson/media/flatgfa/memory.svg&quot; class=&quot;img-responsive bonw&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Path::steps&lt;/code&gt; field refers to a slice of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;path_steps&lt;/code&gt; array, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Handle::segment&lt;/code&gt; field in there refers to a position in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;segments&lt;/code&gt; array.
There are no actual, word-sized pointers to the virtual address space anywhere.
Again, while I put the path names and the nucleotide sequences inline to make this picture simpler, &lt;a href=&quot;https://github.com/cucapra/pollen/blob/main/flatgfa/src/flatgfa.rs&quot;&gt;the actual implementation&lt;/a&gt; stores those in yet more arenas.
My current implementation uses 12 arenas to implement the complete GFA data model.&lt;/p&gt;

&lt;h2 id=&quot;its-pretty-fast-i-guess&quot;&gt;It&amp;rsquo;s Pretty Fast, I Guess&lt;/h2&gt;

&lt;p&gt;What have we really gained by replacing all our pointers with integers?
Even though we haven&amp;rsquo;t fundamentally changed the data structure, there are a few reasons why a flat representation for GFAs should be more efficient:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Faster allocation. By sacrificing the ability to deallocate elements at a fine granularity, we can use simple bump allocation instead of a proper &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;malloc&lt;/code&gt; when building the data structure.&lt;/li&gt;
  &lt;li&gt;Locality. We&amp;rsquo;re forcing logically contiguous elements, such as each path&amp;rsquo;s steps, to be contiguous in memory. That&amp;rsquo;s probably good for spatial locality.&lt;/li&gt;
  &lt;li&gt;Smaller pointers. Replacing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;Segment&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u32&lt;/code&gt; comes with a 2&amp;times; space savings on 64-bit machines. In a data structure with so many internal references, that probably counts for something.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here&amp;rsquo;s a brutally unfair way to measure the bottom-line performance impact of these effects.
We can compare our new, flattened implementation&amp;mdash;called &lt;a href=&quot;https://github.com/cucapra/pollen/tree/main/flatgfa&quot;&gt;FlatGFA&lt;/a&gt; and implemented in Rust&amp;mdash;against our simple-as-possible &lt;a href=&quot;https://cucapra.github.io/pollen/mygfa/&quot;&gt;Python library&lt;/a&gt; &lt;a href=&quot;/~asampson/blog/mygfa.html&quot;&gt;from last time&lt;/a&gt;.
For more context, we can also compare against &lt;a href=&quot;https://odgi.readthedocs.io/en/latest/&quot;&gt;odgi&lt;/a&gt;, a C++ toolkit for GFA processing that &lt;em&gt;also&lt;/em&gt; uses an efficient, index-based representation internally.&lt;/p&gt;

&lt;p&gt;For this experiment, we&amp;rsquo;ll just compare the time to &lt;em&gt;round-trip&lt;/em&gt; a GFA file through the internal representation:
we&amp;rsquo;ll make each tool parse and then immediately pretty-print the GFA to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/null&lt;/code&gt;.&lt;sup id=&quot;fnref:norm&quot;&gt;&lt;a href=&quot;#fn:norm&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;
I measured the round-trip performance on three tiny graphs and three medium-sized ones from the &lt;a href=&quot;https://humanpangenome.org&quot;&gt;Human Pangenome Reference Consortium&lt;/a&gt; and the &lt;a href=&quot;https://github.com/AndreaGuarracino/1000G-ONT-F100-PGGB&quot;&gt;1000 Genomes Project&lt;/a&gt;.&lt;sup id=&quot;fnref:sys&quot;&gt;&lt;a href=&quot;#fn:sys&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class=&quot;figrow&quot;&gt;
&lt;figure style=&quot;width: 55%&quot;&gt;
&lt;img src=&quot;/~asampson/media/flatgfa/roundtrip-mini.svg&quot; class=&quot;bonw&quot; alt=&quot;A bar chart comparing three tools&apos; time to round-trip GFA files through their in-memory representations. This comparison uses small graphs.&quot; /&gt;
&lt;figcaption&gt;Time to round-trip (parse and pretty-print) some tiny GFA files (288&amp;nbsp;kB, 1.0&amp;nbsp;MB, and 1.5&amp;nbsp;MB, from left to right). Our pure-Python &lt;a href=&quot;https://github.com/cucapra/pollen/tree/main/slow_odgi&quot;&gt;slow-odgi&lt;/a&gt; library is about 2&amp;times; slower than odgi.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure style=&quot;max-width: 42%&quot;&gt;
&lt;img src=&quot;/~asampson/media/flatgfa/roundtrip-med.svg&quot; class=&quot;bonw&quot; alt=&quot;A bar chart comparing two fast tools doing the same GFA round-trip task on much larger files.&quot; /&gt;
&lt;figcaption&gt;Round-tripping some bigger GFAs (7.2&amp;nbsp;GB, 2.3&amp;nbsp;GB, and 2.7&amp;nbsp;GB). The pure-Python library is not a contender. FlatGFA is 11.3&amp;times; faster than odgi on average (harmonic mean).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;FlatGFA can round-trip small GFA files about 14&amp;times; faster than &lt;a href=&quot;https://github.com/cucapra/pollen/tree/main/slow_odgi&quot;&gt;slow-odgi&lt;/a&gt;.
That speedup conflates the three fundamental advantages above with mundane implementation differences (FlatGFA is in Rust; slow-odgi is in Python).
I would love to do more measurement work here to disentangle these effects:
for example, we could check how much the pointer size matters by using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u64&lt;/code&gt;s everywhere instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u32&lt;/code&gt;s.&lt;/p&gt;

&lt;p&gt;FlatGFA is also 11.3&amp;times; faster than &lt;a href=&quot;https://odgi.readthedocs.io/en/latest/&quot;&gt;odgi&lt;/a&gt; on average.&lt;sup id=&quot;fnref:fastest&quot;&gt;&lt;a href=&quot;#fn:fastest&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;
It can process the largest graph (7.2&amp;nbsp;GB of uncompressed GFA text) in 67 seconds, versus 14 minutes for odgi.
I don&amp;rsquo;t really know why, because odgi also (sensibly) uses a mostly flattened representation.
My best guess is that odgi&amp;rsquo;s implementers focused their efforts on actually novel, actually smart data structure ideas with asymptotic benefits (e.g., they use a special index to quickly locate steps within a path) and not on boring data-representation engineering that only brings constant factors.
FlatGFA only does boring, but it goes all the way: it exterminates &lt;em&gt;all&lt;/em&gt; the pointers.
And the constant-factor payoff from this basic flattening transformation can be surprisingly large.&lt;/p&gt;

&lt;p&gt;Possible lessons here include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&amp;ldquo;Merely&amp;rdquo; flattening an otherwise naïve data structure can turn it into a competitive one.&lt;/li&gt;
  &lt;li&gt;So it can be helpful to get that in place even before seeking asymptotic gains through indexing and the like.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;a-file-format-for-free&quot;&gt;A File Format for Free&lt;/h2&gt;

&lt;p&gt;Because it has no pointers in it, a ruthlessly flattened in-memory representation has one bonus feature:
it does double duty as a file format.
If we take all those densely packed arrays-of-structs that make up a FlatGFA, concatenate them together, and add a little header to contain the sizes, we have a blob of bytes that we might as well write to disk.&lt;/p&gt;

&lt;p&gt;Turning FlatGFA into a file format took two steps:
applying the amazing &lt;a href=&quot;https://docs.rs/zerocopy/&quot;&gt;zerocopy&lt;/a&gt; crate,
and separating the data store from the interface to the data.&lt;/p&gt;

&lt;h3 id=&quot;use-zerocopy-to-get-asbytes-and-frombytes&quot;&gt;Use zerocopy to get &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsBytes&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FromBytes&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;First, &lt;a href=&quot;https://docs.rs/zerocopy/&quot;&gt;zerocopy&lt;/a&gt; is an immensely rad crate that can certify that it&amp;rsquo;s safe to cast between Rust types and raw bytes.
It contains a bunch of macros, but these macros don&amp;rsquo;t actually generate code for you: they just check that your types obey a bunch of rules.
The zerocopy authors have done the hard work (i.e., thinking carefully and using &lt;a href=&quot;https://github.com/rust-lang/miri&quot;&gt;Miri&lt;/a&gt;) to be pretty confident that all types that obey their rules can be safely transmuted to and from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[u8]&lt;/code&gt; chunks.
These rules include alignment restrictions and the necessity that every bit-pattern is a valid value.
The latter rule makes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enum&lt;/code&gt;s tricky: every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enum&lt;/code&gt; must have 2&lt;em&gt;ⁿ&lt;/em&gt; variants where &lt;em&gt;n&lt;/em&gt; is the number of bits in its representation.
But once you&amp;rsquo;ve cleared all those hurdles, your types gain the &lt;a href=&quot;https://docs.rs/zerocopy/latest/zerocopy/trait.AsBytes.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsBytes&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://docs.rs/zerocopy/latest/zerocopy/trait.FromBytes.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FromBytes&lt;/code&gt;&lt;/a&gt; traits.&lt;/p&gt;

&lt;h3 id=&quot;separate-the-storage-from-the-interface&quot;&gt;Separate the Storage from the Interface&lt;/h3&gt;

&lt;p&gt;Now that all our structs are equipped with the zerocopy superpowers, we need a way to make &lt;em&gt;the entire data structure&lt;/em&gt; map to bytes.
One nice way to do it is to separate our actual data storage location from a lightweight view of all the same data.
The idea is to start with that top-level struct that contains nothing but the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt;s of littler structs:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/~asampson/media/flatgfa/store1.svg&quot; class=&quot;img-responsive bonw&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And to separate it into two structs.
We want one &lt;em&gt;store&lt;/em&gt; object that keeps all those &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt;s and one &lt;em&gt;view&lt;/em&gt; object that has all the same fields but with slices instead of vectors:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/~asampson/media/flatgfa/store2.svg&quot; class=&quot;img-responsive bonw&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ThingStore&lt;/code&gt; struct will never get the zerocopy superpower&amp;mdash;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt;s are inherently pointerful and must live on the heap&amp;mdash;but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ThingView&lt;/code&gt; is perfectly suited.
We can construct one by calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;from_bytes&lt;/code&gt; on different chunks within a big byte buffer:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/~asampson/media/flatgfa/store3.svg&quot; class=&quot;img-responsive bonw&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We&amp;rsquo;ll also need a small &lt;a href=&quot;https://github.com/cucapra/pollen/blob/788aa3e48aff6a8c7b46f10c1c5fcaeee909518b/flatgfa/src/file.rs#L10-L26&quot;&gt;table of contents&lt;/a&gt; at the top of the file to tell us where those chunks are.
But once we&amp;rsquo;ve managed that, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ThingView&lt;/code&gt; serves as an abstraction layer over the two storage styles.
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt;-based store provides heap-allocated, arbitrarily resizable allocation pools;
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;[u8]&lt;/code&gt; option constrains the sizes of the arenas but maps easily to a flat file.&lt;sup id=&quot;fnref:slicevec&quot;&gt;&lt;a href=&quot;#fn:slicevec&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h2 id=&quot;speedup&quot;&gt;0 Copy = ∞ Speedup&lt;/h2&gt;

&lt;p&gt;Using the same type for the in-memory and on-disk representations is not just convenient;
it also makes deserialization &lt;em&gt;infinity times faster&lt;/em&gt;.&lt;sup id=&quot;fnref:capnproto&quot;&gt;&lt;a href=&quot;#fn:capnproto&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;
To &amp;ldquo;open&amp;rdquo; a file, all we need to do is to &lt;a href=&quot;https://linux.die.net/man/2/mmap&quot;&gt;mmap(2)&lt;/a&gt; it.
There is no deserialization step; we don&amp;rsquo;t even have to read the whole file if we don&amp;rsquo;t need it.&lt;/p&gt;

&lt;p&gt;This latter aspect can lead to some pretty funny speedups for FlatGFA compared to odgi, which has its own efficient binary GFA-equivalent file format but which uses traditional serialization.
Here&amp;rsquo;s a performance comparison between &lt;a href=&quot;https://odgi.readthedocs.io/en/latest/rst/commands/odgi_paths.html&quot;&gt;the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;odgi paths -L&lt;/code&gt; command&lt;/a&gt;, which just prints out all the names of the paths in the graph, and the FlatGFA and slow-odgi equivalents:&lt;/p&gt;

&lt;div class=&quot;figrow&quot;&gt;
&lt;figure style=&quot;width: 55%&quot;&gt;
&lt;img src=&quot;/~asampson/media/flatgfa/paths-mini.svg&quot; class=&quot;bonw&quot; alt=&quot;A bar chart comparing three tools&apos; times to print out a list of path names from GFA files.&quot; /&gt;
&lt;figcaption&gt;Time to print the path names from the same graphs as above. The &lt;a href=&quot;https://github.com/cucapra/pollen/tree/main/slow_odgi&quot;&gt;slow-odgi&lt;/a&gt; reference implementation is a hilarious 19&amp;times; slower than odgi on average.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure style=&quot;max-width: 40%&quot;&gt;
&lt;img src=&quot;/~asampson/media/flatgfa/paths-med.svg&quot; class=&quot;bonw&quot; alt=&quot;Another bar chart comparing the same path-name-printing task on three larger GFAs, and only comparing the two faster tools.&quot; /&gt;
&lt;figcaption&gt;Printing paths names from the same larger graphs. FlatGFA is 1,331&amp;times; faster than odgi, which is not infinity, but it&apos;s pretty good.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;This comparison starts with the native binary formats for odgi and FlatGFA, so only slow-odgi actually has to parse any text.
Across the three big GFAs, FlatGFA is 1,331&amp;times; faster than odgi on average.
On the largest (7.2&amp;nbsp;GB) graph, FlatGFA takes 5.8&amp;nbsp;ms to odgi&amp;rsquo;s 12&amp;nbsp;seconds.
If you look at &lt;a href=&quot;https://share.firefox.dev/3KiOKLW&quot;&gt;a profile of where odgi spends its time&lt;/a&gt;, about 90% of it goes to deserialization and 9.9% goes to deallocating that data structure.
Less than 1% of the time is spent on the &amp;ldquo;real work,&amp;rdquo; i.e., extracting and printing those path names.
So this is an extreme edge case where FlatGFA&amp;rsquo;s deserialization- and allocation-free strategy makes for especially silly-looking bar charts.&lt;/p&gt;

&lt;h2 id=&quot;someday-acceleration&quot;&gt;Someday, Acceleration&lt;/h2&gt;

&lt;p&gt;We originally fell into this rabbit hole because we want to build hardware accelerators for this stuff.
Surprising absolutely no one, data representation turns out to be the key to an efficient implementation, regardless of whether it&amp;rsquo;s in hardware or software.
Outside of flattening and memory-mapping, FlatGFA is totally unoptimized&amp;mdash;so we&amp;rsquo;re now fully distracted by understanding the space of possible efficient representations and their implications for hardware design.
We&amp;rsquo;ll get back to implementing that hardware someday.
I promise.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:norm&quot;&gt;
      &lt;p&gt;FlatGFA is the only one of the three tools that actually preserves GFA files, byte for byte, when round-tripping them. Both odgi and mygfa (quite sensibly) normalize the ordering of elements in the graph. I made FlatGFA preserve the contents exactly to make it easier to test.&amp;nbsp;&lt;a href=&quot;#fnref:norm&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:sys&quot;&gt;
      &lt;p&gt;All wall-clock times collected on our lab server, which has two Xeon Gold 6230 processors (20 cores per socket @ 2.1 GHz) and 512 GB RAM. It runs Ubuntu 22.04. Error bars show standard deviations over at least 3 (and usually more like 10) runs, measured with &lt;a href=&quot;https://github.com/sharkdp/hyperfine&quot;&gt;Hyperfine&lt;/a&gt;.&amp;nbsp;&lt;a href=&quot;#fnref:sys&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:fastest&quot;&gt;
      &lt;p&gt;I think this means FlatGFA currently has the fastest GFA parser in the universe. This is not all that impressive; it&amp;rsquo;s an extremely easy-to-parse grammar. Even so, I would love to be proven wrong.&amp;nbsp;&lt;a href=&quot;#fnref:fastest&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:slicevec&quot;&gt;
      &lt;p&gt;In &lt;a href=&quot;https://github.com/cucapra/pollen/tree/main/flatgfa&quot;&gt;the real implementation&lt;/a&gt;, I also added a second storage style based on &lt;a href=&quot;https://docs.rs/tinyvec/latest/tinyvec/struct.SliceVec.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tinyvec::SliceVec&lt;/code&gt;&lt;/a&gt; instead of plain old &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt;. This approach splits the difference between slices and vectors: each arena has a fixed maximum capacity, but its length can be less than that. So the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SliceVec&lt;/code&gt;s, even when they map to a fixed-size &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;[u8]&lt;/code&gt; of file contents, can still shrink and grow within limits.&amp;nbsp;&lt;a href=&quot;#fnref:slicevec&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:capnproto&quot;&gt;
      &lt;p&gt;This hyperbolic framing is stolen from &lt;a href=&quot;https://capnproto.org&quot;&gt;Cap&amp;rsquo;n Proto&lt;/a&gt;, which honestly blew my mind the first time I understood what it was doing.&amp;nbsp;&lt;a href=&quot;#fnref:capnproto&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
    <summary type="html">&lt;p&gt;&lt;a href=&quot;{{site.base}}/blog/mygfa.html&quot;&gt;Last time&lt;/a&gt;, I introduced &lt;a href=&quot;https://en.wikipedia.org/wiki/Pan-genome&quot;&gt;pangenomic variation graphs&lt;/a&gt;, the standard &lt;a href=&quot;https://github.com/GFA-spec/GFA-spec&quot;&gt;text file format&lt;/a&gt; that biologists use for them, and a &lt;a href=&quot;https://cucapra.github.io/pollen/mygfa/&quot;&gt;hopelessly naïve reference data model&lt;/a&gt; we implemented for them. This time, we use a single principle&amp;mdash;flattening&amp;ndash;to build an efficient representation that is not only way faster than the naïve library but also competitive with an &lt;a href=&quot;https://odgi.readthedocs.io/en/latest/&quot;&gt;exisitng, optimized toolkit&lt;/a&gt;. Flattening also yields a memory-mapped file format &amp;ldquo;for free&amp;rdquo; that, in a shamelessly cherry-picked scenario, is more than a thousand times faster than the serialization-based alternative.&lt;/p&gt;

</summary>
  </entry>
  
</feed>
