<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://www.xn--tkuka-m3a3v.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.xn--tkuka-m3a3v.dev/" rel="alternate" type="text/html" /><updated>2026-08-24T16:52:13+00:00</updated><id>https://www.xn--tkuka-m3a3v.dev/feed.xml</id><title type="html">tīkōuka.dev</title><subtitle>Less of a blog and more of a personal wiki as I continuously revise old posts. Rarely about cabbage trees.</subtitle><author><name>sh1boot</name></author><entry><title type="html">CISC-V: an exploratory code compression for RISC-V</title><link href="https://www.xn--tkuka-m3a3v.dev/cisc-v-packet-structure/" rel="alternate" type="text/html" title="CISC-V: an exploratory code compression for RISC-V" /><published>2026-08-14T00:00:00+00:00</published><updated>2026-08-14T00:00:00+00:00</updated><id>https://www.xn--tkuka-m3a3v.dev/cisc-v-packet-structure</id><content type="html" xml:base="https://www.xn--tkuka-m3a3v.dev/cisc-v-packet-structure/"><![CDATA[<p>After months of trying to get around to it, I finally have a straw-man
level system of <a href="/cisc-v-begins/">compressing RISC-V code</a> by squashing
pairs of opcodes into a single 32-bit packet rather than squashing
individual opcodes into 16-bit packets in order to address issues with
unaligned 32-bit instruction words.</p>

<p>The intent is to make a minimal implementation possible which can decode
the first slot easily while simultaneously reformatting the packet to
expose the B slot for interpretation by the same decoder on the next
step.  But also to make compressed code more digestible for wide, multi-issue
implementations.</p>

<p>Not to imply that performance cores <em>want</em> compressed code.  Merely that if they’re stuck with it as a compatibility constraint then it shouldn’t be so painful.</p>

<p>It’s still not a fully defined thing.  Just a thought experiment gone a
little too far.</p>

<p>This takes 1/4 of the total instruction coding space, meaning it steals
1/3 of the space occupied by RVC.  It’s assumed that the remaining 2/3
of RVC won’t be used in conjunction with this as that would undermine
the objectives given below.</p>

<h2 id="why">Why?</h2>

<p>To make compressed code less of an impediment to high-end cores,
while teasing a couple of side benefits.  In particular:</p>

<ul>
  <li>To avoid the problems of unaligned 32-bit instructions</li>
  <li>To avoid the other problems with mixed-size instructions</li>
  <li>To exploit inter-opcode redundancies</li>
  <li>To use much less opcode space</li>
  <li>To dress up like a CISC architecture in order to gain its powers</li>
  <li>To maintain the conventional 2-source, 1-destination data flow model
by executing 32-bit packets sequentially as two separate instructions</li>
  <li>To expose macro-op fusion opportunities within a 32-bit word when
executing whole packets at once</li>
</ul>

<p>Basically, I saw too many complaints about the various consequences of
16-bit aligned instructions mixed with 32-bit aligned instructions, and
so I started <a href="/naturally-aligned-instruction-set/">thinking about it
myself</a>, and did <a href="/experimental-riscv-instruction-compression/">some very rough
hacking</a> to see how I
might solve it, and now I’m at the stage which I present here, which I
hope is good enough to get the general idea across and demonstrate
that the compression can work under the given constraints.</p>

<p>If you’re coming to this from the point of view of out-of-order wide multi-issue pipelines then I’m hoping you’ll be able to just chuck whole 32-bit packets through the front end as single instructions and then do µ-op fission in the usual way.  Control flow only changes between whole packets.</p>

<h2 id="status">Status</h2>

<p>This is just an exploration, not a formal RISC-V proposal.  All things
are subject to change and much needs to be solidified before it can be
implemented.</p>

<p>The results here are intended to establish the viability of the
compression and to give a view on mindset and intent which motivated
decisions, compromises, and blind hope.</p>

<p>Most of this document focuses on the cheap end, and how it might minimise the cost of implementation.  Performance cores just get the occasional hint here and there about shortcuts they might take to avoid pain.</p>

<p>Some features here fall in the “totally unacceptable” category, and that’s OK.  They just fill in gaps and highlight what needs further work.  Some things have turned out to be less untenable than I expected.</p>

<h2 id="packet-structure">Packet structure</h2>

<p>A packet comprises two instructions compressed into a 32-bit instruction
word.  There are four register fields (the three standard ones, plus one
more in part of <code>funct7</code>) which are shared between two regular RISC-V
instructions:</p>

<svg width="100%" height="52" viewbox="0 0 800 52">
<style>
.ins-box-selector,.ins-box-constant {
    --tinted-fill: oklab(from var(--tint-fill-base) l 0 0);
}
.ins-box-imm {
    --tint: 0;
}
.ins-box-rs {
    --tint: 1;
    stop-color: var(--tinted-fill);
}
.ins-box-rd {
    --tint: 2;
    stop-color: var(--tinted-fill);
}
.ins-box-rsd {
    --tint: 1;
    --tint-b: 2;
    fill: url("#RSDGradient");
}
.opcodes td,th {
  width: 4em;
}
</style>
<defs>
<LinearGradient id="RSDGradient" x1="0" y1="0.2" x2="1" y2="0.8">
<stop class="ins-box-rs" offset="30%" />
<stop class="ins-box-rd" offset="70%" />
</LinearGradient>
<g id="header" style="font-weight:bold;">
<rect x="82" y="1" width="126" height="24" class="ins-box-selector tintbox" />
<text x="145.0" y="13">funct7</text>
<rect x="208" y="1" width="90" height="24" class="ins-box-rs tintbox" />
<text x="253.0" y="13">rs2</text>
<rect x="298" y="1" width="90" height="24" class="ins-box-rs tintbox" />
<text x="343.0" y="13">rs1</text>
<rect x="388" y="1" width="54" height="24" class="ins-box-selector tintbox" />
<text x="415.0" y="13">funct3</text>
<rect x="442" y="1" width="90" height="24" class="ins-box-rd tintbox" />
<text x="487.0" y="13">rd</text>
<rect x="532" y="1" width="126" height="24" class="ins-box-selector tintbox" />
<text x="595.0" y="13">opcode</text>
</g>
<g id="header-impl" style="font-weight:bold;">
<use href="#header" />
<text x="2" y="13" style="text-anchor:start;">implicit</text>
</g>
</defs>
<use href="#header" />
<rect x="118" y="30" width="90" height="20" class="ins-box-rsd tintbox" />
<text x="163.0" y="42">r_extra</text>
</svg>

<p>The mapping of fields to different instruction operands is described by a
frame.  Different frames and different opcode pairs within each frame are
currently enumerated in the ten free bits in <code>opcode</code>, <code>funct3</code>, and two
bits of <code>funct7</code> (two bits of <code>opcode</code> are reserved to identify this
encoding scheme).  More on “enumeration” and its decoder implications
later.</p>

<p>No new instruction semantics are introduced; some optional instructions are
included.  A frame merely compressed two consecutive instructions into
one 32-bit packet.  The first instruction (generally) has its source
register fields aligned to the source register fields of 32-bit opcodes,
but the usual destination register field is (generally) the register
written by the second instruction.  Other fields are recycled and
repurposed according to the specific frame structure they follow.</p>

<p>For example, a frame may designate a source register as also the
destination register to save encoding space, or elide the destination
register slot the first instruction and a source register slot of the
second instruction and hard-code them as a temporary register.</p>

<p>Immediates are 5-bit by default, aliasing with a register index.  When
this is insufficient the frame may repurpose another register field to
make a ten-bit immediate.  When ten bits is too many (e.g., the index for
bit shifts on a 64-bit platform) the immediate-consuming instruction is
duplicated in the list of opcodes the frame supports, and the choice
between the two (or four, or eight) copies of the same instruction
serves as an extra bit (or two or three).</p>

<p>For chain rules, temporary storage is used and not encoded in the
packet at all.  It’s left to the implementation to decide what kind of
path this data takes, with the caveat that it must be exposed
architecturally for exceptions.  The possibility is held open for
the decoder to hard-code the temporary register as <code>x31</code> or <code>x7</code> if this
does not impede optimisation (see <a href="#exceptions-and-interrupts">Exceptions and
interrupts</a>).</p>

<p>Frame structures are signalled by the opcode field.  Each frame has a
number of opcode pair configurations it supports, and these are
enumerated and rounded up to a power of two.</p>

<p>Instruction pairs only allow control flow changes in the second slot.
Branch targets are always 32-bit aligned.</p>

<p>A frame might pose a question like “how do we implement load with base
address write-back?” and then encapsulate the two instructions which
implement that (<code>load</code> and <code>addi</code>) by re-using operands from the first
instruction in the second (<code>rbase</code> -&gt; <code>rdest</code>, <code>rbase</code> -&gt; <code>rs1</code>,
<code>offset</code> -&gt; <code>immediate</code>), and then unroll that across all the
opcodes (<code>lb</code>, <code>lbu</code>, <code>lh</code>, etc..) it needs to complete the set.</p>

<p>Typical savings come from sharing <code>rd</code> and <code>rs1</code> (as <code>rsd</code>) encoding,
and from ‘chain’ rules where the first result is used only by the second
operation before being discarded, and so neither the destination
register nor its reference in the next op need to be encoded explicitly.
Implicit <code>sp</code> is used a lot as well, but that saving is usually spent on
a longer offset for large stack frames.</p>

<h2 id="decoding-and-execution">Decoding and execution</h2>

<p>In the simplest implementation an instruction
decoder would decode the first slot normally (with the caveat that the
destination register is not in the usual location), while also preparing
another conventional 32-bit instruction word to feed back to the same
instruction decoder for the next decode step.</p>

<p>Alternatively, an implementation might ingest the packet as a single
instruction and split it into µ-ops at a more convenient stage in
the pipeline.  A more aggressively optimised implementation could fuse
them into a single operation when practical.</p>

<p>Regardless of the implementation a valid packet has the architectural
effect of two ordinary RISC-V instructions executed sequentially, each
consuming its operands and producing its normal architectural
result in turn, with slot B observing the effects of slot A.  Some
combinations may have to be excluded from permitted encodings if they
have problematic implications in high-performance implementations.  The
intent is to capture such cases explicitly if they’re common but not to
leave them as performance landmines if they’re rare.</p>

<p>Similarly, some packets, like <code>rsd-alu-pair</code>, specify separate
destination registers for each slot, meaning they’re able to encode
cases where the second slot must use the result of the first.  This
might offer code density gains but could raise pipeline headaches, so
encoding such dependencies should probably be prohibited.  If it remains
legal then it must still behave <em>as if</em> sequential.</p>

<p>At present there’s no optimisation for ease of decode, other than an
attempt to align the operand fields consistently.  The remaining ten
bits are a naive enumeration of all the allowed permutations, with
little organisation into bits.  That’s to be addressed later, but ten
bits is at least better than 32.</p>

<h2 id="exceptions-and-interrupts">Exceptions and interrupts</h2>

<p>If an exception occurs inside a packet then the architectural state
must be properly resolved such that execution can resume at the
appropriate slot <em>within</em> a packet, as needed.  If the first slot did
not cause the exception then architectural state must be updated
accordingly, <em>as if</em> the two slots execute sequentially.</p>

<p>Since a restart needs to distinguish which instruction caused the fault,
bit 1 of the PC can be used to signal slot B (dressing up as if we’re
executing two 16-bit instructions).  This mechanism is only required for
restarts and doesn’t have to have optimal performance.  Normal branch
and jump targets are always 32-bit aligned, and for all purposes outside of exception and interrupt handling PC can safely be assumed to be 32-bit aligned.</p>

<p>Interrupts are assumed to follow the same protocol as exceptions.  It is
hoped (TBD) that an implementation would be able to meet the
architectural model while deferring interrupts until completion of the
whole packet in order to avoid complexity.</p>

<p>The other obvious state needed for a restart is the temporary value used
in chain operations.</p>

<p>Logically one could hard-code this temporary during instruction decode
as <code>x31</code> (which doesn’t exist on RV32-E, so maybe <code>x7</code> instead?) so
it’s automatically saved; but some implementations may not appreciate
this constraint, and it would be preferable for such a named register
to not be guaranteed to retain a particular value after chain packets in
normal operation.  The intermediate value should only be considered
reliably written back when forced by an exception or interrupt.</p>

<p>Alternatively, just expose it in a CSR.  I’m not sure what’s best for
the most economical implementations.</p>

<p>Another alternative for exceptions, if an implementation doesn’t
want to deal with the complexity at all and has the necessary machinery,
could be to cancel the whole packet (reversing the effects of slot A
where necessary) and for the exception handler to simulate the pair of
instructions one at a time.</p>

<h2 id="breakpoints">Breakpoints</h2>

<p>I assume these can be handled much the same way as exceptions, in the case of hardware breakpoints, or with a couple more rounds of rewriting in-place using standard 32-bit instructions.</p>

<h2 id="the-encoding-provisional">The encoding (provisional)</h2>

<p>Here’s what I came up with.  Opcodes are enumerated simply to
demonstrate that they can actually be encoded into 32 bits while the
frames continue to evolve.  The current assignment of the bit values is
not how it should be done, just something expedient.</p>

<p>Bits marked <code>p</code> below are the bits used to enumerate the opcode
combinations available in each frame.  The values for the integer
stored in the <code>p</code> bits are given in the tables following the frame
structure.  It’s just a provisional assignment and hasn’t been tuned for
a realistic decoder.</p>

<p>Where the enumeration jumps by more than one, and where instructions are
marked “×n”, that’s where bits of the opcode enumeration have been
taken to extend the immediate range.  The plan, here, is to align the
bits which choose between duplicate opcodes to always land in the same
positions in every frame, so that immediates decode consistently
(give or take masking to the proper length).</p>

<h3 id="frame-layouts">frame layouts</h3>

<h3 id="alu-alu-chain">alu-alu-chain</h3>
<p>Two ALU operations, the second consuming the first’s result.</p>

<svg width="100%" height="230" viewbox="0 0 800 230">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">0 1 0 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">tmp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> alu     tmp, rs1a, rs2a</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">rs2b</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">rdb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">tmp</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> alu     rdb, tmp, rs2b</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="92" width="90" height="36" />
    <text x="577" y="110">0 1 0 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="92" width="54" height="36" />
    <text x="415" y="110">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="92" width="18" height="36" />
    <text x="109" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="92" width="18" height="36" />
    <text x="91" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="92" width="36" height="36" />
    <text x="640" y="110">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="90" width="90" height="20" />
    <text x="253" y="100">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="90" width="90" height="20" />
    <text x="343" y="100">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="90" width="40" height="20" />
    <text x="20" y="100">tmp</text>
    
  
  
  <text x="662" y="100" style="text-anchor:start;font-family=monospace;font-size:small;"> alu     tmp, rs1a, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="110" width="90" height="20" />
    <text x="163" y="120">rs2b</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="110" width="90" height="20" />
    <text x="487" y="120">rdb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="110" width="40" height="20" />
    <text x="20" y="120">tmp</text>
    
  
  
  <text x="662" y="120" style="text-anchor:start;font-family=monospace;font-size:small;"> alu     rdb, tmp, rs2b</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="142" width="90" height="36" />
    <text x="577" y="160">0 1 0 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="142" width="54" height="36" />
    <text x="415" y="160">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="142" width="18" height="36" />
    <text x="109" y="160">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="142" width="18" height="36" />
    <text x="91" y="160">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="142" width="36" height="36" />
    <text x="640" y="160">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="140" width="90" height="20" />
    <text x="253" y="150">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="140" width="90" height="20" />
    <text x="343" y="150">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="140" width="40" height="20" />
    <text x="20" y="150">tmp</text>
    
  
  
  <text x="662" y="150" style="text-anchor:start;font-family=monospace;font-size:small;"> alu     tmp, rs1a, rs2a</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="160" width="90" height="20" />
    <text x="163" y="170">immb</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="160" width="90" height="20" />
    <text x="487" y="170">rdb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="160" width="40" height="20" />
    <text x="20" y="170">tmp</text>
    
  
  
  <text x="662" y="170" style="text-anchor:start;font-family=monospace;font-size:small;"> alu     rdb, tmp, immb</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="192" width="90" height="36" />
    <text x="577" y="210">0 1 0 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="192" width="54" height="36" />
    <text x="415" y="210">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="192" width="18" height="36" />
    <text x="109" y="210">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="192" width="18" height="36" />
    <text x="91" y="210">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="192" width="36" height="36" />
    <text x="640" y="210">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="190" width="90" height="20" />
    <text x="253" y="200">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="190" width="90" height="20" />
    <text x="343" y="200">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="190" width="40" height="20" />
    <text x="20" y="200">tmp</text>
    
  
  
  <text x="662" y="200" style="text-anchor:start;font-family=monospace;font-size:small;"> alu     tmp, rs1a, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="210" width="90" height="20" />
    <text x="163" y="220">immb</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="210" width="90" height="20" />
    <text x="487" y="220">rdb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="210" width="40" height="20" />
    <text x="20" y="220">tmp</text>
    
  
  
  <text x="662" y="220" style="text-anchor:start;font-family=monospace;font-size:small;"> alu     rdb, tmp, immb</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>addi<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    
    <th>add</th>
  
    
    <th>andi</th>
  
    
    <th>slli</th>
  
    
    <th>sltu</th>
  
    
    <th>srliw</th>
  
    
    <th>sub</th>
   </tr>
  
  
  <tr><th>addi<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    <td>0</td>
    
  
    <td>2</td>
    
  
    <td>3</td>
    
  
    <td>4</td>
    
  
    <td>5</td>
    
  
    <td>6</td>
    
  
    <td>7</td>
    
   </tr>
  
  
  <tr><th>add</th>
  
    <td>16</td>
    
  
    <td>18</td>
    
  
    <td>19</td>
    
  
    <td>20</td>
    
  
    <td>21</td>
    
  
    <td>22</td>
    
  
    <td>23</td>
    
   </tr>
  
  
  <tr><th>and</th>
  
    <td>24</td>
    
  
    <td>26</td>
    
  
    <td>27</td>
    
  
    <td>28</td>
    
  
    <td>29</td>
    
  
    <td>30</td>
    
  
    <td>31</td>
    
   </tr>
  
  
  <tr><th>or</th>
  
    <td>32</td>
    
  
    <td>34</td>
    
  
    <td>35</td>
    
  
    <td>36</td>
    
  
    <td>37</td>
    
  
    <td>38</td>
    
  
    <td>39</td>
    
   </tr>
  
  
  <tr><th>slli</th>
  
    <td>40</td>
    
  
    <td>42</td>
    
  
    <td>43</td>
    
  
    <td>44</td>
    
  
    <td>45</td>
    
  
    <td>46</td>
    
  
    <td>47</td>
    
   </tr>
  
  
  <tr><th>srli</th>
  
    <td>48</td>
    
  
    <td>50</td>
    
  
    <td>51</td>
    
  
    <td>52</td>
    
  
    <td>53</td>
    
  
    <td>54</td>
    
  
    <td>55</td>
    
   </tr>
  
  
  <tr><th>sub</th>
  
    <td>56</td>
    
  
    <td>58</td>
    
  
    <td>59</td>
    
  
    <td>60</td>
    
  
    <td>61</td>
    
  
    <td>62</td>
    
  
    <td>63</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>addi<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    
    <th>and</th>
  
    
    <th>or</th>
  
    
    <th>sltiu</th>
  
    
    <th>srli</th>
  
    
    <th>xor</th>
  
    
    <th>xori</th>
   </tr>
  
  
  <tr><th>addi<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    <td>64</td>
    
  
    <td>66</td>
    
  
    <td>67</td>
    
  
    <td>68</td>
    
  
    <td>69</td>
    
  
    <td>70</td>
    
  
    <td>71</td>
    
   </tr>
  
  
  <tr><th>add</th>
  
    <td>80</td>
    
  
    <td>82</td>
    
  
    <td>83</td>
    
  
    <td>84</td>
    
  
    <td>85</td>
    
  
    <td>86</td>
    
  
    <td>87</td>
    
   </tr>
  
  
  <tr><th>andi</th>
  
    <td>88</td>
    
  
    <td>90</td>
    
  
    <td>91</td>
    
  
    <td>92</td>
    
  
    <td>93</td>
    
  
    <td>94</td>
    
  
    <td>95</td>
    
   </tr>
  
  
  <tr><th>or</th>
  
    <td>96</td>
    
  
    <td>98</td>
    
  
    <td>99</td>
    
  
    <td>100</td>
    
  
    <td>101</td>
    
  
    <td>102</td>
    
  
    <td>103</td>
    
   </tr>
  
  
  <tr><th>slli</th>
  
    <td>104</td>
    
  
    <td>106</td>
    
  
    <td>107</td>
    
  
    <td>108</td>
    
  
    <td>109</td>
    
  
    <td>110</td>
    
  
    <td>111</td>
    
   </tr>
  
  
  <tr><th>sltiu</th>
  
    <td>112</td>
    
  
    <td>114</td>
    
  
    <td>115</td>
    
  
    <td>116</td>
    
  
    <td>117</td>
    
  
    <td>118</td>
    
  
    <td>119</td>
    
   </tr>
  
  
  <tr><th>xor</th>
  
    <td>120</td>
    
  
    <td>122</td>
    
  
    <td>123</td>
    
  
    <td>124</td>
    
  
    <td>125</td>
    
  
    <td>126</td>
    
  
    <td>127</td>
    
   </tr>
  
</table>


</div>
<h3 id="index-mem-chain">index-mem-chain</h3>
<p>Scaled-index addressing: compute <code>base + i*width</code> and access it.</p>

<svg width="100%" height="130" viewbox="0 0 800 130">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 0 1 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">1 1 p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">tmp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> shXadd  tmp, rs1a, rs2a</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">immb</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">rdb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">tmp</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> load    rdb, k*immb(tmp)</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="92" width="90" height="36" />
    <text x="577" y="110">1 0 1 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="92" width="54" height="36" />
    <text x="415" y="110">1 1 p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="92" width="18" height="36" />
    <text x="109" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="92" width="18" height="36" />
    <text x="91" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="92" width="36" height="36" />
    <text x="640" y="110">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="90" width="90" height="20" />
    <text x="253" y="100">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="90" width="90" height="20" />
    <text x="343" y="100">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="90" width="40" height="20" />
    <text x="20" y="100">tmp</text>
    
  
  
  <text x="662" y="100" style="text-anchor:start;font-family=monospace;font-size:small;"> shXadd  tmp, rs1a, rs2a</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="110" width="90" height="20" />
    <text x="163" y="120">rs2b</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="110" width="90" height="20" />
    <text x="487" y="120">immb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="110" width="40" height="20" />
    <text x="20" y="120">tmp</text>
    
  
  
  <text x="662" y="120" style="text-anchor:start;font-family=monospace;font-size:small;"> store   rs2b, k*immb(tmp)</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>add</th>
   </tr>
  
  
  <tr><th>lbu</th>
  
    <td>0</td>
    
   </tr>
  
  
  <tr><th>sb</th>
  
    <td>1</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>sh1add</th>
   </tr>
  
  
  <tr><th>lhu</th>
  
    <td>2</td>
    
   </tr>
  
  
  <tr><th>sh</th>
  
    <td>3</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>sh2add</th>
   </tr>
  
  
  <tr><th>lw</th>
  
    <td>4</td>
    
   </tr>
  
  
  <tr><th>sw</th>
  
    <td>5</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>sh3add</th>
   </tr>
  
  
  <tr><th>ld</th>
  
    <td>6</td>
    
   </tr>
  
  
  <tr><th>sd</th>
  
    <td>7</td>
    
   </tr>
  
</table>


</div>
<h3 id="pre-inc-pair">pre-inc-pair</h3>
<p>Advance a pointer, then access through it (pre-increment).</p>

<svg width="100%" height="230" viewbox="0 0 800 230">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 0 1 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">1 0 p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">rsda</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rs1a</text>
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> shXadd  rsda, rs1a, rsda</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">immb</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="208" y="60" width="90" height="20" />
    <text x="253" y="70">rsda</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">rdb</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> load    rdb, k*immb(rsda)</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="92" width="90" height="36" />
    <text x="577" y="110">1 0 1 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="92" width="54" height="36" />
    <text x="415" y="110">1 0 p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="92" width="18" height="36" />
    <text x="109" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="92" width="18" height="36" />
    <text x="91" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="92" width="36" height="36" />
    <text x="640" y="110">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="90" width="180" height="20" />
    <text x="208" y="100">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="90" width="90" height="20" />
    <text x="343" y="100">rsda</text>
  
  
  <text x="662" y="100" style="text-anchor:start;font-family=monospace;font-size:small;"> addi    rsda, rsda, k*imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="110" width="90" height="20" />
    <text x="343" y="120">rsda</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="110" width="90" height="20" />
    <text x="487" y="120">rdb</text>
    
  
  
  <text x="662" y="120" style="text-anchor:start;font-family=monospace;font-size:small;"> load    rdb, 0(rsda)</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="142" width="90" height="36" />
    <text x="577" y="160">1 0 1 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="142" width="54" height="36" />
    <text x="415" y="160">1 0 p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="142" width="18" height="36" />
    <text x="109" y="160">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="142" width="18" height="36" />
    <text x="91" y="160">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="142" width="36" height="36" />
    <text x="640" y="160">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="208" y="140" width="90" height="20" />
    <text x="253" y="150">rsda</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="140" width="90" height="20" />
    <text x="343" y="150">rs1a</text>
  
  
  <text x="662" y="150" style="text-anchor:start;font-family=monospace;font-size:small;"> shXadd  rsda, rs1a, rsda</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="160" width="90" height="20" />
    <text x="163" y="170">rs2b</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="208" y="160" width="90" height="20" />
    <text x="253" y="170">rsda</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="160" width="90" height="20" />
    <text x="487" y="170">immb</text>
    
  
  
  <text x="662" y="170" style="text-anchor:start;font-family=monospace;font-size:small;"> store   rs2b, k*immb(rsda)</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="192" width="90" height="36" />
    <text x="577" y="210">1 0 1 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="192" width="54" height="36" />
    <text x="415" y="210">1 0 p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="192" width="18" height="36" />
    <text x="109" y="210">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="192" width="18" height="36" />
    <text x="91" y="210">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="192" width="36" height="36" />
    <text x="640" y="210">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="190" width="180" height="20" />
    <text x="208" y="200">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="190" width="90" height="20" />
    <text x="343" y="200">rsda</text>
  
  
  <text x="662" y="200" style="text-anchor:start;font-family=monospace;font-size:small;"> addi    rsda, rsda, k*imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="210" width="90" height="20" />
    <text x="343" y="220">rsda</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="442" y="210" width="90" height="20" />
    <text x="487" y="220">rs2b</text>
    
  
  
  <text x="662" y="220" style="text-anchor:start;font-family=monospace;font-size:small;"> store   rs2b, 0(rsda)</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>addi</th>
   </tr>
  
  
  <tr><th>ld</th>
  
    <td>0</td>
    
   </tr>
  
  
  <tr><th>sd</th>
  
    <td>1</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>addi</th>
   </tr>
  
  
  <tr><th>lw</th>
  
    <td>2</td>
    
   </tr>
  
  
  <tr><th>sw</th>
  
    <td>3</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>sh3add</th>
   </tr>
  
  
  <tr><th>ld</th>
  
    <td>4</td>
    
   </tr>
  
  
  <tr><th>sd</th>
  
    <td>5</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>sh2add</th>
   </tr>
  
  
  <tr><th>lw</th>
  
    <td>6</td>
    
   </tr>
  
  
  <tr><th>sw</th>
  
    <td>7</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai">Notes and observations (mostly AI)</h4>
<ul>
  <li>The addi rows access AT the bumped pointer: at genuine (non- prologue) surviving-sum sites the memory offset is zero 68-78% of the time, so the rows spend no immb and give the bump the freed column instead – a 10-bit width-scaled imma, which the op declares at no extra codepoints. Even so the bump population is structurally wide (record-sized walks: 10-bit scaled fit is 39-59%); this is the affordable ceiling, not full coverage.</li>
  <li>The shXadd rows keep the 5-bit scaled immb: their stride is the register, so the offset field still earns its column.</li>
  <li>The shXadd form is the scaled POINTER WALK, <code>shXadd rsda, rs1a, rsda</code> (Zba: rd = rs2 + (rs1 « X)) – the pointer advanced by a scaled stride, which is what “advance a pointer” means and the form the corpus emits (its in-place-scaling sibling <code>shXadd a, a, x</code> measured 69 scheduled pairs on sqlite-rv64 against this form’s 210; one row decodes one operand binding, so rules.py admits only this one). The row is drawn at the STANDARD Zba read ports: the shifted stride <code>rs1a</code> in the rs1 column, the pointer <code>rsda</code> in rs2 – Zba’s rs2 position. B needs no port for its base at all: a pre-increment accesses through the UPDATED pointer, which is A’s result, forwarded inside the packet – so nothing competes with A for the columns and the operand-position discipline holds without a trade.
    <h3 id="post-inc-pair">post-inc-pair</h3>
    <p>Access through a pointer, then advance it (post-increment).</p>
  </li>
</ul>

<svg width="100%" height="130" viewbox="0 0 800 130">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 1 0 0 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">1 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rsda</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="40" width="90" height="20" />
    <text x="487" y="50">rda</text>
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> load    rda, k*imma(rsda)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">immb</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="60" width="90" height="20" />
    <text x="343" y="70">rsda</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> addi    rsda, rsda, k*immb</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="92" width="90" height="36" />
    <text x="577" y="110">1 1 0 0 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="92" width="54" height="36" />
    <text x="415" y="110">1 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="92" width="18" height="36" />
    <text x="109" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="92" width="18" height="36" />
    <text x="91" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="92" width="36" height="36" />
    <text x="640" y="110">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="90" width="90" height="20" />
    <text x="253" y="100">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="90" width="90" height="20" />
    <text x="343" y="100">rsda</text>
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="90" width="90" height="20" />
    <text x="487" y="100">imma</text>
  
  
  <text x="662" y="100" style="text-anchor:start;font-family=monospace;font-size:small;"> store   rs2a, k*imma(rsda)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="110" width="90" height="20" />
    <text x="163" y="120">immb</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="110" width="90" height="20" />
    <text x="343" y="120">rsda</text>
    
  
  
  <text x="662" y="120" style="text-anchor:start;font-family=monospace;font-size:small;"> addi    rsda, rsda, k*immb</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>ld</th>
  
    
    <th>sd</th>
   </tr>
  
  
  <tr><th>addi</th>
  
    <td>0</td>
    
  
    <td>1</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>lw</th>
  
    
    <th>sw</th>
   </tr>
  
  
  <tr><th>addi</th>
  
    <td>2</td>
    
  
    <td>3</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-1">Notes and observations (mostly AI)</h4>
<ul>
  <li>Timing oddity here because reg in <code>rd</code> field is written in first cycle not second.</li>
  <li>No shXadd clusters: a post-increment by a register-held stride is a real idiom, but neither clang nor GCC emits it adjacent to the access (zero scheduled pairs on every corpus).</li>
  <li>Both fields earn their columns, unlike pre-inc: the access offset imma is the position inside an unrolled window (small, 67-74% within +/-16) and the stride immb is the window size (wide). They decouple under unrolling – stride = offset + width holds at only 3-19% – so neither a zero offset nor a delta encoding works here.
    <h3 id="mem-sp-pair">mem-sp-pair</h3>
    <p>Two adjacent stack accesses one word apart – a spill or reload pair.</p>
  </li>
</ul>

<svg width="100%" height="130" viewbox="0 0 800 130">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 1 0 0 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">1 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="40" width="180" height="20" />
    <text x="208" y="50">imm</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rda</text>
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">sp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> load    rda, k*imm(sp)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="60" width="180" height="20" />
    <text x="208" y="70">imm</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">rdb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">sp</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> load    rdb, k*imm+k(sp)</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="92" width="90" height="36" />
    <text x="577" y="110">1 1 0 0 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="92" width="54" height="36" />
    <text x="415" y="110">1 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="92" width="18" height="36" />
    <text x="109" y="110">0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="92" width="18" height="36" />
    <text x="91" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="92" width="36" height="36" />
    <text x="640" y="110">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="90" width="90" height="20" />
    <text x="163" y="100">imm[9:5]</text>
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="90" width="90" height="20" />
    <text x="487" y="100">imm[4:0]</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="90" width="90" height="20" />
    <text x="253" y="100">rs2a</text>
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="90" width="40" height="20" />
    <text x="20" y="100">sp</text>
    
  
  
  <text x="662" y="100" style="text-anchor:start;font-family=monospace;font-size:small;"> store   rs2a, k*imm(sp)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="110" width="90" height="20" />
    <text x="163" y="120">imm[9:5]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="110" width="90" height="20" />
    <text x="487" y="120">imm[4:0]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="110" width="90" height="20" />
    <text x="343" y="120">rs2b</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="110" width="40" height="20" />
    <text x="20" y="120">sp</text>
    
  
  
  <text x="662" y="120" style="text-anchor:start;font-family=monospace;font-size:small;"> store   rs2b, k*imm+k(sp)</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  <tr><th>slot A</th><th>slot B</th><th></th></tr>
  
  <tr>
  <td>lx</td><td>lx</td><td>0</td>
  </tr>
  
  <tr>
  <td>sx</td><td>sx</td><td>1</td>
  </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-2">Notes and observations (mostly AI)</h4>
<ul>
  <li>both opcodes in a pair must be identical operations</li>
  <li>offsets differ by one data width, as in mem-base-pair
    <h3 id="mem-base-pair">mem-base-pair</h3>
    <p>Two adjacent accesses through one base register, one data width apart.</p>
  </li>
</ul>

<svg width="100%" height="130" viewbox="0 0 800 130">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 0 1 1 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">1 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="118" y="40" width="90" height="20" />
    <text x="163" y="50">rda</text>
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">imm</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rbase</text>
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> load    rda, k*imm(rbase)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="60" width="90" height="20" />
    <text x="253" y="70">imm</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="60" width="90" height="20" />
    <text x="343" y="70">rbase</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">rdb</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> load    rdb, k*imm+k(rbase)</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="92" width="90" height="36" />
    <text x="577" y="110">1 0 1 1 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="92" width="54" height="36" />
    <text x="415" y="110">1 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="92" width="18" height="36" />
    <text x="109" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="92" width="18" height="36" />
    <text x="91" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="92" width="36" height="36" />
    <text x="640" y="110">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="90" width="90" height="20" />
    <text x="253" y="100">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="90" width="90" height="20" />
    <text x="343" y="100">rbase</text>
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="90" width="90" height="20" />
    <text x="487" y="100">imm</text>
  
  
  <text x="662" y="100" style="text-anchor:start;font-family=monospace;font-size:small;"> store   rs2a, k*imm(rbase)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="110" width="90" height="20" />
    <text x="163" y="120">rs2b</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="110" width="90" height="20" />
    <text x="343" y="120">rbase</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="110" width="90" height="20" />
    <text x="487" y="120">imm</text>
    
  
  
  <text x="662" y="120" style="text-anchor:start;font-family=monospace;font-size:small;"> store   rs2b, k*imm+k(rbase)</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  <tr><th>slot A</th><th>slot B</th><th></th></tr>
  
  <tr>
  <td>lbu</td><td>lbu</td><td>0</td>
  </tr>
  
  <tr>
  <td>lhu</td><td>lhu</td><td>1</td>
  </tr>
  
  <tr>
  <td>lw</td><td>lw</td><td>2</td>
  </tr>
  
  <tr>
  <td>ld</td><td>ld</td><td>3</td>
  </tr>
  
  <tr>
  <td>sb</td><td>sb</td><td>4</td>
  </tr>
  
  <tr>
  <td>sh</td><td>sh</td><td>5</td>
  </tr>
  
  <tr>
  <td>sw</td><td>sw</td><td>6</td>
  </tr>
  
  <tr>
  <td>sd</td><td>sd</td><td>7</td>
  </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-3">Notes and observations (mostly AI)</h4>
<ul>
  <li>both opcodes in a pair must be identical operations</li>
  <li>No <code>lb</code>, <code>lh</code> or <code>lwu</code>: they account for 12 of 37816 scheduled slots across musl-rv32 and sqlite-rv64.
    <h3 id="load-alu-chain">load-alu-chain</h3>
    <p>Load a value and immediately compute with it.</p>
  </li>
</ul>

<svg width="100%" height="130" viewbox="0 0 800 130">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">0 1 1 0 p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">tmp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> load    tmp, k*imma(rs1a)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">rs2b</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">rdb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">tmp</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> alu     rdb, tmp, rs2b</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="92" width="90" height="36" />
    <text x="577" y="110">0 1 1 0 p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="92" width="54" height="36" />
    <text x="415" y="110">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="92" width="18" height="36" />
    <text x="109" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="92" width="18" height="36" />
    <text x="91" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="92" width="36" height="36" />
    <text x="640" y="110">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="90" width="90" height="20" />
    <text x="253" y="100">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="90" width="90" height="20" />
    <text x="343" y="100">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="90" width="40" height="20" />
    <text x="20" y="100">tmp</text>
    
  
  
  <text x="662" y="100" style="text-anchor:start;font-family=monospace;font-size:small;"> load    tmp, k*imma(rs1a)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="110" width="90" height="20" />
    <text x="163" y="120">immb</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="110" width="90" height="20" />
    <text x="487" y="120">rdb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="110" width="40" height="20" />
    <text x="20" y="120">tmp</text>
    
  
  
  <text x="662" y="120" style="text-anchor:start;font-family=monospace;font-size:small;"> alu     rdb, tmp, immb</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>lw<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    
    <th>ld<span style="font-weight:normal;">&nbsp;&times;2</span></th>
   </tr>
  
  
  <tr><th>addi<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    <td>0</td>
    
  
    <td>2</td>
    
   </tr>
  
  
  <tr><th>add</th>
  
    <td>8</td>
    
  
    <td>10</td>
    
   </tr>
  
  
  <tr><th>addw</th>
  
    <td>12</td>
    
  
    <td>14</td>
    
   </tr>
  
  
  <tr><th>and</th>
  
    <td>16</td>
    
  
    <td>18</td>
    
   </tr>
  
  
  <tr><th>andi</th>
  
    <td>20</td>
    
  
    <td>22</td>
    
   </tr>
  
  
  <tr><th>maxu</th>
  
    <td>24</td>
    
  
    <td>26</td>
    
   </tr>
  
  
  <tr><th>or</th>
  
    <td>28</td>
    
  
    <td>30</td>
    
   </tr>
  
  
  <tr><th>slli</th>
  
    <td>32</td>
    
  
    <td>34</td>
    
   </tr>
  
  
  <tr><th>sltiu</th>
  
    <td>36</td>
    
  
    <td>38</td>
    
   </tr>
  
  
  <tr><th>sltu</th>
  
    <td>40</td>
    
  
    <td>42</td>
    
   </tr>
  
  
  <tr><th>srli</th>
  
    <td>44</td>
    
  
    <td>46</td>
    
   </tr>
  
  
  <tr><th>srliw</th>
  
    <td>48</td>
    
  
    <td>50</td>
    
   </tr>
  
  
  <tr><th>sub</th>
  
    <td>52</td>
    
  
    <td>54</td>
    
   </tr>
  
  
  <tr><th>xor</th>
  
    <td>56</td>
    
  
    <td>58</td>
    
   </tr>
  
  
  <tr><th>xori</th>
  
    <td>60</td>
    
  
    <td>62</td>
    
   </tr>
  
</table>


</div>
<h3 id="alu-store-chain">alu-store-chain</h3>
<p>Compute a value and store it.</p>

<svg width="100%" height="130" viewbox="0 0 800 130">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 0 0 1 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">tmp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> alu     tmp, rs1a, rs2a</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">rs1b</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">immb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">tmp</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> store   tmp, k*immb(rs1b)</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="92" width="90" height="36" />
    <text x="577" y="110">1 0 0 1 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="92" width="54" height="36" />
    <text x="415" y="110">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="92" width="18" height="36" />
    <text x="109" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="92" width="18" height="36" />
    <text x="91" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="92" width="36" height="36" />
    <text x="640" y="110">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="90" width="90" height="20" />
    <text x="253" y="100">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="90" width="90" height="20" />
    <text x="343" y="100">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="90" width="40" height="20" />
    <text x="20" y="100">tmp</text>
    
  
  
  <text x="662" y="100" style="text-anchor:start;font-family=monospace;font-size:small;"> alu     tmp, rs1a, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="110" width="90" height="20" />
    <text x="163" y="120">rs1b</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="110" width="90" height="20" />
    <text x="487" y="120">immb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="110" width="40" height="20" />
    <text x="20" y="120">tmp</text>
    
  
  
  <text x="662" y="120" style="text-anchor:start;font-family=monospace;font-size:small;"> store   tmp, k*immb(rs1b)</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>addi<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    
    <th>add</th>
  
    
    <th>addw</th>
  
    
    <th>and</th>
  
    
    <th>andi</th>
  
    
    <th>maxu</th>
  
    
    <th>or</th>
  
    
    <th>slli</th>
  
    
    <th>sltiu</th>
  
    
    <th>sltu</th>
  
    
    <th>srli</th>
  
    
    <th>srliw</th>
  
    
    <th>sub</th>
  
    
    <th>xor</th>
  
    
    <th>xori</th>
   </tr>
  
  
  <tr><th>sw</th>
  
    <td>0</td>
    
  
    <td>2</td>
    
  
    <td>3</td>
    
  
    <td>4</td>
    
  
    <td>5</td>
    
  
    <td>6</td>
    
  
    <td>7</td>
    
  
    <td>8</td>
    
  
    <td>9</td>
    
  
    <td>10</td>
    
  
    <td>11</td>
    
  
    <td>12</td>
    
  
    <td>13</td>
    
  
    <td>14</td>
    
  
    <td>15</td>
    
   </tr>
  
  
  <tr><th>sd</th>
  
    <td>16</td>
    
  
    <td>18</td>
    
  
    <td>19</td>
    
  
    <td>20</td>
    
  
    <td>21</td>
    
  
    <td>22</td>
    
  
    <td>23</td>
    
  
    <td>24</td>
    
  
    <td>25</td>
    
  
    <td>26</td>
    
  
    <td>27</td>
    
  
    <td>28</td>
    
  
    <td>29</td>
    
  
    <td>30</td>
    
  
    <td>31</td>
    
   </tr>
  
</table>


</div>
<h3 id="addi-store-chain">addi-store-chain</h3>
<p>Form a value – constant, copy or sp-relative address – and store it.</p>

<svg width="100%" height="80" viewbox="0 0 800 80">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 1 0 0 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">1 0 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="40" width="180" height="20" />
    <text x="208" y="50">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">tmp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> addi    tmp, rs1a, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">rbase</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">tmp</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> store   tmp, 0(rbase)</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>addi</th>
   </tr>
  
  
  <tr><th>sb</th>
  
    <td>0</td>
    
   </tr>
  
  
  <tr><th>sh</th>
  
    <td>1</td>
    
   </tr>
  
  
  <tr><th>sw</th>
  
    <td>2</td>
    
   </tr>
  
  
  <tr><th>sd</th>
  
    <td>3</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-4">Notes and observations (mostly AI)</h4>
<ul>
  <li>The data width comes from the op list (sb/sh/sw/sd), as in the other memory frames, rather than a width field – 4 codepoints is cheaper than two bits of layout, and it matches existing convention. sh captures nothing on this corpus but only 3 sites carry it, which is too thin to conclude it never would.</li>
  <li>A covers li (rs1a = x0), mv (imma = 0) and addi4spn (rs1a = sp) as register/immediate choices, so they need no opcodes of their own.
    <h3 id="addi-store-off-chain">addi-store-off-chain</h3>
    <p>Compute a value from one base and store it at an offset from another.</p>
  </li>
</ul>

<svg width="100%" height="80" viewbox="0 0 800 80">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 0 1 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">0 1 p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">tmp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> addi    tmp, rs1a, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">rbase</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">immb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">tmp</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> store   tmp, k*immb(rbase)</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>addi</th>
   </tr>
  
  
  <tr><th>sb<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    <td>0</td>
    
   </tr>
  
  
  <tr><th>sh<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    <td>2</td>
    
   </tr>
  
  
  <tr><th>sw<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    <td>4</td>
    
   </tr>
  
  
  <tr><th>sd<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    <td>6</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-5">Notes and observations (mostly AI)</h4>
<ul>
  <li><code>immb</code> is width-scaled and unsigned; <code>imma</code> is signed – and that asymmetry is the frame’s whole shape. A memory offset carries an access width, so five bits of <code>immb</code> reach 4x or 8x further; an <code>addi</code> addend is pointer arithmetic and carries none, so its bits are bytes. <code>load5-load5-chain</code> gets a symmetric split because BOTH its immediates are scaled offsets; this frame cannot, and scaling A by the store’s k would not help – the addends are 93% ODD, only 28.8% are aligned to their own store’s width, and for <code>sw</code> (65% of the population) it is 5 of 295. The stored value is the chain temporary and is not encoded.</li>
  <li>THE WIDTH BELONGS TO B, measured 2026-08-04. The row draws five bits per column, so a sixth costs a doubling. Over 455 scheduled pairs on sqlite-rv32/rv64 and cpp-rv32/rv64: A’s addend fits FIVE bits 97% of the time and two bits 95% of the time, while B’s scaled offset fits five only 33% of the time and six 100%. So A was paying 8 codepoints for the 2% that need a sixth bit. Dropped to 5+6: 16 codepoints to 8, at a cost of 5 pairs on sqlite-rv32 and 4 on sqlite-rv64. Narrowing B too (5+5, 4cp) costs a further 135 and 130 – that bit is real.</li>
  <li>This is a SQLITE-shaped frame: 212 hits on sqlite-rv32 against 12 on cpp-rv32. A cpp-only reading makes it look like the worst frame in the encoding; it is not.</li>
  <li>rules.py EXCLUDES the li form (rs1a = x0) here even though the row draws rs1a and could encode it: li + store belongs to the frames that price it (addi-store-chain at offset zero, alu-store-chain up to a 5-bit offset). The residue – li + store at an offset only this frame’s sixth bit reaches – stays solo.
    <h3 id="load-store-chain">load-store-chain</h3>
    <p>Copy a value from one memory location to another.</p>
  </li>
</ul>

<svg width="100%" height="80" viewbox="0 0 800 80">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 0 1 0 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">1 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">tmp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> load    tmp, k*imma(rs1a)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">rs1b</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">immb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">tmp</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> store   tmp, k*immb(rs1b)</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  <tr><th>slot A</th><th>slot B</th><th></th></tr>
  
  <tr>
  <td>lbu</td><td>sb<span style="font-weight:normal;">&nbsp;&times;4</span></td><td>0</td>
  </tr>
  
  <tr>
  <td>lhu</td><td>sh<span style="font-weight:normal;">&nbsp;&times;4</span></td><td>4</td>
  </tr>
  
  <tr>
  <td>lw</td><td>sw<span style="font-weight:normal;">&nbsp;&times;4</span></td><td>8</td>
  </tr>
  
  <tr>
  <td>ld</td><td>sd<span style="font-weight:normal;">&nbsp;&times;4</span></td><td>12</td>
  </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-6">Notes and observations (mostly AI)</h4>
<ul>
  <li>Both offsets are width-scaled and unsigned; the loaded value is the chain temporary and is not encoded.
    <h3 id="load0-load10-chain">load0-load10-chain</h3>
    <p>Pointer chase: bare first load, the second carries a wide offset.</p>
  </li>
</ul>

<svg width="100%" height="80" viewbox="0 0 800 80">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 1 0 0 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">0 0 p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">tmp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> lx      tmp, 0(rs1a)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="60" width="180" height="20" />
    <text x="208" y="70">immb</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">rdb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">tmp</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> load    rdb, k*immb(tmp)</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>lx</th>
   </tr>
  
  
  <tr><th>lb</th>
  
    <td>0</td>
    
   </tr>
  
  
  <tr><th>lbu</th>
  
    <td>1</td>
    
   </tr>
  
  
  <tr><th>lh</th>
  
    <td>2</td>
    
   </tr>
  
  
  <tr><th>lhu</th>
  
    <td>3</td>
    
   </tr>
  
  
  <tr><th>lw</th>
  
    <td>4</td>
    
   </tr>
  
  
  <tr><th>lwu</th>
  
    <td>5</td>
    
   </tr>
  
  
  <tr><th>ld</th>
  
    <td>6</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-7">Notes and observations (mostly AI)</h4>
<ul>
  <li>The A slot spends ONE opcode, not seven. <code>must_chain_base</code> makes A’s loaded value B’s base address, and a byte or halfword is not an address – so A is the natural word by construction. Measured over every chain the pairer can form, all 11583 of them across the suite, A is <code>lw</code> on RV32 and <code>ld</code> on RV64 100.0% of the time, with no exception on or off the axes. That is what <code>lx</code> names, and it takes the block from 7x7=49 codepoints to 1x7=7.</li>
  <li><code>immb</code> gets the full ten bits – five from <code>funct5</code>, five from <code>rs2</code>, fields the pair leaves free because <code>tmp</code> is implicit and A’s offset is pinned at zero. This single form is 59.9% of all chases.</li>
  <li>Split from a frame that drew BOTH this row and its sibling’s over one 49-codepoint op-select, with nothing selecting between them: a decoder holding the word could not tell whether the field was the first load’s offset or the second’s. That frame’s standing TODO – balance imma against immb – is answered in results/corpus/CHAINS.md.
    <h3 id="load5-load5-chain">load5-load5-chain</h3>
    <p>Pointer chase with BOTH loads offset: a pointer in a slot, then indexed.</p>
  </li>
</ul>

<svg width="100%" height="80" viewbox="0 0 800 80">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 1 0 0 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">0 1 p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">tmp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> lx      tmp, k*imma(rs1a)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">immb</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">rdb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">tmp</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> load    rdb, k*immb(tmp)</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>lx</th>
   </tr>
  
  
  <tr><th>lb</th>
  
    <td>0</td>
    
   </tr>
  
  
  <tr><th>lbu</th>
  
    <td>1</td>
    
   </tr>
  
  
  <tr><th>lh</th>
  
    <td>2</td>
    
   </tr>
  
  
  <tr><th>lhu</th>
  
    <td>3</td>
    
   </tr>
  
  
  <tr><th>lw</th>
  
    <td>4</td>
    
   </tr>
  
  
  <tr><th>lwu</th>
  
    <td>5</td>
    
   </tr>
  
  
  <tr><th>ld</th>
  
    <td>6</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-8">Notes and observations (mostly AI)</h4>
<ul>
  <li>The offset-bearing sibling of load0-load10-chain, on the pattern of addi-store-off-chain. It replaces an earlier <code>deref-load-chain</code>, whose population (offset on the FIRST load, second at zero) is the immb=0 column here – 2397 of its 2425 chases, the 28 lost being those needing more than five bits of <code>imma</code>.</li>
  <li>The ten free bits are split evenly because the corpus says so, not for symmetry. load0-load10-chain has already absorbed the whole imma=0 row, so what is left to catch is diagonal mass, and it is spread: measured corpus totals are 5+5 505255, 6+4 505073, 4+6 505032, 7+3 504522, 3+7 504635. Eleven bits (5+6) would reach ~10977 chases and cost an opcode doubling for ~154 pairs.</li>
  <li><code>rs1a</code> = sp is what makes this frame necessary rather than a rounding error. An sp-based chase – a pointer read out of a stack slot, then indexed – is 58% both-offsets-nonzero and 1% B-only, against 20% and 60% for a non-sp chase. The slot displacement is an A offset by construction.</li>
  <li>The two frames are disjoint by construction: this one demands imma be nonzero and its sibling demands it be zero, so no chase is encodable both ways and neither shadows the other.
    <h3 id="load-base-branch-pair">load-base-branch-pair</h3>
    <p>Load a value and branch on whether it is zero; the value survives.</p>
  </li>
</ul>

<svg width="100%" height="80" viewbox="0 0 800 80">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 0 1 0 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">0 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="118" y="40" width="90" height="20" />
    <text x="163" y="50">rda</text>
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rs1a</text>
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> load    rda, k*imma(rs1a)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">rda</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">immb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">zero</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> beqz/bnez rda, zero, 4*immb</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>lb</th>
  
    
    <th>lbu</th>
  
    
    <th>lh</th>
  
    
    <th>lhu</th>
  
    
    <th>lw</th>
  
    
    <th>lwu</th>
  
    
    <th>ld</th>
   </tr>
  
  
  <tr><th>beqz</th>
  
    <td>0</td>
    
  
    <td>1</td>
    
  
    <td>2</td>
    
  
    <td>3</td>
    
  
    <td>4</td>
    
  
    <td>5</td>
    
  
    <td>6</td>
    
   </tr>
  
  
  <tr><th>bnez</th>
  
    <td>7</td>
    
  
    <td>8</td>
    
  
    <td>9</td>
    
  
    <td>10</td>
    
  
    <td>11</td>
    
  
    <td>12</td>
    
  
    <td>13</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-9">Notes and observations (mostly AI)</h4>
<ul>
  <li><code>immb</code> is the branch displacement, a 5-bit field. Displacements are unresolved labels in the corpus, so their fit is unmeasured.
    <h3 id="load-sp-branch-pair">load-sp-branch-pair</h3>
    <p>Load a stack slot and branch on whether it is zero; the value survives.</p>
  </li>
</ul>

<svg width="100%" height="80" viewbox="0 0 800 80">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 0 1 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">0 0 p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="40" width="180" height="20" />
    <text x="208" y="50">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rda</text>
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">sp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> load    rda, k*imma(sp)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="298" y="60" width="90" height="20" />
    <text x="343" y="70">rda</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">immb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">zero</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> beqz/bnez rda, zero, 4*immb</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>lw</th>
  
    
    <th>ld</th>
  
    
    <th>lbu</th>
   </tr>
  
  
  <tr><th>beqz</th>
  
    <td>0</td>
    
  
    <td>1</td>
    
  
    <td>2</td>
    
   </tr>
  
  
  <tr><th>bnez</th>
  
    <td>3</td>
    
  
    <td>4</td>
    
  
    <td>5</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-10">Notes and observations (mostly AI)</h4>
<ul>
  <li><code>immb</code> as in load-base-branch-pair: unresolved, fit unmeasured.
    <h3 id="inc-branch-pair">inc-branch-pair</h3>
    <p>Step a loop counter by one and branch on the comparison.</p>
  </li>
</ul>

<svg width="100%" height="80" viewbox="0 0 800 80">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 0 1 1 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">0 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rsda</text>
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> inc/dec  rsda</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">immb[9:5]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">immb[4:0]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="60" width="90" height="20" />
    <text x="253" y="70">rs2b</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="60" width="90" height="20" />
    <text x="343" y="70">rsda</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> bXX     rsda, rs2b, 4*immb</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>inc</th>
   </tr>
  
  
  <tr><th>beq</th>
  
    <td>0</td>
    
   </tr>
  
  
  <tr><th>bne</th>
  
    <td>1</td>
    
   </tr>
  
  
  <tr><th>blt</th>
  
    <td>2</td>
    
   </tr>
  
  
  <tr><th>bltu</th>
  
    <td>3</td>
    
   </tr>
  
  
  <tr><th>bltu_r</th>
  
    <td>4</td>
    
   </tr>
  
  
  <tr><th>bge</th>
  
    <td>5</td>
    
   </tr>
  
  
  <tr><th>bgeu</th>
  
    <td>6</td>
    
   </tr>
  
  
  <tr><th>bgeu_r</th>
  
    <td>7</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>dec</th>
   </tr>
  
  
  <tr><th>beq</th>
  
    <td>8</td>
    
   </tr>
  
  
  <tr><th>bne</th>
  
    <td>9</td>
    
   </tr>
  
  
  <tr><th>blt</th>
  
    <td>10</td>
    
   </tr>
  
  
  <tr><th>bltu</th>
  
    <td>11</td>
    
   </tr>
  
  
  <tr><th>bltu_r</th>
  
    <td>12</td>
    
   </tr>
  
  
  <tr><th>bge_r</th>
  
    <td>13</td>
    
   </tr>
  
  
  <tr><th>bgeu</th>
  
    <td>14</td>
    
   </tr>
  
  
  <tr><th>bgeu_r</th>
  
    <td>15</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-11">Notes and observations (mostly AI)</h4>
<ul>
  <li>The step is +/-1, implied by the opcode (<code>inc</code>/<code>dec</code> = <code>addi rsda, rsda, +/-1</code>): 88% of adjacent counter-branch sites compare against a REGISTER, so the immediate column goes to <code>rs2b</code> instead of a step field; <code>rs2b = x0</code> gives every vs-zero form for free. Full XLEN width only – there are no <code>w</code> forms.</li>
  <li><code>_r</code> marks the operand-reversed spelling (the counter in rs2). The two clusters are the best sixteen JOINT direction x mode cells of the adjacent-site census, not a mode product: down-loops are bltu/bgeu-heavy (pointer-vs-limit, both operand orders), up-loops beq/bne with bge/bgeu sum-first. Joint enumeration covers 98.7% of adjacent sites against ~79% for the best 4-mode x 2-direction product at the same sixteen entries.</li>
  <li>The scheduler also matches <code>addiw rsd, rsd, +/-1</code> and bills it here. That is optimistic for unsigned int counters on rv64 (defined wrap is not width-equivalent) in the same spirit as RVC- eligibility; signed counters are provably width-equivalent (overflow is UB), so a packet-targeted compiler emits <code>addi</code>.</li>
  <li><code>immb</code> is the branch displacement in packets. Displacements are unresolved labels in the corpus, so pairwise fit is unmeasured; the label-distance study puts 10-bit fit near 100%.
    <h3 id="bit-test-branch-chain">bit-test-branch-chain</h3>
    <p>Test a bit or bit-field and branch on the result.</p>
  </li>
</ul>

<svg width="100%" height="130" viewbox="0 0 800 130">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 0 0 0 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">tmp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> andi    tmp, rs1a, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">immb[9:5]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">immb[4:0]</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">tmp</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> beqz/bnez tmp, 4*immb</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="92" width="90" height="36" />
    <text x="577" y="110">1 0 0 0 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="92" width="54" height="36" />
    <text x="415" y="110">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="92" width="18" height="36" />
    <text x="109" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="92" width="18" height="36" />
    <text x="91" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="92" width="36" height="36" />
    <text x="640" y="110">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="90" width="90" height="20" />
    <text x="253" y="100">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="90" width="90" height="20" />
    <text x="343" y="100">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="90" width="40" height="20" />
    <text x="20" y="100">tmp</text>
    
  
  
  <text x="662" y="100" style="text-anchor:start;font-family=monospace;font-size:small;"> slli/srli tmp, rs1a, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="110" width="90" height="20" />
    <text x="163" y="120">immb[9:5]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="110" width="90" height="20" />
    <text x="487" y="120">immb[4:0]</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="110" width="40" height="20" />
    <text x="20" y="120">tmp</text>
    
  
  
  <text x="662" y="120" style="text-anchor:start;font-family=monospace;font-size:small;"> beqz/bnez tmp, 4*immb</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>andi<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    
    <th>slli<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    
    <th>srli<span style="font-weight:normal;">&nbsp;&times;2</span></th>
   </tr>
  
  
  <tr><th>beqz</th>
  
    <td>0</td>
    
  
    <td>2</td>
    
  
    <td>4</td>
    
   </tr>
  
  
  <tr><th>bnez</th>
  
    <td>6</td>
    
  
    <td>8</td>
    
  
    <td>10</td>
    
   </tr>
  
  
  <tr><th>beq</th>
  
    <td>12</td>
    
  
    <td>14</td>
    
  
    <td>16</td>
    
   </tr>
  
  
  <tr><th>bne</th>
  
    <td>18</td>
    
  
    <td>20</td>
    
  
    <td>22</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-12">Notes and observations (mostly AI)</h4>
<ul>
  <li>The shift forms are the E1/E2 rewrite targets (low-mask and high-mask zero tests); a single-bit sign test via <code>slli</code> + <code>blt tmp, zero</code> is equivalence E5, a candidate this frame does NOT yet encode – its b list has no blt/bge – and rules.py matches accordingly.
    <h3 id="li-branch-chain">li-branch-chain</h3>
    <p>Compare a register against a constant and branch.</p>
  </li>
</ul>

<svg width="100%" height="80" viewbox="0 0 800 80">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">0 1 1 1 p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">imma</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">tmp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> li      tmp, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">immb[9:5]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">immb[4:0]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="60" width="90" height="20" />
    <text x="343" y="70">rs1b</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">tmp</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> bXX     rs1b, tmp, 4*immb</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>li<span style="font-weight:normal;">&nbsp;&times;8</span></th>
   </tr>
  
  
  <tr><th>beq</th>
  
    <td>0</td>
    
   </tr>
  
  
  <tr><th>bne</th>
  
    <td>8</td>
    
   </tr>
  
  
  <tr><th>blt</th>
  
    <td>16</td>
    
   </tr>
  
  
  <tr><th>bge</th>
  
    <td>24</td>
    
   </tr>
  
  
  <tr><th>bltu</th>
  
    <td>32</td>
    
   </tr>
  
  
  <tr><th>bgeu</th>
  
    <td>40</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-13">Notes and observations (mostly AI)</h4>
<ul>
  <li><code>imma</code> is a 5-bit register column; <code>li</code> declares 8 bits, bought by three opcode doublings (census li fit 66.9% -&gt; 85.3% of 2293, ~13 pairs/codepoint for the extra 32).</li>
  <li>The row spells the constant in rs2. A site with the constant on the LEFT of an asymmetric compare (<code>blt tmp, rs</code>) is still encodable via the dead-tmp rewrite <code>bXX K, rs</code> -&gt; <code>bYY rs, K+1</code> (blt&lt;-&gt;bge, bltu&lt;-&gt;bgeu) – tmp carries only the comparison constant and dies at B, so changing its value is licensed. rules.py accepts those sites and rejects the two edge cases the rewrite cannot reach: K at the top of the field (K+1 overflows) and K = -1 under an unsigned compare (the predicate flips).</li>
  <li>TODO: could replace li with alu op and compare result with zero (mostly?).
    <h3 id="li-czero-chain">li-czero-chain</h3>
    <p>Materialise a constant and conditionally zero it – one arm of a select.</p>
  </li>
</ul>

<svg width="100%" height="80" viewbox="0 0 800 80">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 1 0 0 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">1 1 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="40" width="180" height="20" />
    <text x="208" y="50">imma</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">tmp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> li      tmp, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="60" width="90" height="20" />
    <text x="343" y="70">rs2b</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">rdb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">tmp</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> czero.X rdb, tmp, rs2b</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>li</th>
   </tr>
  
  
  <tr><th>czero.eqz</th>
  
    <td>0</td>
    
   </tr>
  
  
  <tr><th>czero.nez</th>
  
    <td>1</td>
    
   </tr>
  
</table>


</div>
<h3 id="czero-or-chain">czero-or-chain</h3>
<p>Finish a conditional select: merge the surviving arm into the result.</p>

<svg width="100%" height="80" viewbox="0 0 800 80">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 1 0 0 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">1 1 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">tmp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> czero.X tmp, rs1a, rs2a</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">rs2b</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">rdb</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">tmp</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> or      rdb, tmp, rs2b</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>czero.eqz</th>
  
    
    <th>czero.nez</th>
   </tr>
  
  
  <tr><th>or</th>
  
    <td>0</td>
    
  
    <td>1</td>
    
   </tr>
  
</table>


</div>
<h3 id="macro-op-pair">macro-op-pair</h3>
<p>Both halves of ONE computation over the same operands (mul/mulh, div/rem), declared as a pair so an implementation can fuse them.</p>

<svg width="100%" height="130" viewbox="0 0 800 130">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 0 1 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">0 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="118" y="40" width="90" height="20" />
    <text x="163" y="50">rda</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rs1a</text>
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> alu     rda, rs1a, rs2a</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="60" width="90" height="20" />
    <text x="253" y="70">rs2a</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="60" width="90" height="20" />
    <text x="343" y="70">rs1a</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">rdb</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> alu     rdb, rs1a, rs2a</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="92" width="90" height="36" />
    <text x="577" y="110">1 0 1 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="92" width="54" height="36" />
    <text x="415" y="110">0 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="92" width="18" height="36" />
    <text x="109" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="92" width="18" height="36" />
    <text x="91" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="92" width="36" height="36" />
    <text x="640" y="110">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="118" y="90" width="90" height="20" />
    <text x="163" y="100">rda</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="90" width="90" height="20" />
    <text x="253" y="100">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="90" width="90" height="20" />
    <text x="343" y="100">rs1a</text>
  
  
  <text x="662" y="100" style="text-anchor:start;font-family=monospace;font-size:small;"> add     rda, rs1a, rs2a</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="118" y="110" width="90" height="20" />
    <text x="163" y="120">rda</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="110" width="90" height="20" />
    <text x="343" y="120">rs1a</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="110" width="90" height="20" />
    <text x="487" y="120">rdb</text>
    
  
  
  <text x="662" y="120" style="text-anchor:start;font-family=monospace;font-size:small;"> sltu    rdb, rda, rs1a</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>mulh</th>
  
    
    <th>mulhu</th>
  
    
    <th>mulhsu</th>
   </tr>
  
  
  <tr><th>mul</th>
  
    <td>0</td>
    
  
    <td>1</td>
    
  
    <td>2</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>add</th>
  
    
    <th>addw</th>
   </tr>
  
  
  <tr><th>sltu</th>
  
    <td>4</td>
    
  
    <td>5</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>div</th>
   </tr>
  
  
  <tr><th>rem</th>
  
    <td>6</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>divu</th>
   </tr>
  
  
  <tr><th>remu</th>
  
    <td>7</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>divw</th>
   </tr>
  
  
  <tr><th>remw</th>
  
    <td>8</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>divuw</th>
   </tr>
  
  
  <tr><th>remuw</th>
  
    <td>9</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-14">Notes and observations (mostly AI)</h4>
<ul>
  <li>CARRY-OUT, measured (2026-08-04). cpp-rv32 holds 156 carry-shaped adjacencies, godot 36, everything else under five. The cluster takes the frame from 59 hits to 167 on cpp-rv32, but the corpus total rises by 29. The difference is NOT another frame losing the same pairs – <code>alu-alu-chain</code> cannot encode (add, sltu) at all, since <code>sltu</code> appears only in its A sets. It is greedy DISPLACEMENT: claiming the <code>add</code> denies it to whatever was pairing with it from the left, so alu-alu-chain drops 74 elsewhere in the stream. Report the frame’s worth as 29, not 108.</li>
  <li>THE REST OF THE FRAME IS KEPT DESPITE A NEAR-ZERO SCORE. Do not cut the mul/div clusters on pairing-rate evidence; they are not there to earn pairs.</li>
  <li>Every cluster is two halves of ONE computation over the same operands: the low and high words of a multiply, the quotient and remainder of a divide, the sum and difference, the min and the max. Encoding them as a declared pair tells the implementation both results are wanted, so it can FUSE – one pass of the multiplier or divider producing both halves – instead of issuing the operation twice and discarding half of each result. That is a hardware invitation, and it is worth a codepoint block whether or not today’s compilers accept it.</li>
  <li>They mostly do not, yet. Measured over four corpora: 70 scheduled pairs. The ceiling is no higher – adjacent tuple matches with positionally shared operands number 31 on musl-rv32, 25 on cpp- rv32, 2 on sqlite-gcc-rv64, 0 on sqlite-rv64 – so nothing is suppressing it. Notably the frame is NOT register-window constrained: this row draws four full 5-bit fields, so every register encodes.</li>
  <li>ORDER IS NOT ARBITRARY, and it is not a dependency either. The two ops read the same two sources and neither consumes the other’s result, so they commute – but the RISC-V M extension names one sequence as THE fusion idiom, and a microarchitecture told to detect fusable pairs is looking for that one: MULH[[S]U] rdh, rs1, rs2 ; MUL rdl, rs1, rs2 DIV[U] rdq, rs1, rs2 ; REM[U] rdr, rs1, rs2 high half first, quotient first, “source register specifiers must be in the same order and rdh cannot be the same as rs1 or rs2” – that last clause because the fused unit still needs both sources intact when it delivers the second result. <code>_reject_dependence</code> already enforces exactly that (<code>a.rd not in b.uses_regs</code>), so rules.py gets it for free.</li>
  <li>The clusters are listed in the spec’s sequence and the compiler already emits it (hi-first outnumbers lo-first 22:9 on musl-rv32, div-first is universal, and every measured occurrence satisfies the rdh-not-a-source rule). rules.py canonicalises, so both directions still pair; the canonical direction also gets the lighter dependence test, so keep it spec-ordered.</li>
  <li>The gap is a toolchain one – a compiler that knew this pairing were available would emit the two halves adjacently and in order. Treat the low score as a measurement of clang and GCC, not of the frame.
    <h3 id="rsd-alu-pair">rsd-alu-pair</h3>
    <p>Two in-place ALU updates, each rewriting its own source register.</p>
  </li>
</ul>

<svg width="100%" height="230" viewbox="0 0 800 230">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">0 0 p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rsda</text>
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> alu rsda, rsda, rs2a</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">rs2b</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">rsdb</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> alu rsdb, rsdb, rs2b</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="92" width="90" height="36" />
    <text x="577" y="110">0 0 p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="92" width="54" height="36" />
    <text x="415" y="110">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="92" width="18" height="36" />
    <text x="109" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="92" width="18" height="36" />
    <text x="91" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="92" width="36" height="36" />
    <text x="640" y="110">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="90" width="90" height="20" />
    <text x="253" y="100">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="90" width="90" height="20" />
    <text x="343" y="100">rsda</text>
  
  
  <text x="662" y="100" style="text-anchor:start;font-family=monospace;font-size:small;"> alu rsda, rsda, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="110" width="90" height="20" />
    <text x="163" y="120">rs2b</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="442" y="110" width="90" height="20" />
    <text x="487" y="120">rsdb</text>
    
  
  
  <text x="662" y="120" style="text-anchor:start;font-family=monospace;font-size:small;"> alu rsdb, rsdb, rs2b</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="142" width="90" height="36" />
    <text x="577" y="160">0 0 p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="142" width="54" height="36" />
    <text x="415" y="160">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="142" width="18" height="36" />
    <text x="109" y="160">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="142" width="18" height="36" />
    <text x="91" y="160">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="142" width="36" height="36" />
    <text x="640" y="160">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="140" width="90" height="20" />
    <text x="253" y="150">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="140" width="90" height="20" />
    <text x="343" y="150">rsda</text>
  
  
  <text x="662" y="150" style="text-anchor:start;font-family=monospace;font-size:small;"> alu rsda, rsda, rs2a</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="160" width="90" height="20" />
    <text x="163" y="170">immb</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="442" y="160" width="90" height="20" />
    <text x="487" y="170">rsdb</text>
    
  
  
  <text x="662" y="170" style="text-anchor:start;font-family=monospace;font-size:small;"> alu rsdb, rsdb, immb</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="192" width="90" height="36" />
    <text x="577" y="210">0 0 p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="192" width="54" height="36" />
    <text x="415" y="210">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="192" width="18" height="36" />
    <text x="109" y="210">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="192" width="18" height="36" />
    <text x="91" y="210">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="192" width="36" height="36" />
    <text x="640" y="210">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="190" width="90" height="20" />
    <text x="253" y="200">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="190" width="90" height="20" />
    <text x="343" y="200">rsda</text>
  
  
  <text x="662" y="200" style="text-anchor:start;font-family=monospace;font-size:small;"> alu rsda, rsda, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="210" width="90" height="20" />
    <text x="163" y="220">immb</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="442" y="210" width="90" height="20" />
    <text x="487" y="220">rsdb</text>
    
  
  
  <text x="662" y="220" style="text-anchor:start;font-family=monospace;font-size:small;"> alu rsdb, rsdb, immb</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>slli<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    
    <th>addi</th>
  
    
    <th>li</th>
  
    
    <th>addiw</th>
  
    
    <th>andi</th>
  
    
    <th>slliw</th>
  
    
    <th>srli</th>
  
    
    <th>add</th>
  
    
    <th>sub</th>
  
    
    <th>mul</th>
  
    
    <th>or</th>
  
    
    <th>sh1add</th>
  
    
    <th>sh2add</th>
  
    
    <th>sh3add</th>
  
    
    <th>czero.nez</th>
   </tr>
  
  
  <tr><th>li<span style="font-weight:normal;">&nbsp;&times;8</span></th>
  
    <td>0</td>
    
  
    <td>2</td>
    
  
    <td>3</td>
    
  
    <td>4</td>
    
  
    <td>5</td>
    
  
    <td>6</td>
    
  
    <td>7</td>
    
  
    <td>8</td>
    
  
    <td>9</td>
    
  
    <td>10</td>
    
  
    <td>11</td>
    
  
    <td>12</td>
    
  
    <td>13</td>
    
  
    <td>14</td>
    
  
    <td>15</td>
    
   </tr>
  
  
  <tr><th>addi<span style="font-weight:normal;">&nbsp;&times;4</span></th>
  
    <td>128</td>
    
  
    <td>130</td>
    
  
    <td>131</td>
    
  
    <td>132</td>
    
  
    <td>133</td>
    
  
    <td>134</td>
    
  
    <td>135</td>
    
  
    <td>136</td>
    
  
    <td>137</td>
    
  
    <td>138</td>
    
  
    <td>139</td>
    
  
    <td>140</td>
    
  
    <td>141</td>
    
  
    <td>142</td>
    
  
    <td>143</td>
    
   </tr>
  
  
  <tr><th>slli</th>
  
    <td>192</td>
    
  
    <td>194</td>
    
  
    <td>195</td>
    
  
    <td>196</td>
    
  
    <td>197</td>
    
  
    <td>198</td>
    
  
    <td>199</td>
    
  
    <td>200</td>
    
  
    <td>201</td>
    
  
    <td>202</td>
    
  
    <td>203</td>
    
  
    <td>204</td>
    
  
    <td>205</td>
    
  
    <td>206</td>
    
  
    <td>207</td>
    
   </tr>
  
  
  <tr><th>add</th>
  
    <td>208</td>
    
  
    <td>210</td>
    
  
    <td>211</td>
    
  
    <td>212</td>
    
  
    <td>213</td>
    
  
    <td>214</td>
    
  
    <td>215</td>
    
  
    <td>216</td>
    
  
    <td>217</td>
    
  
    <td>218</td>
    
  
    <td>219</td>
    
  
    <td>220</td>
    
  
    <td>221</td>
    
  
    <td>222</td>
    
  
    <td>223</td>
    
   </tr>
  
  
  <tr><th>or</th>
  
    <td>224</td>
    
  
    <td>226</td>
    
  
    <td>227</td>
    
  
    <td>228</td>
    
  
    <td>229</td>
    
  
    <td>230</td>
    
  
    <td>231</td>
    
  
    <td>232</td>
    
  
    <td>233</td>
    
  
    <td>234</td>
    
  
    <td>235</td>
    
  
    <td>236</td>
    
  
    <td>237</td>
    
  
    <td>238</td>
    
  
    <td>239</td>
    
   </tr>
  
  
  <tr><th>czero.eqz</th>
  
    <td>240</td>
    
  
    <td>242</td>
    
  
    <td>243</td>
    
  
    <td>244</td>
    
  
    <td>245</td>
    
  
    <td>246</td>
    
  
    <td>247</td>
    
  
    <td>248</td>
    
  
    <td>249</td>
    
  
    <td>250</td>
    
  
    <td>251</td>
    
  
    <td>252</td>
    
  
    <td>253</td>
    
  
    <td>254</td>
    
  
    <td>255</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-15">Notes and observations (mostly AI)</h4>
<ul>
  <li>The four register operands occupy the four 5-bit columns – 20 bits, the whole operand budget – so registers here are a FULL 5-bit field, x0..x31. An earlier draft anticipated cutting them to 4 bits; that is not needed and was never adopted.</li>
  <li>The two slots declare DIFFERENT op sets, and deliberately. Range past the row’s five drawn bits is bought in opcode entries – an op declaring N bits occupies 2^(N-5) of them – so weight, not op count, is the budget, and one bit on <code>li</code> costs what four reg-reg opcodes cost. A weighs 16 as fifteen ops that are nearly all weight 1 (breadth); B weighs 16 as six ops, of which <code>li</code> at eight bits is 8 and <code>addi</code> at seven is 4 (depth). The block is 16 x 16 = 256, the same as the symmetric set it replaces.</li>
  <li>The asymmetry is only purchasable because the pair is ORDER-FREE in 87.1% of the corpus residue: rsd-alu-pair packs two independent results, so unless one reads the other’s destination the scheduler may emit either orientation and only one need be encodable. The list scheduler already tries both (its tier-1 and tier-2 partner picks). See results/corpus/RSD-RESIDUE.md for the measurement and for the weighted optimisation that chose these two sets.
    <h3 id="prologue-pair">prologue-pair</h3>
    <p>Function prologue: reserve the stack frame and save the return address.</p>
  </li>
</ul>

<svg width="100%" height="80" viewbox="0 0 800 80">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">0 0 1 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">1 1 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-sentinel tintbox" x="442" y="42" width="90" height="36" />
    <text x="487" y="60">0 0 0 1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="40" width="180" height="20" />
    <text x="208" y="50">imm</text>
  
  
  
  
  
    
    <rect class="ins-box-rsd tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">sp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> addi    sp, -16*imm</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="60" width="180" height="20" />
    <text x="208" y="70">imm</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="60" width="90" height="20" />
    <text x="343" y="70">rs1b</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">sp</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> store   rs1b, 16*imm-k(sp)</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>addi</th>
   </tr>
  
  
  <tr><th>sw</th>
  
    <td>0</td>
    
   </tr>
  
  
  <tr><th>sd</th>
  
    <td>1</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-16">Notes and observations (mostly AI)</h4>
<ul>
  <li><code>rs1b</code> is a drawn 5-bit field: ANY register may be the one stored at the top of the new frame. ra is the overwhelmingly common case but not a constraint – a leaf function that keeps its fp saves s0 there instead, and rules.py accepts it.
    <h3 id="epilogue-pair">epilogue-pair</h3>
    <p>Function epilogue: release the stack frame and return.</p>
  </li>
</ul>

<svg width="100%" height="80" viewbox="0 0 800 80">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">0 0 1 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">1 1 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-sentinel tintbox" x="442" y="42" width="90" height="36" />
    <text x="487" y="60">0 0 0 1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="40" width="180" height="20" />
    <text x="208" y="50">imm</text>
  
  
  
  
  
    
    <rect class="ins-box-rsd tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">sp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> addi    sp, 16*imm</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="60" width="90" height="20" />
    <text x="343" y="70">rs1b</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> jr      rs1b</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>addi</th>
   </tr>
  
  
  <tr><th>jr</th>
  
    <td>0</td>
    
   </tr>
  
  
  <tr><th>ret</th>
  
    <td>1</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-17">Notes and observations (mostly AI)</h4>
<ul>
  <li>The row draws only the target register: the rs2+rs1 columns carry the sp adjustment, so a <code>jalr</code> here has a ZERO offset by construction – there is no field for one – and rules.py rejects the nonzero-offset spelling.
    <h3 id="dual-setup-pair">dual-setup-pair</h3>
    <p>Two independent small moves or constants – argument marshalling.</p>
  </li>
</ul>

<svg width="100%" height="280" viewbox="0 0 800 280">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 0 0 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rda</text>
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> mv      rda, rs2a</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">rs2b</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">rdb</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> mv/li   rdb, rs2b</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="92" width="90" height="36" />
    <text x="577" y="110">1 0 0 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="92" width="54" height="36" />
    <text x="415" y="110">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="92" width="18" height="36" />
    <text x="109" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="92" width="18" height="36" />
    <text x="91" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="92" width="36" height="36" />
    <text x="640" y="110">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="90" width="90" height="20" />
    <text x="253" y="100">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="298" y="90" width="90" height="20" />
    <text x="343" y="100">rda</text>
  
  
  <text x="662" y="100" style="text-anchor:start;font-family=monospace;font-size:small;"> mv      rda, rs2a</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="110" width="90" height="20" />
    <text x="163" y="120">immb</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="110" width="90" height="20" />
    <text x="487" y="120">rdb</text>
    
  
  
  <text x="662" y="120" style="text-anchor:start;font-family=monospace;font-size:small;"> mv/li   rdb, immb</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="142" width="90" height="36" />
    <text x="577" y="160">1 0 0 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="142" width="54" height="36" />
    <text x="415" y="160">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="142" width="18" height="36" />
    <text x="109" y="160">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="142" width="18" height="36" />
    <text x="91" y="160">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="142" width="36" height="36" />
    <text x="640" y="160">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="140" width="90" height="20" />
    <text x="253" y="150">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="298" y="140" width="90" height="20" />
    <text x="343" y="150">rda</text>
  
  
  <text x="662" y="150" style="text-anchor:start;font-family=monospace;font-size:small;"> li      rda, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="160" width="90" height="20" />
    <text x="163" y="170">immb</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="160" width="90" height="20" />
    <text x="487" y="170">rdb</text>
    
  
  
  <text x="662" y="170" style="text-anchor:start;font-family=monospace;font-size:small;"> li      rdb, immb</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="192" width="90" height="36" />
    <text x="577" y="210">1 0 0 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="192" width="54" height="36" />
    <text x="415" y="210">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="192" width="18" height="36" />
    <text x="109" y="210">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="192" width="18" height="36" />
    <text x="91" y="210">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="192" width="36" height="36" />
    <text x="640" y="210">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="190" width="126" height="20" />
    <text x="271" y="200">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="334" y="190" width="54" height="20" />
    <text x="361" y="200">rda</text>
  
  
  <text x="662" y="200" style="text-anchor:start;font-family=monospace;font-size:small;"> li      rda, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="210" width="90" height="20" />
    <text x="163" y="220">immb</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="210" width="90" height="20" />
    <text x="487" y="220">rdb</text>
    
  
  
  <text x="662" y="220" style="text-anchor:start;font-family=monospace;font-size:small;"> li      rdb, immb</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="242" width="90" height="36" />
    <text x="577" y="260">1 0 0 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="242" width="54" height="36" />
    <text x="415" y="260">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="242" width="18" height="36" />
    <text x="109" y="260">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="242" width="18" height="36" />
    <text x="91" y="260">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="242" width="36" height="36" />
    <text x="640" y="260">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="240" width="90" height="20" />
    <text x="253" y="250">imma</text>
  
  
  <text x="662" y="250" style="text-anchor:start;font-family=monospace;font-size:small;"> li      arda, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="260" width="90" height="20" />
    <text x="163" y="270">immb</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="260" width="90" height="20" />
    <text x="487" y="270">rdb</text>
    
  
  
  <text x="662" y="270" style="text-anchor:start;font-family=monospace;font-size:small;"> mv/li   rdb, immb</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>mv</th>
   </tr>
  
  
  <tr><th>addi4spn<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    <td>8</td>
    
   </tr>
  
  
  <tr><th>mv</th>
  
    <td>10</td>
    
   </tr>
  
  
  <tr><th>li</th>
  
    <td>11</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>li<span style="font-weight:normal;">&nbsp;&times;2</span></th>
   </tr>
  
  
  <tr><th>addi4spn<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    <td>0</td>
    
   </tr>
  
  
  <tr><th>mv</th>
  
    <td>4</td>
    
   </tr>
  
  
  <tr><th>li</th>
  
    <td>6</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>addi4spn</th>
   </tr>
  
  
  <tr><th>addi4spn<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    <td>12</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-18">Notes and observations (mostly AI)</h4>
<ul>
  <li>THE WIDE BAND IS ARGUMENT-DESTINED, measured (2026-08-05). With the width caps relaxed to ten bits, wide <code>li</code> destinations are argument registers 86-89% of the time on musl-gcc-rv32 + sqlite-rv64 (at 7 bits 404 arg vs 51 other, at 8 bits 938 vs 154) and 85%/65% on cpp-rv32 – the arg-call-pair effect, without the call. The band this replaces, 6 bits at any rd, is nearly vacant: keeping it alongside the 8-bit band (re-measured, full scheduler) buys +18 pairs over three corpora for a doubled block. Dropped.</li>
  <li>Swept with the real scheduler against the 6-bit-any-rd baseline (musl-gcc-rv32 / sqlite-rv64 / cpp-rv32, corpus TOTALS so displacement is netted): +169 / +429 / +216 pairs. Part of the gain is displacement – rsd-alu-pair gives back up to 110, li-branch-chain up to 50 – which the totals already count. Re-measured 2026-08-07 on the merged operand-position baseline (post jalr-split and pcrel reworks, which absorbed part of the original gain): +144 / +249 on musl-gcc-rv32 / sqlite-rv64. Still positive at no block cost.</li>
  <li><code>addi4spn</code> deliberately does NOT get the split: its wide destinations are a coin flip on musl+sqlite (129 arg vs 135 other at 7 bits) and splitting it regressed musl-gcc while paying on cpp (+375/-71 relative) – a C++-marshalling bet, not a win. Its 6-bit band stays as it was.</li>
  <li>The a0-a7 restriction is enforced by scheduler/rules.py (<code>_ARG_REGS</code>, shared with arg-call-pair); the yaml states it as the 3-bit destination part of the split rows, which is how arg-call-pair states it too. ONE wide variant on purpose: an intermediate draft carried the split in BOTH slots so per-slot pricing would see one declared width, which bought nothing – either slot alone covers every pair, because the frame is order-free and the encoder places the wide operand. The asymmetric ops spelling (li_8s in A, bare li in B) prices each slot as it actually is; rules.py stays slot-agnostic, accepting the wide op in either stream position.</li>
  <li>A-SIDE, NOT B-SIDE, and the two are not interchangeable in shape even though they are in capture. The spare bits are in rs1: it is the destination port, rda is the only operand drawn there, and narrowing rda to a0-a7 frees two bits for imma without touching another column – which is arg-call-pair row 2 exactly. Slot B has nothing equivalent, since rd holds rdb alone and immb would have to straddle two columns. Each side also costs exactly one canonical-order inversion (A-side spells (wide li, mv) li-first; a B-side band would spell (addi4spn, wide li) spn-first), so that is not a discriminator.</li>
  <li>PRICED 13 BY THE MODEL, ~19 BY HAND, in the same 32-block. opcode_codepoints scores each op against the slot’s WIDEST row, so slot B’s split rows (7-bit field) hide addi4spn’s sixth bit there, which in the full-rd rows still rides an opcode repeat – the same widest-row coarseness arg-call-pair already lives with; it also cannot see that B’s li needs its full-rd narrow row beside the split rows. A band-by-band hand count (B li: rd5 narrow + rd3 seven-bit + one repeat for the eighth) is ~19. Both are inside the block; the gap is a known model artifact, not spare room to spend.
    <h3 id="load-call-chain">load-call-chain</h3>
    <p>Load a function pointer and call through it (virtual dispatch).</p>
  </li>
</ul>

<svg width="100%" height="130" viewbox="0 0 800 130">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">0 0 1 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">1 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-sentinel tintbox" x="442" y="42" width="90" height="36" />
    <text x="487" y="60">0 0 0 1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="40" width="180" height="20" />
    <text x="208" y="50">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="40" width="40" height="20" />
    <text x="20" y="50">tmp</text>
    
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> load    tmp, k*imma(rs1a)</text>

  
  
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">ra</text>
    
  
    
    <rect class="ins-box-rs tintbox" x="40" y="60" width="40" height="20" />
    <text x="60" y="70">tmp</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> jalr    ra, 0(tmp)</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="92" width="90" height="36" />
    <text x="577" y="110">0 0 1 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="92" width="54" height="36" />
    <text x="415" y="110">1 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="92" width="18" height="36" />
    <text x="109" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="92" width="18" height="36" />
    <text x="91" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="92" width="36" height="36" />
    <text x="640" y="110">1 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-sentinel tintbox" x="442" y="92" width="90" height="36" />
    <text x="487" y="110">0 0 0 1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="90" width="180" height="20" />
    <text x="208" y="100">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="90" width="90" height="20" />
    <text x="343" y="100">rs1a</text>
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="90" width="40" height="20" />
    <text x="20" y="100">tmp</text>
    
  
  
  <text x="662" y="100" style="text-anchor:start;font-family=monospace;font-size:small;"> load    tmp, k*imma(rs1a)</text>

  
  
  
  
  
  
  
    
    <rect class="ins-box-rd tintbox" x="0" y="110" width="40" height="20" />
    <text x="20" y="120">t1</text>
    
  
    
    <rect class="ins-box-rs tintbox" x="40" y="110" width="40" height="20" />
    <text x="60" y="120">tmp</text>
    
  
  
  <text x="662" y="120" style="text-anchor:start;font-family=monospace;font-size:small;"> jalr    t1, 0(tmp)</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>lw</th>
  
    
    <th>ld</th>
   </tr>
  
  
  <tr><th>jalr ra</th>
  
    <td>0</td>
    
  
    <td>1</td>
    
   </tr>
  
  
  <tr><th>jalr t1</th>
  
    <td>2</td>
    
  
    <td>3</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-19">Notes and observations (mostly AI)</h4>
<ul>
  <li><code>rd: unused</code> leaves the selecting sentinel to the enumerator, which allocates it from the reserved x0/x2 pool per (host, sentinel) – both patterns are reserved either way, so whichever has room carries this frame.</li>
  <li>The link register is NOT drawn – both templates share one row and are told apart by the op-select, which is why the budget is 4 for two loads rather than 2. <code>rules.py</code> reads the permitted set from the <code>b</code> op list above (each op’s <code>encode.rd</code>), so a register that is not spelled here cannot be paired by the scheduler either.
    <h3 id="arg-call-pair">arg-call-pair</h3>
    <p>Set up an argument, then call through a hard-coded base register.</p>
  </li>
</ul>

<svg width="100%" height="330" viewbox="0 0 800 330">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 0 1 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">1 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rda</text>
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> mv      rda, rs2a</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">immb[9:5]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="60" width="90" height="20" />
    <text x="487" y="70">immb[4:0]</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rsd tintbox" x="0" y="60" width="40" height="20" />
    <text x="20" y="70">ra</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> jalr    ra, 4*immb(ra)</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="92" width="90" height="36" />
    <text x="577" y="110">1 0 1 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="92" width="54" height="36" />
    <text x="415" y="110">1 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="92" width="18" height="36" />
    <text x="109" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="92" width="18" height="36" />
    <text x="91" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="92" width="36" height="36" />
    <text x="640" y="110">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="90" width="126" height="20" />
    <text x="271" y="100">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="334" y="90" width="54" height="20" />
    <text x="361" y="100">rda</text>
  
  
  <text x="662" y="100" style="text-anchor:start;font-family=monospace;font-size:small;"> li      rda, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="110" width="90" height="20" />
    <text x="163" y="120">immb[9:5]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="110" width="90" height="20" />
    <text x="487" y="120">immb[4:0]</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rsd tintbox" x="0" y="110" width="40" height="20" />
    <text x="20" y="120">ra</text>
    
  
  
  <text x="662" y="120" style="text-anchor:start;font-family=monospace;font-size:small;"> jalr    ra, 4*immb(ra)</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="142" width="90" height="36" />
    <text x="577" y="160">1 0 1 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="142" width="54" height="36" />
    <text x="415" y="160">1 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="142" width="18" height="36" />
    <text x="109" y="160">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="142" width="18" height="36" />
    <text x="91" y="160">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="142" width="36" height="36" />
    <text x="640" y="160">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="140" width="126" height="20" />
    <text x="271" y="150">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="334" y="140" width="54" height="20" />
    <text x="361" y="150">rda</text>
  
  
  <text x="662" y="150" style="text-anchor:start;font-family=monospace;font-size:small;"> addi4spn rda, 4*imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="160" width="90" height="20" />
    <text x="163" y="170">immb[9:5]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="160" width="90" height="20" />
    <text x="487" y="170">immb[4:0]</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rsd tintbox" x="0" y="160" width="40" height="20" />
    <text x="20" y="170">ra</text>
    
  
  
  <text x="662" y="170" style="text-anchor:start;font-family=monospace;font-size:small;"> jalr    ra, 4*immb(ra)</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="192" width="90" height="36" />
    <text x="577" y="210">1 0 1 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="192" width="54" height="36" />
    <text x="415" y="210">1 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="192" width="18" height="36" />
    <text x="109" y="210">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="192" width="18" height="36" />
    <text x="91" y="210">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="192" width="36" height="36" />
    <text x="640" y="210">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="190" width="126" height="20" />
    <text x="271" y="200">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="334" y="190" width="54" height="20" />
    <text x="361" y="200">rda</text>
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="190" width="40" height="20" />
    <text x="20" y="200">sp</text>
    
  
  
  <text x="662" y="200" style="text-anchor:start;font-family=monospace;font-size:small;"> load    rda, k*imma(sp)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="210" width="90" height="20" />
    <text x="163" y="220">immb[9:5]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="210" width="90" height="20" />
    <text x="487" y="220">immb[4:0]</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rsd tintbox" x="0" y="210" width="40" height="20" />
    <text x="20" y="220">ra</text>
    
  
  
  <text x="662" y="220" style="text-anchor:start;font-family=monospace;font-size:small;"> jalr    ra, 4*immb(ra)</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="242" width="90" height="36" />
    <text x="577" y="260">1 0 1 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="242" width="54" height="36" />
    <text x="415" y="260">1 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="242" width="18" height="36" />
    <text x="109" y="260">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="242" width="18" height="36" />
    <text x="91" y="260">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="242" width="36" height="36" />
    <text x="640" y="260">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="240" width="90" height="20" />
    <text x="253" y="250">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="298" y="240" width="90" height="20" />
    <text x="343" y="250">imma</text>
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="240" width="40" height="20" />
    <text x="20" y="250">sp</text>
    
  
  
  <text x="662" y="250" style="text-anchor:start;font-family=monospace;font-size:small;"> store   rs2a, k*imma(sp)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="260" width="90" height="20" />
    <text x="163" y="270">immb[9:5]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="260" width="90" height="20" />
    <text x="487" y="270">immb[4:0]</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rsd tintbox" x="0" y="260" width="40" height="20" />
    <text x="20" y="270">ra</text>
    
  
  
  <text x="662" y="270" style="text-anchor:start;font-family=monospace;font-size:small;"> jalr    ra, 4*immb(ra)</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="292" width="90" height="36" />
    <text x="577" y="310">1 0 1 0 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="292" width="54" height="36" />
    <text x="415" y="310">1 p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="292" width="18" height="36" />
    <text x="109" y="310">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="292" width="18" height="36" />
    <text x="91" y="310">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="292" width="36" height="36" />
    <text x="640" y="310">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="290" width="126" height="20" />
    <text x="271" y="300">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="334" y="290" width="54" height="20" />
    <text x="361" y="300">rda</text>
  
  
  <text x="662" y="300" style="text-anchor:start;font-family=monospace;font-size:small;"> addi rda, rda, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="310" width="90" height="20" />
    <text x="163" y="320">immb[9:5]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="310" width="90" height="20" />
    <text x="487" y="320">immb[4:0]</text>
    
  
  
  
  
  
    
    <rect class="ins-box-rs tintbox" x="0" y="310" width="40" height="20" />
    <text x="20" y="320">t1</text>
    
  
  
  <text x="662" y="320" style="text-anchor:start;font-family=monospace;font-size:small;"> jr      4*immb(t1)</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>mv</th>
  
    
    <th>li</th>
  
    
    <th>addi4spn</th>
  
    
    <th>lw</th>
  
    
    <th>ld</th>
  
    
    <th>sw</th>
  
    
    <th>sd</th>
  
    
    <th>addi</th>
   </tr>
  
  
  <tr><th>jalr</th>
  
    <td>0</td>
    
  
    <td>1</td>
    
  
    <td>2</td>
    
  
    <td>3</td>
    
  
    <td>4</td>
    
  
    <td>5</td>
    
  
    <td>6</td>
    
  
    <td>7</td>
    
   </tr>
  
  
  <tr><th>jr t1</th>
  
    <td>8</td>
    
  
    <td>9</td>
    
  
    <td>10</td>
    
  
    <td>11</td>
    
  
    <td>12</td>
    
  
    <td>13</td>
    
  
    <td>14</td>
    
  
    <td>15</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-20">Notes and observations (mostly AI)</h4>
<ul>
  <li>Row 1 holds <code>mv</code>, which needs only rs1a and rda; row 2 holds the three ops that want a wide immediate and an ARGUMENT destination, splitting the rs1 column three-two so <code>imma</code> reaches seven bits; row 3 holds the stores, whose source register needs all five bits and whose stack offsets are small. The call displacement rides funct5+rd in every row – the S-type immediate positions.</li>
  <li>A 3-bit rda costs almost nothing here and buys two: <code>li</code> at rda3+imm5 catches 925 of the 933 that an unrestricted rda catches on cpp-rv32, because these are argument setups by construction. 3+7 beats 5+5 by 68% on cpp <code>li</code> and 18% on cpp <code>addi4spn</code>.</li>
  <li>Load and store offsets scale by the ACCESS width, as <code>c.lwsp</code> and <code>c.sdsp</code> do: spill slots are aligned to the access, so <code>k*imm</code> costs nothing and reaches four or eight times further.</li>
  <li><code>addi4spn</code> scales by four, which is a real trade rather than a free one: 10.7% of cpp’s <code>addi rd,sp</code> are NOT 4-aligned, because C++ takes the address of byte- and short-sized stack temporaries, so scaling costs 13% of the cpp hits (6473 against 7456 for a raw 7-bit field) and buys 0..508 instead of 0..127. On rv64 the scaled form is the one the fit prefers outright.</li>
  <li>The rd column carries the displacement in every row, so this frame neither hosts nor is hosted.
    <h3 id="setup-jump-pair">setup-jump-pair</h3>
    <p>Set up an argument or return value, then transfer control.</p>
  </li>
</ul>

<svg width="100%" height="380" viewbox="0 0 800 380">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">1 0 0 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="40" width="90" height="20" />
    <text x="487" y="50">rda</text>
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> mv      rda, rs2a</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">rs1b</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> jr/jalr rs1b</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="92" width="90" height="36" />
    <text x="577" y="110">1 0 0 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="92" width="54" height="36" />
    <text x="415" y="110">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="92" width="18" height="36" />
    <text x="109" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="92" width="18" height="36" />
    <text x="91" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="92" width="36" height="36" />
    <text x="640" y="110">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="90" width="90" height="20" />
    <text x="253" y="100">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="90" width="90" height="20" />
    <text x="343" y="100">rs1a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="90" width="90" height="20" />
    <text x="487" y="100">rda</text>
  
  
  <text x="662" y="100" style="text-anchor:start;font-family=monospace;font-size:small;"> load    rda, k*imma(rs1a)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="110" width="90" height="20" />
    <text x="163" y="120">rs1b</text>
    
  
  
  <text x="662" y="120" style="text-anchor:start;font-family=monospace;font-size:small;"> jr/jalr rs1b</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="142" width="90" height="36" />
    <text x="577" y="160">1 0 0 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="142" width="54" height="36" />
    <text x="415" y="160">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="142" width="18" height="36" />
    <text x="109" y="160">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="142" width="18" height="36" />
    <text x="91" y="160">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="142" width="36" height="36" />
    <text x="640" y="160">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="140" width="180" height="20" />
    <text x="208" y="150">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="140" width="90" height="20" />
    <text x="487" y="150">rda</text>
  
  
  <text x="662" y="150" style="text-anchor:start;font-family=monospace;font-size:small;"> load    rda, k*imma(rs1a)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="160" width="90" height="20" />
    <text x="343" y="170">rs1b</text>
    
  
  
  <text x="662" y="170" style="text-anchor:start;font-family=monospace;font-size:small;"> jr/jalr rs1b</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="192" width="90" height="36" />
    <text x="577" y="210">1 0 0 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="192" width="54" height="36" />
    <text x="415" y="210">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="192" width="18" height="36" />
    <text x="109" y="210">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="192" width="18" height="36" />
    <text x="91" y="210">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="192" width="36" height="36" />
    <text x="640" y="210">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="190" width="180" height="20" />
    <text x="208" y="200">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="442" y="190" width="90" height="20" />
    <text x="487" y="200">rda</text>
  
  
  <text x="662" y="200" style="text-anchor:start;font-family=monospace;font-size:small;"> li      rda, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="210" width="90" height="20" />
    <text x="343" y="220">rs1b</text>
    
  
  
  <text x="662" y="220" style="text-anchor:start;font-family=monospace;font-size:small;"> jr/jalr rs1b</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="242" width="90" height="36" />
    <text x="577" y="260">1 0 0 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="242" width="54" height="36" />
    <text x="415" y="260">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="242" width="18" height="36" />
    <text x="109" y="260">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="242" width="18" height="36" />
    <text x="91" y="260">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="242" width="36" height="36" />
    <text x="640" y="260">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="240" width="90" height="20" />
    <text x="253" y="250">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="298" y="240" width="90" height="20" />
    <text x="343" y="250">rda</text>
  
  
  <text x="662" y="250" style="text-anchor:start;font-family=monospace;font-size:small;"> mv      rda, rs2a</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="260" width="90" height="20" />
    <text x="163" y="270">immb[9:5]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="260" width="90" height="20" />
    <text x="487" y="270">immb[4:0]</text>
    
  
  
  <text x="662" y="270" style="text-anchor:start;font-family=monospace;font-size:small;"> j       4*immb</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="292" width="90" height="36" />
    <text x="577" y="310">1 0 0 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="292" width="54" height="36" />
    <text x="415" y="310">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="292" width="18" height="36" />
    <text x="109" y="310">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="292" width="18" height="36" />
    <text x="91" y="310">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="292" width="36" height="36" />
    <text x="640" y="310">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="208" y="290" width="90" height="20" />
    <text x="253" y="300">rda</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="298" y="290" width="90" height="20" />
    <text x="343" y="300">rs1a</text>
  
  
  <text x="662" y="300" style="text-anchor:start;font-family=monospace;font-size:small;"> load    rda, 0(rs1a)</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="310" width="90" height="20" />
    <text x="163" y="320">immb[9:5]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="310" width="90" height="20" />
    <text x="487" y="320">immb[4:0]</text>
    
  
  
  <text x="662" y="320" style="text-anchor:start;font-family=monospace;font-size:small;"> j       4*immb</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="342" width="90" height="36" />
    <text x="577" y="360">1 0 0 1 1</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="342" width="54" height="36" />
    <text x="415" y="360">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="342" width="18" height="36" />
    <text x="109" y="360">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="342" width="18" height="36" />
    <text x="91" y="360">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="342" width="36" height="36" />
    <text x="640" y="360">1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="340" width="90" height="20" />
    <text x="253" y="350">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rd tintbox" x="298" y="340" width="90" height="20" />
    <text x="343" y="350">rda</text>
  
  
  <text x="662" y="350" style="text-anchor:start;font-family=monospace;font-size:small;"> li      rda, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="118" y="360" width="90" height="20" />
    <text x="163" y="370">immb[9:5]</text>
    
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="442" y="360" width="90" height="20" />
    <text x="487" y="370">immb[4:0]</text>
    
  
  
  <text x="662" y="370" style="text-anchor:start;font-family=monospace;font-size:small;"> j       4*immb</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>mv</th>
  
    
    <th>lbu</th>
  
    
    <th>lw</th>
  
    
    <th>ld</th>
   </tr>
  
  
  <tr><th>ret</th>
  
    <td>0</td>
    
  
    <td>1</td>
    
  
    <td>2</td>
    
  
    <td>3</td>
    
   </tr>
  
  
  <tr><th>jalr ra</th>
  
    <td>4</td>
    
  
    <td>5</td>
    
  
    <td>6</td>
    
  
    <td>7</td>
    
   </tr>
  
  
  <tr><th>jr</th>
  
    <td>8</td>
    
  
    <td>9</td>
    
  
    <td>10</td>
    
  
    <td>11</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>li</th>
   </tr>
  
  
  <tr><th>ret</th>
  
    <td>16</td>
    
   </tr>
  
  
  <tr><th>jalr ra</th>
  
    <td>17</td>
    
   </tr>
  
  
  <tr><th>jr</th>
  
    <td>18</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>mv</th>
  
    
    <th>lbu</th>
  
    
    <th>lw</th>
  
    
    <th>ld</th>
   </tr>
  
  
  <tr><th>j</th>
  
    <td>20</td>
    
  
    <td>21</td>
    
  
    <td>22</td>
    
  
    <td>23</td>
    
   </tr>
  
</table>




<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>li</th>
   </tr>
  
  
  <tr><th>j</th>
  
    <td>24</td>
    
   </tr>
  
</table>


</div>
<h4 id="notes-and-observations-mostly-ai-21">Notes and observations (mostly AI)</h4>
<ul>
  <li><code>j</code> covers <code>jal x0</code>; a jal with a real destination is a call and is excluded from every jump frame.</li>
  <li>Direct <code>j</code> (78-92% of this frame’s packets) takes rows 3-4: <code>rs1b</code> is dropped – a direct jump has no register operand – and <code>immb</code> takes funct5+rd, a 10-bit displacement in PACKET units at the branch-immediate positions every control frame shares. Packets are 4-byte aligned, so the low bit RVC must carry is dead and a displacement costs 0.54x its RVC bits. 10 bits covers 84.6% of direct <code>j</code> on sqlite and 97-98% on musl.</li>
  <li>rules.py cannot range-check the displacement: corpus jump operands are unresolved labels, so a pairwise rule has nothing to test. The scheduled count includes the over-range tail (~15% on sqlite). See results/corpus/README.md.</li>
  <li>On the direct-<code>j</code> rows a load has no offset field (offsets are zero in 98.2% of chained cases anyway) and <code>li</code> narrows to 5 bits.
    <h3 id="arith-jump-pair">arith-jump-pair</h3>
    <p>A last in-place computation, then a control transfer.</p>
  </li>
</ul>

<svg width="100%" height="180" viewbox="0 0 800 180">

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="42" width="90" height="36" />
    <text x="577" y="60">0 0 1 1 p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="42" width="54" height="36" />
    <text x="415" y="60">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="42" width="18" height="36" />
    <text x="109" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="42" width="18" height="36" />
    <text x="91" y="60">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="42" width="36" height="36" />
    <text x="640" y="60">1 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-sentinel tintbox" x="442" y="42" width="90" height="36" />
    <text x="487" y="60">0 0 0 1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="208" y="40" width="90" height="20" />
    <text x="253" y="50">rs2a</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="40" width="90" height="20" />
    <text x="343" y="50">rsda</text>
  
  
  <text x="662" y="50" style="text-anchor:start;font-family=monospace;font-size:small;"> alu     rsda, rsda, rs2a</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="60" width="90" height="20" />
    <text x="163" y="70">rs1b</text>
    
  
  
  <text x="662" y="70" style="text-anchor:start;font-family=monospace;font-size:small;"> jr/jalr rs1b</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="92" width="90" height="36" />
    <text x="577" y="110">0 0 1 1 p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="92" width="54" height="36" />
    <text x="415" y="110">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="92" width="18" height="36" />
    <text x="109" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="92" width="18" height="36" />
    <text x="91" y="110">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="92" width="36" height="36" />
    <text x="640" y="110">1 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-sentinel tintbox" x="442" y="92" width="90" height="36" />
    <text x="487" y="110">0 0 0 1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="90" width="90" height="20" />
    <text x="253" y="100">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="90" width="90" height="20" />
    <text x="343" y="100">rsda</text>
  
  
  <text x="662" y="100" style="text-anchor:start;font-family=monospace;font-size:small;"> alu     rsda, rsda, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="110" width="90" height="20" />
    <text x="163" y="120">rs1b</text>
    
  
  
  <text x="662" y="120" style="text-anchor:start;font-family=monospace;font-size:small;"> jr/jalr rs1b</text>

  <use href="#header-impl" />
  
  

  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="532" y="142" width="90" height="36" />
    <text x="577" y="160">0 0 1 1 p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="388" y="142" width="54" height="36" />
    <text x="415" y="160">p p p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="100" y="142" width="18" height="36" />
    <text x="109" y="160">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-selector tintbox" x="82" y="142" width="18" height="36" />
    <text x="91" y="160">p</text>
  
    
    
    
    
    
    
    <rect class="ins-box-constant tintbox" x="622" y="142" width="36" height="36" />
    <text x="640" y="160">1 0</text>
  
    
    
    
    
    
    
    <rect class="ins-box-sentinel tintbox" x="442" y="142" width="90" height="36" />
    <text x="487" y="160">0 0 0 1 0</text>
  

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-imm tintbox" x="208" y="140" width="90" height="20" />
    <text x="253" y="150">imma</text>
  
    
    
    
    
    
    
    <rect class="ins-box-rsd tintbox" x="298" y="140" width="90" height="20" />
    <text x="343" y="150">rsda</text>
  
  
  <text x="662" y="150" style="text-anchor:start;font-family=monospace;font-size:small;"> li     rsda, imma</text>

  
  
  
    
    
    
    
    
    
    <rect class="ins-box-rs tintbox" x="118" y="160" width="90" height="20" />
    <text x="163" y="170">rs1b</text>
    
  
  
  <text x="662" y="170" style="text-anchor:start;font-family=monospace;font-size:small;"> jr/jalr rs1b</text>

</svg>

<div style="display:flex;flex-wrap:wrap;gap:1em;">



<table class="opcodes" style="width:auto;">
  
  <tr style="font-size:small;text-align:center;"><th>slot B</th><th colspan="999">slot A</th></tr>
  <tr><th></th>
  
    
    <th>addi<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    
    <th>li<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    
    <th>addiw<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    
    <th>andi<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    
    <th>slli<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    
    <th>srli<span style="font-weight:normal;">&nbsp;&times;2</span></th>
  
    
    <th>add</th>
  
    
    <th>and</th>
  
    
    <th>or</th>
  
    
    <th>xor</th>
   </tr>
  
  
  <tr><th>ret</th>
  
    <td>0</td>
    
  
    <td>2</td>
    
  
    <td>4</td>
    
  
    <td>6</td>
    
  
    <td>8</td>
    
  
    <td>10</td>
    
  
    <td>12</td>
    
  
    <td>13</td>
    
  
    <td>14</td>
    
  
    <td>15</td>
    
   </tr>
  
  
  <tr><th>jalr ra</th>
  
    <td>16</td>
    
  
    <td>18</td>
    
  
    <td>20</td>
    
  
    <td>22</td>
    
  
    <td>24</td>
    
  
    <td>26</td>
    
  
    <td>28</td>
    
  
    <td>29</td>
    
  
    <td>30</td>
    
  
    <td>31</td>
    
   </tr>
  
  
  <tr><th>jr</th>
  
    <td>32</td>
    
  
    <td>34</td>
    
  
    <td>36</td>
    
  
    <td>38</td>
    
  
    <td>40</td>
    
  
    <td>42</td>
    
  
    <td>44</td>
    
  
    <td>45</td>
    
  
    <td>46</td>
    
  
    <td>47</td>
    
   </tr>
  
  
  <tr><th>j</th>
  
    <td>48</td>
    
  
    <td>50</td>
    
  
    <td>52</td>
    
  
    <td>54</td>
    
  
    <td>56</td>
    
  
    <td>58</td>
    
  
    <td>60</td>
    
  
    <td>61</td>
    
  
    <td>62</td>
    
  
    <td>63</td>
    
   </tr>
  
</table>


</div>

<h2 id="the-results">The results</h2>

<p>Random test files, compiled with RVC and run through a scheduler to try
to pick out viable instruction pairs gives these results:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>corpus          insns   pairs  packet%  realRVC%   vsRVC  to parity
testcase0       21875    4185    80.9%     81.6%   99.1%      -167
godot           90171   15823    82.5%     76.3%  108.1%     +5545
cpp-rv32       418345   92667    77.8%     71.0%  109.7%    +28786
cpp-rv64       409200   87664    78.6%     71.2%  110.4%    +30274
musl-rv32      118990   27712    76.7%     74.9%  102.4%     +2159
musl-rv64      102010   22208    78.2%     72.7%  107.6%     +5608
sqlite-rv32    192688   45512    76.4%     72.1%  105.9%     +8258
sqlite-rv64    189602   42689    77.5%     72.1%  107.5%    +10266
---------------------------------------------------------------------
RV32 aggregate 751898  170076    77.4%     72.2%  107.2%    +39036
RV64 aggregate 790983  168384    78.7%     72.2%  109.1%    +51693
COMBINED      1542881  338460    78.1%     72.2%  108.1%    +90729
</code></pre></div></div>

<p>This is suboptimal because it’s not using a compiler which optimises for
the instruction set I’ve created.  It’s also suboptimal because I’m
using code compiled for RVC, which has register pressure that does not
apply here, and so it uses more instructions than strictly necessary (at
least in Clang’s case – GCC finds excuses to use more
instructions regardless).  I have some other test cases but the
tabulation got mucky, so let’s just run with the above for now.</p>

<h2 id="development-process">Development process</h2>

<p>I vibe-coded an instruction scheduler which scans a corpus of assembly
and attempts to find and fuse pairable instructions according to rules
describing what a legitimate instruction pair would
look like; just to get a feel for what sorts of pairing rules I could
introduce.  It simply looks at register usage to determine when a result
is no longer needed, and which instructions can move past which other
instructions.</p>

<p>This seemed easier than writing my own compiler to optimise a made-up
instruction set which I hadn’t designed yet, but it also has
limitations.  There’s no expectation for it to yield runnable code;
merely to give a feel for how things would pack if the nits can be
worked out.</p>

<p>Then I randomly threw rules at it to see what would stick.</p>

<p>And I pivoted to laying out the bit patterns by hand in order to prove
that the budgets were being met.</p>

<p>Attempting to draw things around the standard RISC-V frames brought out
the four-register-field pattern shown above, while sweeping up the
remaining bits for an unregulated mix of frame selection and opcode
selection.</p>

<p>Then, I did a bad thing: I ran optimisers against an irresponsibly
small corpus to see what could be squeezed out.</p>

<p>Here’s the tooling, such as it is: <a href="https://github.com/sh1bot/ciscv_experiment">CISC-V experiment</a>
(content-warning: unchecked AI output)</p>

<p>It’s no longer human-readable.  <a href="https://claude.ai/">Claude</a> has taken things in its own
direction.
Much of what it does is flaky and unreliable, and most of the code is
only there as a toolkit for “what if?” queries posed to the AI.  I don’t
really want to think about working with that code by hand.</p>

<p>But it’s sufficient to get a gist of whether the ideas make sense and
how they map to real code.</p>

<h3 id="pairing-rule-selection">Pairing rule selection</h3>

<p>I drew inspiration from classic CISC operations, proposals for macro-op
fusion, and things other architectures do.  And just kind of threw them
all in there and mused openly to Claude about how that looked to me and
asked it for feedback.</p>

<p>Claude was not a reliable witness, and led me down many garden paths of
faulty analyses and losses of comprehension.  But it was an exploration
I could pick up on a whim with my phone, so it won on convenience.</p>

<p>It feels like it’s come out with a lot of redundancies; though often the
apparent redundancies are just redistributions of immediate sizes to
suit different idioms.</p>

<h3 id="biclique-optimisation">Biclique optimisation</h3>

<p>Some (many?) instructions pair naturally with only a limited set of
other instructions.  Attempting to pair a free choice of any operation
with free choice of any other operation isn’t fruitful.  By cutting the
frames into different sections with different purposes (often emulating
different CISC instructions) it becomes easy to minimise the
combinations which are worth making space for.  Possibly (TBD) at the
cost of decode complexity.</p>

<p>Conversely, rules like <code>rsd-alu-pair</code> avoid relationships between slots
and so encoding freedom of operation order wastes nearly one bit, and
removing that freedom realises more opportunities for tuning.</p>

<p>All this optimisation risks over-fitting and adding complexity to the
decoder, so it must be done thoughtfully (Narrator: it has not been done
thoughtfully).</p>

<h3 id="immediate-sizing">Immediate sizing</h3>

<p>This is gnarly.  You get 5 bits by default, aliasing with a register
index.  Five bits can’t fully specify a bit shift on a 64-bit target.
Five bits is often not enough for a lot of things.  And you have to
decide whether it’s signed or not.</p>

<p>In a lot of cases the thing to do is sacrifice another register operand
to get a 10-bit immediate.  Implicit SP with a 10-bit offset means you
can get to a lot of local variables.</p>

<p>Also, the value of the immediate in one slot will often be scaled by the
instruction choice in the other slot (e.g., <code>addi</code> gets its immediate
scaled by 4 if it’s paired with <code>lw</code>), and of course memory access also
uses its own implicit scaling.</p>

<p>Otherwise, duplicate the opcode in the opcode list to extend the
immediate range by one bit.</p>

<p>Trawling through code there are patterns of step changes in immediate
requirements.  The specific corpus is an obvious source of this effect,
and some artefacts of the original immediate limits of the existing
architecture, but other things appear to be a legitimate reflection of
the way code tends to work.  I just kind of guessed.  Claude helped.  It
did lead to a lot of redundancy, but I didn’t have many better ideas, so
immediate shaping became a big factor in the design choices.</p>

<h3 id="enumeration">Enumeration</h3>

<p>For the sake of a quick POC I just enumerated all the frames according
to their population counts rounded up to powers of two, and hoped I
wouldn’t run out of space.  There’s scope to do this more intelligently,
signalling specific conditions relevant to the instruction decoder in
specific bits of the enumeration, but I haven’t put the effort in at
this stage.</p>

<h3 id="loadstore-pairings">Load/store pairings</h3>

<p>I was initially very reluctant to merge load/store data operands into
interdependent arrangements with arithmetic, because it leaves no space
for scheduling around memory delays.  Eventually I resigned myself to
accepting it, though, because without that there’s going to be a lot
less compression.</p>

<p>Also, it opened up opportunities to hint at things that could be left
out of direct ALU paths.  Things like indirect branching via memory;
where branch prediction (when present) typically goes ahead with its
decision without seeing the data, and the data is only there to cancel
the prediction after the fact if it was inconsistent.</p>

<p>Chaining with a base-register operand was less painful.  In particular
filling the gap RISC-V leaves with <code>rb+k*ri</code> address generation which,
it turns out, can be done via temporary register without needing to save
the intermediate result.</p>

<h2 id="future-work">Future work</h2>

<h3 id="decode-complexity">decode complexity</h3>

<p>A major problem right now is that the enumeration of frames and opcodes
within frames doesn’t really attempt to match established conventions
about how the decoder can resolve details quickly.  It does (generally)
put rs1 and rs2 for the first instruction in the same slots, so they can
be prepared early, but there are other details one wants to know soonish
which could be surfaced but have not been.</p>

<p>With a bit of massaging it appears to be possible to reduce the
signature of each frame type down to a handful of bits in the opcode
field, and squeeze a signature for the transform required to turn the
slot-B parameters into a slot-A frame in another handful of bits.  But I
have not written (or vibed) such an allocator yet.</p>

<h4 id="how-to-approach-that">how to approach that</h4>

<p>What we have right now is ten bits of “deal with it later”, and 20 bits
corresponding to operand fields in fairly regular positions.  1024
codepoints, only about 3/4 populated after rounding each frame up to a
power of two.</p>

<p>What I believe is needed is to collate every codepoint by its slot-A
operand patterns (including immediate sizes and positions) and to pack
these bits together as a convenient decoder index.  I think Claude said
this was doable in about three or four bits.</p>

<p>Then, without regard to those bits, we need to sort the codepoints by
the slot B operands in the same way, and pack these together in the same
way, but in some different bits, so that we can begin the re-format of
slot B into a shape the normal instruction decoder can ingest.  One
would also want a clear signal for branches and jumps, for the
instruction prefetch pipeline.</p>

<p>There’s no guarantee that will resolve into a sensible number of bits,
but I asked Claude to try and it said it might get away with three more
bits, but I haven’t verified.</p>

<p>And in the cases where a list of opcodes contains repetitions of an
opcode to make up extra immediate bits, that enumeration should be
swizzled to put the redundant opcode selector at bit 30 or 31 of the
word, so immediate decode is relatively consistent.  This seems like the
least hard problem, but it should be done.</p>

<h3 id="quality">quality</h3>

<p>Another problem is lack of thoughtful optimisation and, conversely,
gross overfitting to my limited test corpus.</p>

<p>And, of course, the regularity needs to be improved.  Balancing
regularity and generality against compression.  And then factoring the
cost model of a realistic compiler in and then fitting more tightly to
that, and then reasserting regularity and generality all over again.
Around and around and around…</p>

<p>That said, this adventure promotes itself as being CISC-inspired, so
being a random bucket of overlapping things that seemed like good ideas
is pretty on-brand.</p>

<h3 id="sundries">sundries</h3>

<p>And a toolchain would be nice, too.  And an implementation.  And from
those, feedback into what makes a better target, and back around the
tuning loops again with that insight in mind.</p>

<h2 id="ai-disclosure-statement">AI disclosure statement</h2>

<p>Yep.</p>]]></content><author><name>sh1boot</name></author><category term="computer-architecture" /><category term="riscv" /><category term="compression" /><category term="vibe-coding" /><summary type="html"><![CDATA[After months of trying to get around to it, I finally have a straw-man level system of compressing RISC-V code by squashing pairs of opcodes into a single 32-bit packet rather than squashing individual opcodes into 16-bit packets in order to address issues with unaligned 32-bit instruction words.]]></summary></entry><entry><title type="html">CISC-V: code compression in the style of a CISC architecture</title><link href="https://www.xn--tkuka-m3a3v.dev/cisc-v-begins/" rel="alternate" type="text/html" title="CISC-V: code compression in the style of a CISC architecture" /><published>2026-05-31T00:00:00+00:00</published><updated>2026-07-06T19:47:44+00:00</updated><id>https://www.xn--tkuka-m3a3v.dev/cisc-v-begins</id><content type="html" xml:base="https://www.xn--tkuka-m3a3v.dev/cisc-v-begins/"><![CDATA[<p>Given a mixed-length instruction stream like Thumb or RVC you encounter
a few different headaches.  The most infamous case is the complexity
of handling un-aligned 32-bit instructions which can straddle
architectural boundaries like page or cache line boundaries.  There are
also caching and security implications of being able to jump into the
middle of an instruction, and multi-issue problems with recognising
correct instruction start points, and so on…</p>

<p>It also, while introducing some of the complexity of CISC, leaves behind
other CISC advantages like explicit macro-op encoding for often-fused
operations in higher-performance machines.  But by blithely throwing in
CISC instructions to get code size down you give up the simplicity of
implementation for compact designs.</p>

<p>So I’ve been <a href="/experimental-riscv-instruction-compression">tinkering</a> to try to find
a different compromise which allows both fast and compact
implementations to do things in a way that suits their separate goals.</p>

<p>I had hoped that this post, when I got to it, would be where I published
a more complete proof of concept over what I have discussed before, but
things have not gone to plan and I’m rushing this out tonight.</p>

<p>On the plus side, at least I have the name, now: CISC-V</p>

<p>And as we all know, naming is half the battle.</p>

<h2 id="instruction-packet-based-compression">Instruction-packet-based compression</h2>

<p>While strict alignment could be imposed on an existing 16-bit
instruction compression scheme, that gives up coding space to express
things which are no longer legal.  It makes more sense to use 32-bit
packets (or, conceivably, 64-bit packets) which can, in turn, exploit
the intrinsic pairing of two opcodes within a packet for other gains.</p>

<p>That’s the plan, here.  One 32-bit opcode may occupy a whole packet, or
two compact instructions can occupy the same space instead.</p>

<p>Since a lot of assembly refers to the same register repeatedly from one
instruction to the next it also makes sense to exploit this redundancy
within packets to regain some coding space.  CISC typically exploits
this by using the same register as destination and one of its sources,
but we can take things further by sharing with an adjacent instruction.</p>

<p>The down side is that some instructions which should be compact will
fail to be compact because they can’t be moved adjacent to something
they can share the packet with.</p>

<p>Other constraints can be imposed on the packet, as well.</p>

<p>In particular, both instructions must execute (give or take exceptions).
There’s no branching to just the second instruction in the packet (all
branch offsets must be 32-bit aligned, so relative branches reach
further).  And any branch out of the packet is coded as the second
instruction, so it can’t prevent the execution of the other instruction
in the packet.</p>

<p>(There’s an alternative model, here, where the first instruction can be
a forward branch to the next packet, rendering the second instruction in
the packet conditional, and the implementation can decide how it’s going
to handle that)</p>

<p>The architecture still has to allow for exception restarts at the second
instruction, but it doesn’t have to encode relative branches, and use of
such return addresses can be restricted to appropriate instructions
and/or privilege levels.  Or difficult restarts can just be emulated in
software.</p>

<h3 id="decoding-models">Decoding models</h3>

<h4 id="baby-steps">Baby steps</h4>

<p>Rather than decoding four bytes starting at the 16-bit address and
advancing either two or four bytes accordingly, we always examine a
32-bit packet and decode it in one of two different ways depending on
whether our instruction pointer is at an odd or even 16-bit boundary.</p>

<p>Alternatively, upon entry to a 32-bit packet, we decode the first
instruction and begin to execute it, and concurrently we shuffle the
bits around to produce a 32-bit opcode to decode on the next cycle using
the same instruction decoder (with a bit of extra logic so exception
resume can ignore the first instruction when necessary).</p>

<p>This is the target of the “as-if model” for other approaches.  Ambiguous
cases, if they’re allowed (preferably not!), should be resolved in terms
of what this model would do.  Here we risk producing those gnarly
situations that cause high-performance implementations to do pipeline
flushes, so try very hard to avoid this.</p>

<h4 id="omnomnomnom">Omnomnomnom!</h4>

<p>If you have the sort of implementation which likes to pick up dozens of
opcodes at once and throw them all down the pipeline in parallel to be
sorted out later, then you probably don’t want to hear about decoding
each 32-bit packet twice and producing up to twice as many instructions
as packets, even if most of them are no-op placeholders for instructions
which didn’t decode.</p>

<p>To avoid that headache I propose pushing it back to the µ-op fission
stage, since that exists already and it’s already in the habit of
splitting things up which are too complicated.</p>

<p>What could possibly go wrong?  I don’t know, so I assume it’s fine.</p>

<p>As a secondary benefit, it creates opportunities to <em>not</em> break some
instructions, and to treat some packets as pre-fused macro-ops.</p>

<h3 id="exceptions">Exceptions</h3>

<p>A restartable exception may be triggered within an instruction pair, and
the architecture has to allow for this.</p>

<p>My working assumption is that bit 1 in the program counter or return
address signals that the first instruction is to be ignored (it has
already executed and retired), but this will not be used in any normal
operation outside of exceptions.  No calls can set this bit, no returns
outside of exceptions should accept it being set, and relative branches
are in 32-bit increments.</p>

<p>Also, while instruction pairing may suggest the use of a direct data
path from one instruction to the next within the packet, without the need
to land the result in a register, this data still has to be exposed for
save, restore, and inspection by an exception handler. So a temporary
register must be available.</p>

<h2 id="the-experiment">The experiment</h2>

<p>I vibe-coded a tool to try to maximise pairs of instructions by
reordering instructions in ways that were functionally equivalent (in
particular, by noting when registers were dead) but which would open up
pairing opportunities.  Unfortunately Claude soon became mired in its
own bad code, and I was spending more time asking it to clean up its
messes than I was trying new experiments.</p>

<p>For sample data I used a generic kernel built for a basic RVA profile
(or maybe RVM?), and I built <a href="https://docs.godotengine.org/en/stable/engine_details/development/compiling/index.html">Godot</a> for RVA23, and ran stats on both
of these, attempting to reorder instructions to bring pairs together,
but not too intelligently.</p>

<p>This is operating a little blind, and overlooking how the compiler might
arrange things differently if it knew the target instruction set, but
it’s the limit of the effort I’m going to make.  Instead, I spent most
of my energy cursing at Claude.</p>

<p>The whole effort was kind of a bust, and I needed to spend more time
than I had doing it all from scratch.  I could, theoretically, vibe-code
it from scratch asking for a much more restricted tool where I could do
the hard parts myself, but I don’t have time for that.</p>

<p>What I did get, though, was a better feel for what pairing rules are
actually useful if the compiler were to put things in an order that
exploited them.</p>

<h2 id="pairing-types">Pairing types</h2>

<p>The rules I found which most often identify pairable instructions are:</p>

<ul>
  <li>load/store at adjacent memory locations (Aarch64’s <code>ldp</code> and <code>stp</code>
opcodes) (top by a large margin)</li>
  <li>pairs of independent <code>mv</code> or <code>li</code> instructions</li>
  <li>double-indirect memory accesses (load a pointer, then load/store
whatever that pointer points to)</li>
  <li>pre/post increment memory operations</li>
  <li>pairs of independent arithmetic in two-operand form (<code>rsd, rs2</code>)</li>
  <li>compare-branch chains (branch depends on result of comparison)</li>
  <li>load-branch chains (load followed by conditional branch on loaded
value; which is then discarded)</li>
  <li>arithmetic chains</li>
</ul>

<h3 id="chain-rules">Chain rules</h3>

<p>Chains are where the second instruction depends on the first, and in the
experiments that I did the result of the first must also be discarded
after use by the second, so the intermediate value needn’t be exposed in
an architectural register – except that it’s still needed for
single-stepping implementations and exceptions.  My solution, here, is
to use <code>x31</code> or <code>x15</code> as the hard-coded temporary register, with a rule
that the content of the register is undefined after the second
instruction.</p>

<p>Some pre-increment rules could be chains, too.  These are cases where a
value is added to a register before using that register as the base of a
memory operation, <em>and</em> the base register is discarded after use.
Pre-increment achieves this but it overwrites the original base register
with the modified address, which is not always the desired outcome
(maybe 50:50, varying significantly by testcase).</p>

<p>So, instruction pairs like:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>op0, rd0, ra0, rb0
op1, rd1, rd0, rb1
</code></pre></div></div>
<p>or</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>op0, rd1, ra0, rb0
op1, rd1, rd1, rb1
</code></pre></div></div>
<p>can be replaced with:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>op0, x31, ra0, rb0
op1, rd1, x31, rb1
</code></pre></div></div>
<p>and then <code>x31</code> is not coded at all, but rather deduce that from the
pairing category.</p>

<p>This pattern has further sub-classes where the relationship between
<code>op0</code> and <code>op1</code> is constrained to save coding space.</p>

<p>First, exclude the two-operand instructions (one-in-one-out), like <code>li</code>
and <code>mv</code> because they don’t benefit.</p>

<p>If the second instruction is load/store, then the first is preparing
its base address (pre-increment, no write-back), and shifts and bit
manipulation aren’t likely to be any use, so make a category and exclude
those possibilities.  At the same time the load/store instruction
encodes a data size and this can restrict possibilities for the other
instruction; eg., by choosing the right <code>X</code> for <code>shXadd</code> so we need only
one entry in the set for all the adds.</p>

<p>Some instructions might not themselves be common enough to make
available in both slots in a generic chain, but when they’re used we can
guess at what they’ll pair with and make a mini set for them.  For
example, <code>slli</code> is often followed by <code>srli</code>, <code>srai</code>, <code>add</code>, <code>sub</code>, or
<code>or</code>.</p>

<p>I had hoped to prohibit load-use-discard altogether and to keep the
emphasis on things that paired without huge unknowable delays between
the two, but the reality is that it’s too common to ignore in a
compression scheme.</p>

<p>So if the first instruction is a load it may go on to generic
arithmetic (which usually has other pairing opportunities anyway), but a
lot of cases involve conditional branches or second indirections, and
these are very hard to ignore.</p>

<p>I’d be curious to see how load-branch-discard could play out if it’s
signalled explicitly in the instruction stream.  Could branch prediction
handle it differently from the signals it gets from the ALU?</p>

<p>Similarly, could explicitly-coded double-indirection (load-load-discard)
suggest data prediction optimisations where they’re not otherwise
warranted?</p>

<h3 id="two-operand-arithmetic-rules">two-operand arithmetic rules</h3>

<p>The best-known code size optimisation is to encode three-operand
instructions with two operands by re-using the first source as the
destination register.</p>

<p>In theory this could be another mode for chain rules, where the first
result is saved for later and also forwarded to the second instruction;
but that doesn’t seem to come up that frequently in the general case (a
notable exception is pre-increment addressing).</p>

<p>Really a pair of these just sweeps up a lot of arithmetic which doesn’t
otherwise fit a chain rule. This is redundant with chain rules if two of
these in a packet use the same destination register (reserve for future
use?), and the chain version is slightly more flexible in that it starts
with two read-only inputs rather than just one.</p>

<p>But these can also be used to fill the pairing space with compact
non-arithmetic instructions, like updating the stack pointer before
function return, or implementing pre-increment with writeback before a
memory access.</p>

<p>It’s tempting to take aside some operations which are uninteresting when
<code>ra</code> and <code>rb</code> are the same register, and to re-purpose those as unary
operations like <code>neg</code>, <code>not</code>, and <code>slli rd, 1, rd</code>.</p>

<p>Another special case here would be <code>addi sp, imm</code>, where the immediate
here has to be a multiple of 16, so it can reach further, which is not a
case which applies to <code>addi rd, sp, imm</code>.</p>

<h3 id="two-in-two-out-rules">two-in-two-out rules</h3>

<p>There’s a group of operations which are often paired implicitly in CISC
architectures, like <code>mul</code>/<code>mulh</code> or <code>div</code>/<code>rem</code>, and it’s potentially
beneficial to fuse these because most of the work overlaps.</p>

<p>Instructions like those take the same two inputs for two different
operations which produce two different outputs.</p>

<p>Other obvious pairs would be <code>min</code>/<code>max</code>, <code>add</code>/<code>sub</code>, <code>and</code>/<code>andn</code>, but
these are only meaningful for coding efficiency (if you want to keep the
inputs unmodified).</p>

<p>For coding efficiency I also put <code>mv</code>/<code>mv</code>, <code>li</code>/<code>li</code>, and <code>mv</code>/<code>li</code>
(<code>li</code>/<code>mv</code> is uninteresting) into this set, where each instruction
simply takes one or other argument directly.</p>

<p>We can also put the load-pair and store-pair instructions in this
category, with a small wrinkle that the second instruction does the same
thing but takes the immediate with an extra offset (the data size of the
memory operation).</p>

<h2 id="the-compressible-instruction-sets">The compressible instruction sets</h2>

<p>I simply haven’t had enough time to decide what to put in my straw-man
proposal, yet. And I’ve run out of time to work it out.  I blame AI.</p>

<p>I’ve cobbled together enough fundamentals to make a Linux kernel smaller
than its RVC build (if my vibe-coded analysis tool is to be trusted),
but when I tried the same on a compiled Godot binary results were much,
much poorer.  I blame C++ for that.</p>

<h2 id="instruction-encoding">Instruction encoding</h2>

<p>I’ve avoided dealing with this.  What I do instead is count up the
number of bits I need to encode all the fields, and then count the
number of combinations this creates and add these to the total, and try
to keep that total less than 2^30.</p>

<p>Actually laying the bits out in an instruction packet doesn’t seem
terribly interesting.  I guess it’s nice to align the source and
destination registers of the first instruction with those of the 32-bit
instructions (in a compact implementation there’s another cycle to use
to redistribute the bits for a second interpretation).</p>]]></content><author><name>sh1boot</name></author><category term="vibe-coding" /><category term="computer-architecture" /><category term="riscv" /><summary type="html"><![CDATA[Given a mixed-length instruction stream like Thumb or RVC you encounter a few different headaches. The most infamous case is the complexity of handling un-aligned 32-bit instructions which can straddle architectural boundaries like page or cache line boundaries. There are also caching and security implications of being able to jump into the middle of an instruction, and multi-issue problems with recognising correct instruction start points, and so on…]]></summary></entry><entry><title type="html">In-filling 3D prints with holes</title><link href="https://www.xn--tkuka-m3a3v.dev/infilling-with-voids/" rel="alternate" type="text/html" title="In-filling 3D prints with holes" /><published>2026-01-18T00:00:00+00:00</published><updated>2026-07-03T18:53:07+00:00</updated><id>https://www.xn--tkuka-m3a3v.dev/infilling-with-voids</id><content type="html" xml:base="https://www.xn--tkuka-m3a3v.dev/infilling-with-voids/"><![CDATA[<p>I got to thinking about 3d printing infill the other day, and eventually
I decided that there should be ways of scooping large chunks out of the
middle, rather than in-filling with an homogenous sparse pattern, and
retaining some or all of the original strength of the homogenous fill.</p>

<p>I was thinking a sphere, originally.  And applying that subtraction
recursively in all of the solid areas left by the previous removals.
Why a sphere?  Well, that’s the mathematical ideal.  Unfortunately you
can’t print a sphere without something inside to support the roof (and
maybe floor) while it’s printing.  Also, the continuous symmetry of a
sphere doesn’t really mean much when it’s sliced into layers and has
different strength characteristics in different directions.</p>

<p>So spheres are obviously not ideal at all.  But lots of things won’t be
ideal, here.  Let’s start compromising!</p>

<p>First, the sphere’s internal support.  Ideally this support structure
would <em>not</em> be too rigid.  This is because a rigid support undermines
the even distribution of forces we’re trying to get from a sphere.  I’m
no civil engineer but I have played Bridge Builder Game, and I know that
if you make one part too strong you can force other parts to fail
because they then get all the stress.</p>

<p>Ignoring that problem, I tried to get <a href="https://www.orcaslicer.com/">OrcaSlicer</a> to add whatever it
thought appropriate to support the roof of a sphere.  Because my sphere
was a void inside of a cube it was technically an external support, and
it gave me this tree-like thing made of rings, which added a lot to the
print time.</p>

<p>I think the ideal support would have been <a href="https://www.orcaslicer.com/wiki/print_settings/strength/strength_settings_patterns#lightning">Lightning</a> but I didn’t see
that as an option.  I guess it would deface the print if it were used as
an external support, but my “external” is actually internal to the model
and I won’t be trying to remove it.  Another caveat there is you
probably wouldn’t want a deliberately flimsy printing support to break
off and rattle around inside the model at some later point.</p>

<p>So I stopped messing about with that and made a different shape which
loosely approximated a sphere but tapered to points at each end.  The
so-called “fusiform”.  It looks to me like an onion:</p>

<style>
    .click-embed {
      background-color: transparent;
      border-style: none;
      width: 100%;
    }
  </style>

<iframe class="click-embed" id="tinkercad-43k3a2lx2md" name="tinkercad-43k3a2lx2md" style="aspect-ratio:16/9;" scrolling="no" allowfullscreen="" sandbox="allow-scripts allow-same-origin allow-popups" srcdoc="&lt;style&gt; html,body {
    overflow: clip;
    margin: 0;
    background-color: transparent;
    justify-content: center center;
    text-align: center;
    height: 100%;
  }
  .maximised-image {
    object-fit: contain;
    width: 100%;
    height: 100%;
  }
  .button {
    position: absolute;
    top: 5%;
    left: 5%;
    padding: 6px 6px;
    border: 1px outset buttonborder;
    color: buttontext;
    background-color: buttonface;
    font-family: sans-serif;
    text-decoration: none;
  } &lt;/style&gt;
  &lt;img class=&quot;maximised-image&quot; src=&quot;/images/infill-onion-void.png&quot; /&gt;
  &lt;a href=&quot;https://www.tinkercad.com/embed/43k3a2lx2MD?editbtn=1&quot;&gt;&lt;div class=&quot;button&quot;&gt;Click to view in 3D&lt;/div&gt;&lt;/a&gt;">
  <a href="https://www.tinkercad.com/things/43k3a2lx2MD">
    <img src="/images/infill-onion-void.png" alt="Click to view in 3D" />
  </a>
</iframe>

<p>I think the tapers should be better than 30° overhang, but OrcaSlicer
disagreed and only stopped adding supports when I lowered the threshold
to about 27°.  I figured it was a rounding error and I just switched
supports off and assume my effort was good enough.</p>

<p>This shape is at least circular in one axis, meaning that it should be
resistant to buckling.  It comes to a point at the top and bottom, but
unlike a bridge those points are points on a 2D plane supported all
around by thicker material, and hopefully that’s good enough.  Plus I
have a few millimetres clearance before hitting the outside wall, with
infill to spread that load (don’t try that excuse when building a
bridge!).</p>

<p><a href="/blobs/cube-minus-onion.stl">download STL</a></p>

<p>With OrcaSlicer’s default settings (15% crosshatch infill, and some
walls and stuff) the cost of the onion’s walls inside of a 10cm cube
approaches the cost of the infill it replaces.  It’s less material but
only about 10% less time.</p>

<p>In order to make myself look more successful I changed the infill
configuration to use more expensive infill.  Presumably stronger infill, and
still relevant – maybe more relevant – when there’s a huge hole in the
middle.</p>

<table>
  <thead>
    <tr>
      <th>10cm cube</th>
      <th>15% c/hatch</th>
      <th>30% c/hatch</th>
      <th>15% cubic</th>
      <th>20% cubic</th>
      <th>20% gyroid</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Filament, solid</td>
      <td>78.63m</td>
      <td>133.39m</td>
      <td>79.15m</td>
      <td>98.45m</td>
      <td>94.99m</td>
    </tr>
    <tr>
      <td>Filament, onion</td>
      <td>66.77m</td>
      <td>100.35m</td>
      <td>66.12m</td>
      <td>77.91m</td>
      <td>76.47m</td>
    </tr>
    <tr>
      <td>Time, solid</td>
      <td>7h19m</td>
      <td>12h46m</td>
      <td>5h28m</td>
      <td>6h38m</td>
      <td>11h57m</td>
    </tr>
    <tr>
      <td>Time, onion</td>
      <td>6h28m</td>
      <td>10h08m</td>
      <td>5h26m</td>
      <td>6h14m</td>
      <td>9h21m</td>
    </tr>
  </tbody>
</table>

<p>I also tried adding extra, smaller onions in the corners to eat up more
volume, but it only made things worse – wall thickness remaining
constant, wall area shrinking, but enclosed volume shrinking much
faster.  So nevermind that; but it did highlight that I should test a
smaller cube, and I did that instead.</p>

<table>
  <thead>
    <tr>
      <th>5cm cube</th>
      <th>15% c/hatch</th>
      <th>30% c/hatch</th>
      <th>15% cubic</th>
      <th>20% gyroid</th>
      <th>30% gyroid</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Filament, solid</td>
      <td>12.11m</td>
      <td>18.62m</td>
      <td>12.02m</td>
      <td>13.94m</td>
      <td>18.19m</td>
    </tr>
    <tr>
      <td>Filament, onion</td>
      <td>11.80m</td>
      <td>15.45m</td>
      <td>11.49m</td>
      <td>12.70m</td>
      <td>15.11m</td>
    </tr>
    <tr>
      <td>Time, solid</td>
      <td>1h20m</td>
      <td>2h00m</td>
      <td>1h07m</td>
      <td>1h50m</td>
      <td>2h36m</td>
    </tr>
    <tr>
      <td>Time, onion</td>
      <td>1h24m</td>
      <td>1h50m</td>
      <td>1h16m</td>
      <td>1h41m</td>
      <td>2h08m</td>
    </tr>
  </tbody>
</table>

<p>The big question, though, is is it as strong as or stronger than the
homogenous infill?</p>

<p>Well, I don’t know!  I don’t have a 3D printer, and I don’t have the
means to test it scientifically, and there are a lot of different
ways to define “stronger”.  This is all abstract and theoretical.</p>

<p>The next step is to try to increase the volume of the void without
deviating too much further from our spherical ideal.</p>

<p>Enter, The <a href="https://en.wikipedia.org/wiki/Sphube">Sphube</a>!</p>

<iframe class="click-embed" id="tinkercad-7dc2ekdfy6a" name="tinkercad-7dc2ekdfy6a" style="aspect-ratio:16/9;" scrolling="no" allowfullscreen="" sandbox="allow-scripts allow-same-origin allow-popups" srcdoc="&lt;style&gt; html,body {
    overflow: clip;
    margin: 0;
    background-color: transparent;
    justify-content: center center;
    text-align: center;
    height: 100%;
  }
  .maximised-image {
    object-fit: contain;
    width: 100%;
    height: 100%;
  }
  .button {
    position: absolute;
    top: 5%;
    left: 5%;
    padding: 6px 6px;
    border: 1px outset buttonborder;
    color: buttontext;
    background-color: buttonface;
    font-family: sans-serif;
    text-decoration: none;
  } &lt;/style&gt;
  &lt;img class=&quot;maximised-image&quot; src=&quot;/images/infill-sphube-void.png&quot; /&gt;
  &lt;a href=&quot;https://www.tinkercad.com/embed/7dc2EkDfy6a?editbtn=1&quot;&gt;&lt;div class=&quot;button&quot;&gt;Click to view in 3D&lt;/div&gt;&lt;/a&gt;">
  <a href="https://www.tinkercad.com/things/7dc2EkDfy6a">
    <img src="/images/infill-sphube-void.png" alt="Click to view in 3D" />
  </a>
</iframe>

<p>You might be more familiar with the <a href="https://en.wikipedia.org/wiki/Squircle">squircle</a>, and this is just a 3D
extension on that idea.  These squircles and hypersquircles have the
benefit of being continuous curves, and so should be a bit more resistant
to buckling than a flat-faced cube would be.  That makes them a more
viable <a href="https://en.wikipedia.org/wiki/Monocoque">monocoque</a>.</p>

<p>What we’re looking at in the general form would be some kind of
shrunken, rounded monocoque approximation of the real model – a rigid
empty shell – and on top of that we build up using a practical in-fill
pattern, and on top of that we build the desired outer shape of the
model.  This construction should work like a <a href="https://en.wikipedia.org/wiki/Truss_arch_bridge">truss arch bridge</a>, with
the infill acting as truss, the monocoque providing the arch(es), and
the external model being the road surface people drive across.</p>

<p>Consider how the cross-section looks something like a bridge (two
bridges):</p>
<div style="display:flex;justify-content:center;align-items:center;">
<svg width="420" height="420">
<pattern id="infill" patternUnits="userSpaceOnUse" width="20" height="20">
  <path d="M0,10 10,0 20,10 10,20 z" stroke-width="2" fill="none" stroke="currentColor" />
</pattern>
<path d="M10,410 v-400 h400 v200 l-50,0
        c0,-120 -30,-150 -150,-150
        c-120,0 -150,30 -150,150
        c0,120 30,150 150,150
        c120,0 150,-30 150,-150
        h50 v200 h-400" fill="url(#infill)" stroke="none" />
<path d="M10,410 v-400 h400 v400 h-400 z M360,210
        c0,-120 -30,-150 -150,-150
        c-120,0 -150,30 -150,150
        c0,120 30,150 150,150
        c120,0 150,-30 150,-150" fill="none" stroke="currentColor" stroke-width="8" />
</svg>
</div>
<p>This is a really half-arsed bridge.  The infill is just a regular
pattern rather than triangles with carefully-chosen dimensions and
placement.  But it might be sufficient.  Baby steps.</p>

<p>The down-side is that trusses add tensile stress (where steel excels,
but 3D prints do not), which means that layer adhesion becomes a much
more concerning factor.  Maybe that’s why this isn’t a standard approach
already.</p>

<p>But that’s really the big idea, here.  Make all the walls into trusses
with the underside of the truss being a strong monocoque which resists
compression by being wholly convex.</p>

<p>Right now I don’t have the means to convert an arbitrary model to
its eroded, curved form.  I tried searching for model erosion tools and
mostly just found ways to make things look weathered.</p>

<p>But I do at least know how to erode a square down to a squircle.  That
can be done with something like a <a href="https://iquilezles.org/articles/smin/"><code>smoothmax()</code></a>, or a
<a href="https://en.wikipedia.org/wiki/Generalized_mean">generalised mean</a> of x and y coordinates.  As in “we’re inside the
squircle if <code>smoothmax(abs(dx), abs(dy)) &lt; r</code>”.</p>

<p>If you imagine using <code>max()</code> in place of <code>smoothmax()</code> then that would
give you a square boundary.  And if you replaced <code>max()</code> with
<code>sqrt(dx^2 + dy^2)</code> then a circle.  <code>smoothmax()</code> and generalised mean
can pick functions somewhere in between, with a parameter that allows them
to express both.</p>

<p>After a bit of digging I found a <a href="https://github.com/fogleman/sdf">simple way</a> to get from those
simple equations to an .STL file.</p>

<p><a href="/blobs/sphubes.tar.xz">STL sphubes</a> (<a href="/blobs/sphubes_lowres.tar.xz">lower-resolution STL sphubes</a>)</p>

<p>But as we learned with the sphere, this isn’t going to work because of
the roof problem, and my lack of access to an “external” lightning fill.
Now it’s a bit worse because that roof is wider and flatter.</p>

<p><a href="/blobs/cube-minus-sphube.stl">download STL anyway</a></p>

<p>So back to the compromises I must go…</p>

<p>Or I can just post this as-is and go tinker with sphubes generalised to
other platonic solids for no clear reason at all:</p>

<iframe class="click-embed" id="desmos-vaghz21ukz" name="desmos-vaghz21ukz" style="aspect-ratio:16/9;" scrolling="no" allowfullscreen="" sandbox="allow-scripts allow-same-origin" srcdoc="&lt;style&gt; html,body {
    overflow: clip;
    margin: 0;
    background-color: transparent;
    justify-content: center center;
    text-align: center;
    height: 100%;
  }
  .maximised-image {
    object-fit: contain;
    width: 100%;
    height: 100%;
  }
  .button {
    position: absolute;
    top: 5%;
    left: 5%;
    padding: 6px 6px;
    border: 1px outset buttonborder;
    color: buttontext;
    background-color: buttonface;
    font-family: sans-serif;
    text-decoration: none;
  } &lt;/style&gt;
  &lt;img class=&quot;maximised-image&quot; src=&quot;/images/dodecasphedron.png&quot; /&gt;
  &lt;a href=&quot;https://www.desmos.com/3d/vaghz21ukz?embed&quot;&gt;&lt;div class=&quot;button&quot;&gt;Click to view in Desmos&lt;/div&gt;&lt;/a&gt;">
  <a href="https://www.desmos.com/3d/vaghz21ukz">
    <img src="/images/dodecasphedron.png" alt="Click to view in Desmos" />
  </a>
</iframe>]]></content><author><name>sh1boot</name></author><category term="3d-printing" /><category term="daft-ideas" /><summary type="html"><![CDATA[I got to thinking about 3d printing infill the other day, and eventually I decided that there should be ways of scooping large chunks out of the middle, rather than in-filling with an homogenous sparse pattern, and retaining some or all of the original strength of the homogenous fill.]]></summary></entry><entry><title type="html">Choosing n different colours for graphs</title><link href="https://www.xn--tkuka-m3a3v.dev/evenly-distributed-colours/" rel="alternate" type="text/html" title="Choosing n different colours for graphs" /><published>2025-11-22T00:00:00+00:00</published><updated>2026-07-03T18:53:07+00:00</updated><id>https://www.xn--tkuka-m3a3v.dev/evenly-distributed-colours</id><content type="html" xml:base="https://www.xn--tkuka-m3a3v.dev/evenly-distributed-colours/"><![CDATA[<p>One way to generate a palette of colours for distinguishing different
lines and objects in diagrams is to take regular steps around the hue
parameter of the HSL colour wheel.  If you know how many you’ll need
then you can subdivide the space evenly, or if you do not then you can
use 1/φ as the interval instead.  But this has limitations…</p>

<p>Of course a much simpler solution is to just pick a bunch of reasonable
colours and put them in a table (eg., <a href="https://artshacker.com/wp-content/uploads/2014/12/Kellys-22-colour-chart.jpg">1</a> <a href="https://en.wikipedia.org/wiki/Tube_map#Line_colours">2</a> <a href="https://sashamaps.net/docs/resources/20-colors/">3</a>).  But doing things the hard way is more
interesting.  Also, a list which includes both black and white isn’t
solving quite the right problem for this post…</p>

<p>Spoiler alert: this won’t (directly) attempt to address accessibility
for colour-blind users.</p>

<p>I’ve written <a href="/what-ive-learned-so-far-about-web-stuff/">in the past</a> about trying to draw diagrams
and graphs on web pages.  The essential point is that you can embed SVG
with a transparent background but you must use <code>currentColor</code> as the pen
colour when you do this, so that the image is drawn in the same colour
as the text, rather than assuming that the background is always white so
you need to draw black on top.  If you use something like <a href="https://darkreader.org/">Dark
Reader</a> you’ll often see this go awry.</p>

<p>Alternatively you can force the background of the image to be a known
colour, but then on a contrasting background that can still be hard to
look at.</p>

<p>So I know how to draw lines with reasonable contrast from the
background, without assuming that the background will be light or dark.
The next problem is to add to that palette some extra colours which also
contrast with the background but are visibly distinct from each other.
Like three lines on a graph.</p>

<h2 id="single-parameter-variation">Single-parameter variation</h2>

<p>A quick-and-dirty notion of “contrast” is having a different brightness.
Having a different colour but the same brightness can be very hard to
look at.  So for starters, let’s look at just varying the colour while
keeping brightness at a single value chosen to contrast with the text or
the background.</p>

<p>$\frac{n}{\varphi} \mod 1$ has the property that every new $n$ falls
inside one of the largest gaps, and inside the largest span of
contiguous largest gaps (when there are many largest-equal gaps), etc.,
subdividing that gap/span by 1:φ, which is tolerably close to 1:2.</p>

<p>That is to say that each new value is as far as possible from as many
previous values as possible without deciding in advance how many
values you’ll need or changing step sizes at different stages in the
sequence.</p>

<p>Anyway, let’s have a look.  These colours step around the hue of the HSL
space:</p>
<style>
.example {
    display: flex;
    flex-wrap: wrap;
    width: auto;
    height: 90px;
    border: 1px solid;
    overflow: auto;
    resize: both;
}
.example span {
    width: 38px;
    min-height: 30px;
    flex-grow: 1;
    text-align: center;
    line-height: 30px;
    border: .5px solid black;
}
figcaption {
    text-align: center;
    font-family: monospace;
}
</style>

<figure>
<div class="example">
<span style="color:black;background: hsl(0deg, 60%, 70%);">0</span>
<span style="color:black;background: hsl(222deg, 60%, 70%);">1</span>
<span style="color:black;background: hsl(85deg, 60%, 70%);">2</span>
<span style="color:black;background: hsl(307deg, 60%, 70%);">3</span>
<span style="color:black;background: hsl(170deg, 60%, 70%);">4</span>
<span style="color:black;background: hsl(32deg, 60%, 70%);">5</span>
<span style="color:black;background: hsl(255deg, 60%, 70%);">6</span>
<span style="color:black;background: hsl(117deg, 60%, 70%);">7</span>
<span style="color:black;background: hsl(340deg, 60%, 70%);">8</span>
<span style="color:black;background: hsl(202deg, 60%, 70%);">9</span>
<span style="color:black;background: hsl(65deg, 60%, 70%);">10</span>
<span style="color:black;background: hsl(287deg, 60%, 70%);">11</span>
<span style="color:black;background: hsl(150deg, 60%, 70%);">12</span>
<span style="color:black;background: hsl(12deg, 60%, 70%);">13</span>
<span style="color:black;background: hsl(235deg, 60%, 70%);">14</span>
<span style="color:black;background: hsl(97deg, 60%, 70%);">15</span>
<span style="color:black;background: hsl(320deg, 60%, 70%);">16</span>
<span style="color:black;background: hsl(182deg, 60%, 70%);">17</span>
<span style="color:black;background: hsl(45deg, 60%, 70%);">18</span>
<span style="color:black;background: hsl(267deg, 60%, 70%);">19</span>
<span style="color:black;background: hsl(130deg, 60%, 70%);">20</span>
<span style="color:black;background: hsl(352deg, 60%, 70%);">21</span>
<span style="color:black;background: hsl(215deg, 60%, 70%);">22</span>
<span style="color:black;background: hsl(77deg, 60%, 70%);">23</span>
<span style="color:black;background: hsl(300deg, 60%, 70%);">24</span>
<span style="color:black;background: hsl(162deg, 60%, 70%);">25</span>
<span style="color:black;background: hsl(25deg, 60%, 70%);">26</span>
<span style="color:black;background: hsl(247deg, 60%, 70%);">27</span>
<span style="color:black;background: hsl(110deg, 60%, 70%);">28</span>
<span style="color:black;background: hsl(332deg, 60%, 70%);">29</span>
<span style="color:black;background: hsl(195deg, 60%, 70%);">30</span>
<span style="color:black;background: hsl(57deg, 60%, 70%);">31</span>
<span style="color:black;background: hsl(280deg, 60%, 70%);">32</span>
<span style="color:black;background: hsl(142deg, 60%, 70%);">33</span>
<span style="color:black;background: hsl(5deg, 60%, 70%);">34</span>
<span style="color:black;background: hsl(227deg, 60%, 70%);">35</span>
<span style="color:black;background: hsl(90deg, 60%, 70%);">36</span>
<span style="color:black;background: hsl(312deg, 60%, 70%);">37</span>
<span style="color:black;background: hsl(175deg, 60%, 70%);">38</span>
<span style="color:black;background: hsl(37deg, 60%, 70%);">39</span>
<span style="color:black;background: hsl(260deg, 60%, 70%);">40</span>
<span style="color:black;background: hsl(122deg, 60%, 70%);">41</span>
<span style="color:black;background: hsl(345deg, 60%, 70%);">42</span>
<span style="color:black;background: hsl(207deg, 60%, 70%);">43</span>
<span style="color:black;background: hsl(70deg, 60%, 70%);">44</span>
<span style="color:black;background: hsl(292deg, 60%, 70%);">45</span>
<span style="color:black;background: hsl(155deg, 60%, 70%);">46</span>
<span style="color:black;background: hsl(17deg, 60%, 70%);">47</span>
<span style="color:black;background: hsl(240deg, 60%, 70%);">48</span>
<span style="color:black;background: hsl(102deg, 60%, 70%);">49</span>
<span style="color:black;background: hsl(325deg, 60%, 70%);">50</span>
<span style="color:black;background: hsl(187deg, 60%, 70%);">51</span>
<span style="color:black;background: hsl(50deg, 60%, 70%);">52</span>
<span style="color:black;background: hsl(272deg, 60%, 70%);">53</span>
<span style="color:black;background: hsl(135deg, 60%, 70%);">54</span>
<span style="color:black;background: hsl(357deg, 60%, 70%);">55</span>
<span style="color:black;background: hsl(220deg, 60%, 70%);">56</span>
<span style="color:black;background: hsl(82deg, 60%, 70%);">57</span>
<span style="color:black;background: hsl(305deg, 60%, 70%);">58</span>
<span style="color:black;background: hsl(167deg, 60%, 70%);">59</span>
<span style="color:black;background: hsl(30deg, 60%, 70%);">60</span>
<span style="color:black;background: hsl(252deg, 60%, 70%);">61</span>
<span style="color:black;background: hsl(115deg, 60%, 70%);">62</span>
</div>
<figcaption>HSL(n / φ % 1 &times; 360&deg;, 60%, 70%)</figcaption>
</figure>

<p>It’s interactive.  You can resize the box to change the way rows line
up, so you can put different colours next to each other for comparison.</p>

<p>And if you do that you’ll see a problem.  It seems to visit relatively
few colours before coming back around to use something very similar to a
colour that’s already been used.  So things get indistinct much sooner
than one might hope.</p>

<p>Fun fact: When taking steps of 1/φ mod 1 those “kind of similar” colours
occur at distances which are Fibonacci numbers.  Resize the box to have
a Fibonacci number of columns and you’ll see stripes.</p>

<p>HSL is tied to the numerical coding of colour in RGB.  It’s made out of
up and down ramps of R and G and B without regard to how they’re
perceived.  OKLCh, on the other hand, is tied more closely to human
perception.  Maybe that’ll help:</p>

<figure>
<div class="example">
<span style="color:black;background: oklch(75% 30% 0deg);">0</span>
<span style="color:black;background: oklch(75% 30% 222deg);">1</span>
<span style="color:black;background: oklch(75% 30% 85deg);">2</span>
<span style="color:black;background: oklch(75% 30% 307deg);">3</span>
<span style="color:black;background: oklch(75% 30% 170deg);">4</span>
<span style="color:black;background: oklch(75% 30% 32deg);">5</span>
<span style="color:black;background: oklch(75% 30% 255deg);">6</span>
<span style="color:black;background: oklch(75% 30% 117deg);">7</span>
<span style="color:black;background: oklch(75% 30% 340deg);">8</span>
<span style="color:black;background: oklch(75% 30% 202deg);">9</span>
<span style="color:black;background: oklch(75% 30% 65deg);">10</span>
<span style="color:black;background: oklch(75% 30% 287deg);">11</span>
<span style="color:black;background: oklch(75% 30% 150deg);">12</span>
<span style="color:black;background: oklch(75% 30% 12deg);">13</span>
<span style="color:black;background: oklch(75% 30% 235deg);">14</span>
<span style="color:black;background: oklch(75% 30% 97deg);">15</span>
<span style="color:black;background: oklch(75% 30% 320deg);">16</span>
<span style="color:black;background: oklch(75% 30% 182deg);">17</span>
<span style="color:black;background: oklch(75% 30% 45deg);">18</span>
<span style="color:black;background: oklch(75% 30% 267deg);">19</span>
<span style="color:black;background: oklch(75% 30% 130deg);">20</span>
<span style="color:black;background: oklch(75% 30% 352deg);">21</span>
<span style="color:black;background: oklch(75% 30% 215deg);">22</span>
<span style="color:black;background: oklch(75% 30% 77deg);">23</span>
<span style="color:black;background: oklch(75% 30% 300deg);">24</span>
<span style="color:black;background: oklch(75% 30% 162deg);">25</span>
<span style="color:black;background: oklch(75% 30% 25deg);">26</span>
<span style="color:black;background: oklch(75% 30% 247deg);">27</span>
<span style="color:black;background: oklch(75% 30% 110deg);">28</span>
<span style="color:black;background: oklch(75% 30% 332deg);">29</span>
<span style="color:black;background: oklch(75% 30% 195deg);">30</span>
<span style="color:black;background: oklch(75% 30% 57deg);">31</span>
<span style="color:black;background: oklch(75% 30% 280deg);">32</span>
<span style="color:black;background: oklch(75% 30% 142deg);">33</span>
<span style="color:black;background: oklch(75% 30% 5deg);">34</span>
<span style="color:black;background: oklch(75% 30% 227deg);">35</span>
<span style="color:black;background: oklch(75% 30% 90deg);">36</span>
<span style="color:black;background: oklch(75% 30% 312deg);">37</span>
<span style="color:black;background: oklch(75% 30% 175deg);">38</span>
<span style="color:black;background: oklch(75% 30% 37deg);">39</span>
<span style="color:black;background: oklch(75% 30% 260deg);">40</span>
<span style="color:black;background: oklch(75% 30% 122deg);">41</span>
<span style="color:black;background: oklch(75% 30% 345deg);">42</span>
<span style="color:black;background: oklch(75% 30% 207deg);">43</span>
<span style="color:black;background: oklch(75% 30% 70deg);">44</span>
<span style="color:black;background: oklch(75% 30% 292deg);">45</span>
<span style="color:black;background: oklch(75% 30% 155deg);">46</span>
<span style="color:black;background: oklch(75% 30% 17deg);">47</span>
<span style="color:black;background: oklch(75% 30% 240deg);">48</span>
<span style="color:black;background: oklch(75% 30% 102deg);">49</span>
<span style="color:black;background: oklch(75% 30% 325deg);">50</span>
<span style="color:black;background: oklch(75% 30% 187deg);">51</span>
<span style="color:black;background: oklch(75% 30% 50deg);">52</span>
<span style="color:black;background: oklch(75% 30% 272deg);">53</span>
<span style="color:black;background: oklch(75% 30% 135deg);">54</span>
<span style="color:black;background: oklch(75% 30% 357deg);">55</span>
<span style="color:black;background: oklch(75% 30% 220deg);">56</span>
<span style="color:black;background: oklch(75% 30% 82deg);">57</span>
<span style="color:black;background: oklch(75% 30% 305deg);">58</span>
<span style="color:black;background: oklch(75% 30% 167deg);">59</span>
<span style="color:black;background: oklch(75% 30% 30deg);">60</span>
<span style="color:black;background: oklch(75% 30% 252deg);">61</span>
<span style="color:black;background: oklch(75% 30% 115deg);">62</span>
</div>
<figcaption>OKLCh(75% 30% (n / φ % 1 &times; 360&deg;))</figcaption>
</figure>

<p>This has the unfortunate effect (normally a feature) of flattening the
lightness of each colour, so none of the colours are distinguished by
the perceptual lightness variations which would sneak through HSL.
Maybe the hues are more evenly spread, but I can’t see it.</p>

<p>On the positive side, the contrast with the numbers written on the boxes
is more even.  That’s important.</p>

<p>Another problem with OKLCh is that it’s so easy to stumble out of gamut
(the range of colours which the display can represent) and this brings
<a href="https://en.wikipedia.org/wiki/Color_management#Gamut_mapping">gamut mapping</a> into play.  The way to do that is not well defined
right now and it may never be defined in a way that’s useful for these
purposes.  It’s not always obvious how and when the test swatches I’m
using here will be clipped to fit the display capabilities, so it’s hard
to be confident that everybody sees the same thing.</p>

<p>That’s a problem with human perception anyway, but this makes it so much
worse.</p>

<p>But let’s persevere with it a while longer…</p>

<h2 id="multi-parameter-variation">Multi-parameter variation</h2>

<p>Changing just the one parameter doesn’t seem to get us a lot of distinct
choices.  The next thing we can change without interfering with our
fixed brightness constraint is saturation, or C for “chromatic
intensity” in OKLCh.  Alternatively, C represents the distance which the
a (green-red) and b (blue-yellow) values are from 0,0 in OKLab, so we
could vary a and b instead of C and h.</p>

<p>So how do you get the properties of $\frac{n}{\varphi} \mod 1$ in two
dimensions?  It turns out a (the?) <a href="https://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/">generalisation</a> takes us to the <a href="https://en.wikipedia.org/wiki/Plastic_ratio">plastic ratio</a> (ρ=1.3247), next.  In
short, multiply $n$ by  (1/ρ, 1/ρ²), or (0.7548776662, 0.5698402910) mod</p>
<ol>
  <li>This maximises the minimum distance between any two points in two
dimensions.</li>
</ol>

<p>Here’s how that looks in OKLab:</p>
<figure>
<div class="example">
<span style="color:black;background: oklab(.75 -0.2 -0.2);">0</span>
<span style="color:black;background: oklab(.75 0.102 0.028);">1</span>
<span style="color:black;background: oklab(.75 0.004 -0.144);">2</span>
<span style="color:black;background: oklab(.75 -0.094 0.084);">3</span>
<span style="color:black;background: oklab(.75 -0.192 -0.088);">4</span>
<span style="color:black;background: oklab(.75 0.11 0.14);">5</span>
<span style="color:black;background: oklab(.75 0.012 -0.032);">6</span>
<span style="color:black;background: oklab(.75 -0.086 0.196);">7</span>
<span style="color:black;background: oklab(.75 -0.184 0.023);">8</span>
<span style="color:black;background: oklab(.75 0.118 -0.149);">9</span>
<span style="color:black;background: oklab(.75 0.02 0.079);">10</span>
<span style="color:black;background: oklab(.75 -0.079 -0.093);">11</span>
<span style="color:black;background: oklab(.75 -0.177 0.135);">12</span>
<span style="color:black;background: oklab(.75 0.125 -0.037);">13</span>
<span style="color:black;background: oklab(.75 0.027 0.191);">14</span>
<span style="color:black;background: oklab(.75 -0.071 0.019);">15</span>
<span style="color:black;background: oklab(.75 -0.169 -0.153);">16</span>
<span style="color:black;background: oklab(.75 0.133 0.075);">17</span>
<span style="color:black;background: oklab(.75 0.035 -0.097);">18</span>
<span style="color:black;background: oklab(.75 -0.063 0.131);">19</span>
<span style="color:black;background: oklab(.75 -0.161 -0.041);">20</span>
<span style="color:black;background: oklab(.75 0.141 0.187);">21</span>
<span style="color:black;background: oklab(.75 0.043 0.015);">22</span>
<span style="color:black;background: oklab(.75 -0.055 -0.157);">23</span>
<span style="color:black;background: oklab(.75 -0.153 0.07);">24</span>
<span style="color:black;background: oklab(.75 0.149 -0.102);">25</span>
<span style="color:black;background: oklab(.75 0.051 0.126);">26</span>
<span style="color:black;background: oklab(.75 -0.047 -0.046);">27</span>
<span style="color:black;background: oklab(.75 -0.145 0.182);">28</span>
<span style="color:black;background: oklab(.75 0.157 0.01);">29</span>
<span style="color:black;background: oklab(.75 0.059 -0.162);">30</span>
<span style="color:black;background: oklab(.75 -0.04 0.066);">31</span>
<span style="color:black;background: oklab(.75 -0.138 -0.106);">32</span>
<span style="color:black;background: oklab(.75 0.164 0.122);">33</span>
<span style="color:black;background: oklab(.75 0.066 -0.05);">34</span>
<span style="color:black;background: oklab(.75 -0.032 0.178);">35</span>
<span style="color:black;background: oklab(.75 -0.13 0.006);">36</span>
<span style="color:black;background: oklab(.75 0.172 -0.166);">37</span>
<span style="color:black;background: oklab(.75 0.074 0.062);">38</span>
<span style="color:black;background: oklab(.75 -0.024 -0.11);">39</span>
<span style="color:black;background: oklab(.75 -0.122 0.117);">40</span>
<span style="color:black;background: oklab(.75 0.18 -0.055);">41</span>
<span style="color:black;background: oklab(.75 0.082 0.173);">42</span>
<span style="color:black;background: oklab(.75 -0.016 0.001);">43</span>
<span style="color:black;background: oklab(.75 -0.114 -0.171);">44</span>
<span style="color:black;background: oklab(.75 0.188 0.057);">45</span>
<span style="color:black;background: oklab(.75 0.09 -0.115);">46</span>
<span style="color:black;background: oklab(.75 -0.008 0.113);">47</span>
<span style="color:black;background: oklab(.75 -0.106 -0.059);">48</span>
<span style="color:black;background: oklab(.75 0.196 0.169);">49</span>
<span style="color:black;background: oklab(.75 0.098 -0.003);">50</span>
<span style="color:black;background: oklab(.75 -0.0 -0.175);">51</span>
<span style="color:black;background: oklab(.75 -0.099 0.053);">52</span>
<span style="color:black;background: oklab(.75 -0.197 -0.119);">53</span>
<span style="color:black;background: oklab(.75 0.105 0.109);">54</span>
<span style="color:black;background: oklab(.75 0.007 -0.064);">55</span>
<span style="color:black;background: oklab(.75 -0.091 0.164);">56</span>
<span style="color:black;background: oklab(.75 -0.189 -0.008);">57</span>
<span style="color:black;background: oklab(.75 0.113 -0.18);">58</span>
<span style="color:black;background: oklab(.75 0.015 0.048);">59</span>
<span style="color:black;background: oklab(.75 -0.083 -0.124);">60</span>
<span style="color:black;background: oklab(.75 -0.181 0.104);">61</span>
<span style="color:black;background: oklab(.75 0.121 -0.068);">62</span>
</div>
<figcaption>OKLab(.75 (n / ρ % 1 &times; .4 - .2) (n / ρ² % 1 &times; .4 - .2))</figcaption>
</figure>

<p>This gives uniform coverage of a square in the chroma plane, so it has
pointy corners where the saturation reaches further out than it can near
the edges.  It’s probably going out of gamut and being clipped in
unpredictable ways.</p>

<p>In another problem space we could use rejection sampling to avoid those
ugly corners, but then we can’t define a colour as a simple function of
$n$.  Instead, a technique to map two uniform random values (a square)
to a uniform distribution over a disc is to take one value as the radius
and the other as an angle around that circle.  Squaring the value used
as radius compensates for the over-concentration of points around the
centre (proof left as an exercise for Google search).</p>

<p>Does this retain the mathematical rigour of low-discrepancy sequences?
No.  Not at all.  But it’s the best I have right now.</p>

<p>And here’s what that gives us for OKLCh:</p>
<figure>
<div class="example">
<span style="color:black;background: oklch(.75 calc(sqrt(0.0) * .2) 0deg);">0</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.755) * .2) 205deg);">1</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.51) * .2) 50deg);">2</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.265) * .2) 255deg);">3</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.02) * .2) 101deg);">4</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.774) * .2) 306deg);">5</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.529) * .2) 151deg);">6</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.284) * .2) 356deg);">7</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.039) * .2) 201deg);">8</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.794) * .2) 46deg);">9</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.549) * .2) 251deg);">10</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.304) * .2) 97deg);">11</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.059) * .2) 302deg);">12</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.813) * .2) 147deg);">13</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.568) * .2) 352deg);">14</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.323) * .2) 197deg);">15</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.078) * .2) 42deg);">16</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.833) * .2) 247deg);">17</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.588) * .2) 93deg);">18</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.343) * .2) 298deg);">19</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.098) * .2) 143deg);">20</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.852) * .2) 348deg);">21</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.607) * .2) 193deg);">22</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.362) * .2) 38deg);">23</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.117) * .2) 243deg);">24</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.872) * .2) 89deg);">25</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.627) * .2) 294deg);">26</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.382) * .2) 139deg);">27</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.137) * .2) 344deg);">28</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.891) * .2) 189deg);">29</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.646) * .2) 34deg);">30</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.401) * .2) 239deg);">31</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.156) * .2) 85deg);">32</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.911) * .2) 290deg);">33</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.666) * .2) 135deg);">34</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.421) * .2) 340deg);">35</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.176) * .2) 185deg);">36</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.93) * .2) 30deg);">37</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.685) * .2) 235deg);">38</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.44) * .2) 81deg);">39</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.195) * .2) 286deg);">40</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.95) * .2) 131deg);">41</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.705) * .2) 336deg);">42</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.46) * .2) 181deg);">43</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.215) * .2) 26deg);">44</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.969) * .2) 231deg);">45</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.724) * .2) 77deg);">46</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.479) * .2) 282deg);">47</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.234) * .2) 127deg);">48</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.989) * .2) 332deg);">49</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.744) * .2) 177deg);">50</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.499) * .2) 22deg);">51</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.254) * .2) 227deg);">52</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.009) * .2) 73deg);">53</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.763) * .2) 278deg);">54</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.518) * .2) 123deg);">55</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.273) * .2) 328deg);">56</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.028) * .2) 173deg);">57</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.783) * .2) 18deg);">58</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.538) * .2) 223deg);">59</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.293) * .2) 69deg);">60</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.048) * .2) 274deg);">61</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.802) * .2) 119deg);">62</span>
</div>
<figcaption>OKLCh(.75 (sqrt(n / ρ % 1) &times; .2) (n / ρ² % 1 &times; 360&deg;))</figcaption>
</figure>

<p>Something really unfortunate about the plastic ratio shows up, here.
It’s too close to 4/3.  This has the consequence that one parameter
appears nearly periodic mod 4, with a very slow precession.  For
example, in the polar test case, we start at 0 so the first radius is
zero (grey), and every fourth colour after that is very close to grey as
well, and it takes a long time to climb out of that hole.</p>

<p>By switching the axes around then the problem will manifest in the hue
instead:</p>

<figure>
<div class="example">
<span style="color:black;background: oklch(.75 calc(sqrt(0.0) * .2) 0deg);">0</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.57) * .2) 272deg);">1</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.14) * .2) 184deg);">2</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.71) * .2) 95deg);">3</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.279) * .2) 7deg);">4</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.849) * .2) 279deg);">5</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.419) * .2) 191deg);">6</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.989) * .2) 102deg);">7</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.559) * .2) 14deg);">8</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.129) * .2) 286deg);">9</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.698) * .2) 198deg);">10</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.268) * .2) 109deg);">11</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.838) * .2) 21deg);">12</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.408) * .2) 293deg);">13</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.978) * .2) 205deg);">14</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.548) * .2) 116deg);">15</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.117) * .2) 28deg);">16</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.687) * .2) 300deg);">17</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.257) * .2) 212deg);">18</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.827) * .2) 123deg);">19</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.397) * .2) 35deg);">20</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.967) * .2) 307deg);">21</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.536) * .2) 219deg);">22</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.106) * .2) 130deg);">23</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.676) * .2) 42deg);">24</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.246) * .2) 314deg);">25</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.816) * .2) 226deg);">26</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.386) * .2) 137deg);">27</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.956) * .2) 49deg);">28</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.525) * .2) 321deg);">29</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.095) * .2) 233deg);">30</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.665) * .2) 144deg);">31</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.235) * .2) 56deg);">32</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.805) * .2) 328deg);">33</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.375) * .2) 240deg);">34</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.944) * .2) 151deg);">35</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.514) * .2) 63deg);">36</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.084) * .2) 335deg);">37</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.654) * .2) 247deg);">38</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.224) * .2) 158deg);">39</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.794) * .2) 70deg);">40</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.363) * .2) 342deg);">41</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.933) * .2) 254deg);">42</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.503) * .2) 166deg);">43</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.073) * .2) 77deg);">44</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.643) * .2) 349deg);">45</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.213) * .2) 261deg);">46</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.782) * .2) 173deg);">47</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.352) * .2) 84deg);">48</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.922) * .2) 356deg);">49</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.492) * .2) 268deg);">50</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.062) * .2) 180deg);">51</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.632) * .2) 91deg);">52</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.202) * .2) 3deg);">53</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.771) * .2) 275deg);">54</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.341) * .2) 187deg);">55</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.911) * .2) 98deg);">56</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.481) * .2) 10deg);">57</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.051) * .2) 282deg);">58</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.621) * .2) 194deg);">59</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.19) * .2) 105deg);">60</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.76) * .2) 17deg);">61</span>
<span style="color:black;background: oklch(.75 calc(sqrt(0.33) * .2) 289deg);">62</span>
</div>
<figcaption>OKLCh(.75 (sqrt(n / ρ² % 1) &times; .2) (n / ρ % 1 &times; 360&deg;))</figcaption>
</figure>

<p>For completeness, let’s also try OKLCh but with fixed C and varying the
lightness instead.</p>

<figure>
<div class="example">
<span style="color:black;background: oklch(0.63 .12 0deg);">0</span>
<span style="color:black;background: oklch(0.819 .12 205deg);">1</span>
<span style="color:black;background: oklch(0.757 .12 50deg);">2</span>
<span style="color:black;background: oklch(0.696 .12 255deg);">3</span>
<span style="color:black;background: oklch(0.635 .12 101deg);">4</span>
<span style="color:black;background: oklch(0.824 .12 306deg);">5</span>
<span style="color:black;background: oklch(0.762 .12 151deg);">6</span>
<span style="color:black;background: oklch(0.701 .12 356deg);">7</span>
<span style="color:black;background: oklch(0.64 .12 201deg);">8</span>
<span style="color:black;background: oklch(0.828 .12 46deg);">9</span>
<span style="color:black;background: oklch(0.767 .12 251deg);">10</span>
<span style="color:black;background: oklch(0.706 .12 97deg);">11</span>
<span style="color:black;background: oklch(0.645 .12 302deg);">12</span>
<span style="color:black;background: oklch(0.833 .12 147deg);">13</span>
<span style="color:black;background: oklch(0.772 .12 352deg);">14</span>
<span style="color:black;background: oklch(0.711 .12 197deg);">15</span>
<span style="color:black;background: oklch(0.65 .12 42deg);">16</span>
<span style="color:black;background: oklch(0.838 .12 247deg);">17</span>
<span style="color:black;background: oklch(0.777 .12 93deg);">18</span>
<span style="color:black;background: oklch(0.716 .12 298deg);">19</span>
<span style="color:black;background: oklch(0.654 .12 143deg);">20</span>
<span style="color:black;background: oklch(0.843 .12 348deg);">21</span>
<span style="color:black;background: oklch(0.782 .12 193deg);">22</span>
<span style="color:black;background: oklch(0.721 .12 38deg);">23</span>
<span style="color:black;background: oklch(0.659 .12 243deg);">24</span>
<span style="color:black;background: oklch(0.848 .12 89deg);">25</span>
<span style="color:black;background: oklch(0.787 .12 294deg);">26</span>
<span style="color:black;background: oklch(0.725 .12 139deg);">27</span>
<span style="color:black;background: oklch(0.664 .12 344deg);">28</span>
<span style="color:black;background: oklch(0.853 .12 189deg);">29</span>
<span style="color:black;background: oklch(0.792 .12 34deg);">30</span>
<span style="color:black;background: oklch(0.73 .12 239deg);">31</span>
<span style="color:black;background: oklch(0.669 .12 85deg);">32</span>
<span style="color:black;background: oklch(0.858 .12 290deg);">33</span>
<span style="color:black;background: oklch(0.796 .12 135deg);">34</span>
<span style="color:black;background: oklch(0.735 .12 340deg);">35</span>
<span style="color:black;background: oklch(0.674 .12 185deg);">36</span>
<span style="color:black;background: oklch(0.863 .12 30deg);">37</span>
<span style="color:black;background: oklch(0.801 .12 235deg);">38</span>
<span style="color:black;background: oklch(0.74 .12 81deg);">39</span>
<span style="color:black;background: oklch(0.679 .12 286deg);">40</span>
<span style="color:black;background: oklch(0.867 .12 131deg);">41</span>
<span style="color:black;background: oklch(0.806 .12 336deg);">42</span>
<span style="color:black;background: oklch(0.745 .12 181deg);">43</span>
<span style="color:black;background: oklch(0.684 .12 26deg);">44</span>
<span style="color:black;background: oklch(0.872 .12 231deg);">45</span>
<span style="color:black;background: oklch(0.811 .12 77deg);">46</span>
<span style="color:black;background: oklch(0.75 .12 282deg);">47</span>
<span style="color:black;background: oklch(0.689 .12 127deg);">48</span>
<span style="color:black;background: oklch(0.877 .12 332deg);">49</span>
<span style="color:black;background: oklch(0.816 .12 177deg);">50</span>
<span style="color:black;background: oklch(0.755 .12 22deg);">51</span>
<span style="color:black;background: oklch(0.693 .12 227deg);">52</span>
<span style="color:black;background: oklch(0.632 .12 73deg);">53</span>
<span style="color:black;background: oklch(0.821 .12 278deg);">54</span>
<span style="color:black;background: oklch(0.76 .12 123deg);">55</span>
<span style="color:black;background: oklch(0.698 .12 328deg);">56</span>
<span style="color:black;background: oklch(0.637 .12 173deg);">57</span>
<span style="color:black;background: oklch(0.826 .12 18deg);">58</span>
<span style="color:black;background: oklch(0.764 .12 223deg);">59</span>
<span style="color:black;background: oklch(0.703 .12 69deg);">60</span>
<span style="color:black;background: oklch(0.642 .12 274deg);">61</span>
<span style="color:black;background: oklch(0.831 .12 119deg);">62</span>
</div>
<figcaption>OKLCh((n/ρ % 1 &times; .25 + .63) .12 (n / ρ² % 1 &times; 360&deg;))</figcaption>
</figure>

<p>Or swapping the axes:</p>

<figure>
<div class="example">
<span style="color:black;background: oklch(0.63 .12 0deg);">0</span>
<span style="color:black;background: oklch(0.772 .12 272deg);">1</span>
<span style="color:black;background: oklch(0.665 .12 184deg);">2</span>
<span style="color:black;background: oklch(0.807 .12 95deg);">3</span>
<span style="color:black;background: oklch(0.7 .12 7deg);">4</span>
<span style="color:black;background: oklch(0.842 .12 279deg);">5</span>
<span style="color:black;background: oklch(0.735 .12 191deg);">6</span>
<span style="color:black;background: oklch(0.877 .12 102deg);">7</span>
<span style="color:black;background: oklch(0.77 .12 14deg);">8</span>
<span style="color:black;background: oklch(0.662 .12 286deg);">9</span>
<span style="color:black;background: oklch(0.805 .12 198deg);">10</span>
<span style="color:black;background: oklch(0.697 .12 109deg);">11</span>
<span style="color:black;background: oklch(0.84 .12 21deg);">12</span>
<span style="color:black;background: oklch(0.732 .12 293deg);">13</span>
<span style="color:black;background: oklch(0.874 .12 205deg);">14</span>
<span style="color:black;background: oklch(0.767 .12 116deg);">15</span>
<span style="color:black;background: oklch(0.659 .12 28deg);">16</span>
<span style="color:black;background: oklch(0.802 .12 300deg);">17</span>
<span style="color:black;background: oklch(0.694 .12 212deg);">18</span>
<span style="color:black;background: oklch(0.837 .12 123deg);">19</span>
<span style="color:black;background: oklch(0.729 .12 35deg);">20</span>
<span style="color:black;background: oklch(0.872 .12 307deg);">21</span>
<span style="color:black;background: oklch(0.764 .12 219deg);">22</span>
<span style="color:black;background: oklch(0.657 .12 130deg);">23</span>
<span style="color:black;background: oklch(0.799 .12 42deg);">24</span>
<span style="color:black;background: oklch(0.692 .12 314deg);">25</span>
<span style="color:black;background: oklch(0.834 .12 226deg);">26</span>
<span style="color:black;background: oklch(0.726 .12 137deg);">27</span>
<span style="color:black;background: oklch(0.869 .12 49deg);">28</span>
<span style="color:black;background: oklch(0.761 .12 321deg);">29</span>
<span style="color:black;background: oklch(0.654 .12 233deg);">30</span>
<span style="color:black;background: oklch(0.796 .12 144deg);">31</span>
<span style="color:black;background: oklch(0.689 .12 56deg);">32</span>
<span style="color:black;background: oklch(0.831 .12 328deg);">33</span>
<span style="color:black;background: oklch(0.724 .12 240deg);">34</span>
<span style="color:black;background: oklch(0.866 .12 151deg);">35</span>
<span style="color:black;background: oklch(0.759 .12 63deg);">36</span>
<span style="color:black;background: oklch(0.651 .12 335deg);">37</span>
<span style="color:black;background: oklch(0.793 .12 247deg);">38</span>
<span style="color:black;background: oklch(0.686 .12 158deg);">39</span>
<span style="color:black;background: oklch(0.828 .12 70deg);">40</span>
<span style="color:black;background: oklch(0.721 .12 342deg);">41</span>
<span style="color:black;background: oklch(0.863 .12 254deg);">42</span>
<span style="color:black;background: oklch(0.756 .12 166deg);">43</span>
<span style="color:black;background: oklch(0.648 .12 77deg);">44</span>
<span style="color:black;background: oklch(0.791 .12 349deg);">45</span>
<span style="color:black;background: oklch(0.683 .12 261deg);">46</span>
<span style="color:black;background: oklch(0.826 .12 173deg);">47</span>
<span style="color:black;background: oklch(0.718 .12 84deg);">48</span>
<span style="color:black;background: oklch(0.861 .12 356deg);">49</span>
<span style="color:black;background: oklch(0.753 .12 268deg);">50</span>
<span style="color:black;background: oklch(0.645 .12 180deg);">51</span>
<span style="color:black;background: oklch(0.788 .12 91deg);">52</span>
<span style="color:black;background: oklch(0.68 .12 3deg);">53</span>
<span style="color:black;background: oklch(0.823 .12 275deg);">54</span>
<span style="color:black;background: oklch(0.715 .12 187deg);">55</span>
<span style="color:black;background: oklch(0.858 .12 98deg);">56</span>
<span style="color:black;background: oklch(0.75 .12 10deg);">57</span>
<span style="color:black;background: oklch(0.643 .12 282deg);">58</span>
<span style="color:black;background: oklch(0.785 .12 194deg);">59</span>
<span style="color:black;background: oklch(0.678 .12 105deg);">60</span>
<span style="color:black;background: oklch(0.82 .12 17deg);">61</span>
<span style="color:black;background: oklch(0.713 .12 289deg);">62</span>
</div>
<figcaption>OKLCh((n/ρ² % 1 &times; .25 + .63) .12 (n / ρ % 1 &times; 360&deg;))</figcaption>
</figure>

<h2 id="varying-all-three-parameters">Varying all three parameters</h2>

<p>Next step is to make adjustments to all three parameters; but only
modest adjustments so that all results still have strong contrast with
the background colour.</p>

<p>I don’t know of a name for what comes after Golden and Plastic, but its
value is g=1.22074408460575947536, and the reciprocals of the powers are
(0.8191725134, 0.6710436067, 0.5497004779).</p>

<p>The lightness figure needs compression to ensure things don’t wander too
far and start failing to meet the original contrast limitation.</p>

<figure>
<div class="example">
<span style="color:black;background: oklab(0.63 -0.175 -0.175);">0</span>
<span style="color:black;background: oklab(0.835 0.06 0.017);">1</span>
<span style="color:black;background: oklab(0.79 -0.055 -0.14);">2</span>
<span style="color:black;background: oklab(0.744 -0.17 0.052);">3</span>
<span style="color:black;background: oklab(0.699 0.064 -0.105);">4</span>
<span style="color:black;background: oklab(0.654 -0.051 0.087);">5</span>
<span style="color:black;background: oklab(0.859 -0.166 -0.071);">6</span>
<span style="color:black;background: oklab(0.814 0.069 0.122);">7</span>
<span style="color:black;background: oklab(0.768 -0.046 -0.036);">8</span>
<span style="color:black;background: oklab(0.723 -0.161 0.157);">9</span>
<span style="color:black;background: oklab(0.678 0.074 -0.001);">10</span>
<span style="color:black;background: oklab(0.633 -0.041 -0.159);">11</span>
<span style="color:black;background: oklab(0.838 -0.157 0.034);">12</span>
<span style="color:black;background: oklab(0.792 0.078 -0.124);">13</span>
<span style="color:black;background: oklab(0.747 -0.037 0.069);">14</span>
<span style="color:black;background: oklab(0.702 -0.152 -0.089);">15</span>
<span style="color:black;background: oklab(0.657 0.083 0.103);">16</span>
<span style="color:black;background: oklab(0.861 -0.032 -0.054);">17</span>
<span style="color:black;background: oklab(0.816 -0.147 0.138);">18</span>
<span style="color:black;background: oklab(0.771 0.087 -0.019);">19</span>
<span style="color:black;background: oklab(0.726 -0.028 0.173);">20</span>
<span style="color:black;background: oklab(0.681 -0.143 0.015);">21</span>
<span style="color:black;background: oklab(0.635 0.092 -0.142);">22</span>
<span style="color:black;background: oklab(0.84 -0.023 0.05);">23</span>
<span style="color:black;background: oklab(0.795 -0.138 -0.108);">24</span>
<span style="color:black;background: oklab(0.75 0.097 0.085);">25</span>
<span style="color:black;background: oklab(0.705 -0.019 -0.073);">26</span>
<span style="color:black;background: oklab(0.659 -0.134 0.12);">27</span>
<span style="color:black;background: oklab(0.864 0.101 -0.038);">28</span>
<span style="color:black;background: oklab(0.819 -0.014 0.154);">29</span>
<span style="color:black;background: oklab(0.774 -0.129 -0.003);">30</span>
<span style="color:black;background: oklab(0.729 0.106 -0.161);">31</span>
<span style="color:black;background: oklab(0.683 -0.009 0.032);">32</span>
<span style="color:black;background: oklab(0.638 -0.124 -0.126);">33</span>
<span style="color:black;background: oklab(0.843 0.11 0.066);">34</span>
<span style="color:black;background: oklab(0.798 -0.005 -0.091);">35</span>
<span style="color:black;background: oklab(0.753 -0.12 0.101);">36</span>
<span style="color:black;background: oklab(0.707 0.115 -0.056);">37</span>
<span style="color:black;background: oklab(0.662 -0.0 0.136);">38</span>
<span style="color:black;background: oklab(0.867 -0.115 -0.022);">39</span>
<span style="color:black;background: oklab(0.822 0.12 0.171);">40</span>
<span style="color:black;background: oklab(0.777 0.004 0.013);">41</span>
<span style="color:black;background: oklab(0.731 -0.111 -0.144);">42</span>
<span style="color:black;background: oklab(0.686 0.124 0.048);">43</span>
<span style="color:black;background: oklab(0.641 0.009 -0.11);">44</span>
<span style="color:black;background: oklab(0.846 -0.106 0.083);">45</span>
<span style="color:black;background: oklab(0.8 0.129 -0.075);">46</span>
<span style="color:black;background: oklab(0.755 0.014 0.118);">47</span>
<span style="color:black;background: oklab(0.71 -0.101 -0.04);">48</span>
<span style="color:black;background: oklab(0.665 0.133 0.152);">49</span>
<span style="color:black;background: oklab(0.87 0.018 -0.005);">50</span>
<span style="color:black;background: oklab(0.824 -0.097 -0.163);">51</span>
<span style="color:black;background: oklab(0.779 0.138 0.03);">52</span>
<span style="color:black;background: oklab(0.734 0.023 -0.128);">53</span>
<span style="color:black;background: oklab(0.689 -0.092 0.064);">54</span>
<span style="color:black;background: oklab(0.644 0.143 -0.093);">55</span>
<span style="color:black;background: oklab(0.848 0.027 0.099);">56</span>
<span style="color:black;background: oklab(0.803 -0.088 -0.058);">57</span>
<span style="color:black;background: oklab(0.758 0.147 0.134);">58</span>
<span style="color:black;background: oklab(0.713 0.032 -0.024);">59</span>
<span style="color:black;background: oklab(0.668 -0.083 0.169);">60</span>
<span style="color:black;background: oklab(0.872 0.152 0.011);">61</span>
<span style="color:black;background: oklab(0.827 0.037 -0.146);">62</span>
</div>
<figcaption>OKLab((n/g % 1 &times; .25 + .63) (n/g² % 1 &times; .35 - .175) (n/g³ % 1 &times; .35 - .175))</figcaption>
</figure>

<p>But I preferred the result with the terms in a different order:</p>

<figure>
<div class="example">
<span style="color:black;background: oklab(0.63 -0.175 -0.175);">0</span>
<span style="color:black;background: oklab(0.798 0.017 0.112);">1</span>
<span style="color:black;background: oklab(0.716 -0.14 0.048);">2</span>
<span style="color:black;background: oklab(0.633 0.052 -0.015);">3</span>
<span style="color:black;background: oklab(0.801 -0.105 -0.078);">4</span>
<span style="color:black;background: oklab(0.719 0.087 -0.141);">5</span>
<span style="color:black;background: oklab(0.637 -0.071 0.145);">6</span>
<span style="color:black;background: oklab(0.804 0.122 0.082);">7</span>
<span style="color:black;background: oklab(0.722 -0.036 0.019);">8</span>
<span style="color:black;background: oklab(0.64 0.157 -0.045);">9</span>
<span style="color:black;background: oklab(0.808 -0.001 -0.108);">10</span>
<span style="color:black;background: oklab(0.725 -0.159 -0.171);">11</span>
<span style="color:black;background: oklab(0.643 0.034 0.116);">12</span>
<span style="color:black;background: oklab(0.811 -0.124 0.052);">13</span>
<span style="color:black;background: oklab(0.729 0.069 -0.011);">14</span>
<span style="color:black;background: oklab(0.646 -0.089 -0.074);">15</span>
<span style="color:black;background: oklab(0.814 0.103 -0.138);">16</span>
<span style="color:black;background: oklab(0.732 -0.054 0.149);">17</span>
<span style="color:black;background: oklab(0.65 0.138 0.086);">18</span>
<span style="color:black;background: oklab(0.817 -0.019 0.022);">19</span>
<span style="color:black;background: oklab(0.735 0.173 -0.041);">20</span>
<span style="color:black;background: oklab(0.653 0.015 -0.104);">21</span>
<span style="color:black;background: oklab(0.821 -0.142 -0.167);">22</span>
<span style="color:black;background: oklab(0.739 0.05 0.119);">23</span>
<span style="color:black;background: oklab(0.656 -0.108 0.056);">24</span>
<span style="color:black;background: oklab(0.824 0.085 -0.007);">25</span>
<span style="color:black;background: oklab(0.742 -0.073 -0.071);">26</span>
<span style="color:black;background: oklab(0.66 0.12 -0.134);">27</span>
<span style="color:black;background: oklab(0.827 -0.038 0.153);">28</span>
<span style="color:black;background: oklab(0.745 0.154 0.09);">29</span>
<span style="color:black;background: oklab(0.663 -0.003 0.026);">30</span>
<span style="color:black;background: oklab(0.831 -0.161 -0.037);">31</span>
<span style="color:black;background: oklab(0.748 0.032 -0.1);">32</span>
<span style="color:black;background: oklab(0.666 -0.126 -0.164);">33</span>
<span style="color:black;background: oklab(0.834 0.066 0.123);">34</span>
<span style="color:black;background: oklab(0.752 -0.091 0.06);">35</span>
<span style="color:black;background: oklab(0.669 0.101 -0.003);">36</span>
<span style="color:black;background: oklab(0.837 -0.056 -0.067);">37</span>
<span style="color:black;background: oklab(0.755 0.136 -0.13);">38</span>
<span style="color:black;background: oklab(0.673 -0.022 0.157);">39</span>
<span style="color:black;background: oklab(0.84 0.171 0.093);">40</span>
<span style="color:black;background: oklab(0.758 0.013 0.03);">41</span>
<span style="color:black;background: oklab(0.676 -0.144 -0.033);">42</span>
<span style="color:black;background: oklab(0.844 0.048 -0.096);">43</span>
<span style="color:black;background: oklab(0.761 -0.11 -0.16);">44</span>
<span style="color:black;background: oklab(0.679 0.083 0.127);">45</span>
<span style="color:black;background: oklab(0.847 -0.075 0.064);">46</span>
<span style="color:black;background: oklab(0.765 0.118 0.0);">47</span>
<span style="color:black;background: oklab(0.683 -0.04 -0.063);">48</span>
<span style="color:black;background: oklab(0.85 0.152 -0.126);">49</span>
<span style="color:black;background: oklab(0.768 -0.005 0.161);">50</span>
<span style="color:black;background: oklab(0.686 -0.163 0.097);">51</span>
<span style="color:black;background: oklab(0.854 0.03 0.034);">52</span>
<span style="color:black;background: oklab(0.771 -0.128 -0.029);">53</span>
<span style="color:black;background: oklab(0.689 0.064 -0.093);">54</span>
<span style="color:black;background: oklab(0.857 -0.093 -0.156);">55</span>
<span style="color:black;background: oklab(0.775 0.099 0.131);">56</span>
<span style="color:black;background: oklab(0.692 -0.058 0.067);">57</span>
<span style="color:black;background: oklab(0.86 0.134 0.004);">58</span>
<span style="color:black;background: oklab(0.778 -0.024 -0.059);">59</span>
<span style="color:black;background: oklab(0.696 0.169 -0.122);">60</span>
<span style="color:black;background: oklab(0.863 0.011 0.164);">61</span>
<span style="color:black;background: oklab(0.781 -0.146 0.101);">62</span>
</div>
<figcaption>OKLab((n/g² % 1 &times; .25 + .63) (n/g³ % 1 &times; .35 - .175) (n/g % 1 &times; .35 - .175))</figcaption>
</figure>

<p>I wasn’t sure about the appropriateness of compressing an axis of an
LDS the way I was doing it, so I tried using a smaller modulo instead:</p>

<figure>
<div class="example">
<span style="color:black;background: oklab(0.63 -0.175 -0.175);">0</span>
<span style="color:black;background: oklab(0.801 0.017 0.112);">1</span>
<span style="color:black;background: oklab(0.722 -0.14 0.048);">2</span>
<span style="color:black;background: oklab(0.643 0.052 -0.015);">3</span>
<span style="color:black;background: oklab(0.814 -0.105 -0.078);">4</span>
<span style="color:black;background: oklab(0.735 0.087 -0.141);">5</span>
<span style="color:black;background: oklab(0.656 -0.071 0.145);">6</span>
<span style="color:black;background: oklab(0.827 0.122 0.082);">7</span>
<span style="color:black;background: oklab(0.748 -0.036 0.019);">8</span>
<span style="color:black;background: oklab(0.669 0.157 -0.045);">9</span>
<span style="color:black;background: oklab(0.84 -0.001 -0.108);">10</span>
<span style="color:black;background: oklab(0.761 -0.159 -0.171);">11</span>
<span style="color:black;background: oklab(0.683 0.034 0.116);">12</span>
<span style="color:black;background: oklab(0.854 -0.124 0.052);">13</span>
<span style="color:black;background: oklab(0.775 0.069 -0.011);">14</span>
<span style="color:black;background: oklab(0.696 -0.089 -0.074);">15</span>
<span style="color:black;background: oklab(0.867 0.103 -0.138);">16</span>
<span style="color:black;background: oklab(0.788 -0.054 0.149);">17</span>
<span style="color:black;background: oklab(0.709 0.138 0.086);">18</span>
<span style="color:black;background: oklab(0.88 -0.019 0.022);">19</span>
<span style="color:black;background: oklab(0.801 0.173 -0.041);">20</span>
<span style="color:black;background: oklab(0.722 0.015 -0.104);">21</span>
<span style="color:black;background: oklab(0.643 -0.142 -0.167);">22</span>
<span style="color:black;background: oklab(0.814 0.05 0.119);">23</span>
<span style="color:black;background: oklab(0.735 -0.108 0.056);">24</span>
<span style="color:black;background: oklab(0.656 0.085 -0.007);">25</span>
<span style="color:black;background: oklab(0.827 -0.073 -0.071);">26</span>
<span style="color:black;background: oklab(0.748 0.12 -0.134);">27</span>
<span style="color:black;background: oklab(0.669 -0.038 0.153);">28</span>
<span style="color:black;background: oklab(0.84 0.154 0.09);">29</span>
<span style="color:black;background: oklab(0.761 -0.003 0.026);">30</span>
<span style="color:black;background: oklab(0.682 -0.161 -0.037);">31</span>
<span style="color:black;background: oklab(0.853 0.032 -0.1);">32</span>
<span style="color:black;background: oklab(0.774 -0.126 -0.164);">33</span>
<span style="color:black;background: oklab(0.695 0.066 0.123);">34</span>
<span style="color:black;background: oklab(0.867 -0.091 0.06);">35</span>
<span style="color:black;background: oklab(0.788 0.101 -0.003);">36</span>
<span style="color:black;background: oklab(0.709 -0.056 -0.067);">37</span>
<span style="color:black;background: oklab(0.88 0.136 -0.13);">38</span>
<span style="color:black;background: oklab(0.801 -0.022 0.157);">39</span>
<span style="color:black;background: oklab(0.722 0.171 0.093);">40</span>
<span style="color:black;background: oklab(0.643 0.013 0.03);">41</span>
<span style="color:black;background: oklab(0.814 -0.144 -0.033);">42</span>
<span style="color:black;background: oklab(0.735 0.048 -0.096);">43</span>
<span style="color:black;background: oklab(0.656 -0.11 -0.16);">44</span>
<span style="color:black;background: oklab(0.827 0.083 0.127);">45</span>
<span style="color:black;background: oklab(0.748 -0.075 0.064);">46</span>
<span style="color:black;background: oklab(0.669 0.118 0.0);">47</span>
<span style="color:black;background: oklab(0.84 -0.04 -0.063);">48</span>
<span style="color:black;background: oklab(0.761 0.152 -0.126);">49</span>
<span style="color:black;background: oklab(0.682 -0.005 0.161);">50</span>
<span style="color:black;background: oklab(0.853 -0.163 0.097);">51</span>
<span style="color:black;background: oklab(0.774 0.03 0.034);">52</span>
<span style="color:black;background: oklab(0.695 -0.128 -0.029);">53</span>
<span style="color:black;background: oklab(0.866 0.064 -0.093);">54</span>
<span style="color:black;background: oklab(0.787 -0.093 -0.156);">55</span>
<span style="color:black;background: oklab(0.708 0.099 0.131);">56</span>
<span style="color:black;background: oklab(0.879 -0.058 0.067);">57</span>
<span style="color:black;background: oklab(0.801 0.134 0.004);">58</span>
<span style="color:black;background: oklab(0.722 -0.024 -0.059);">59</span>
<span style="color:black;background: oklab(0.643 0.169 -0.122);">60</span>
<span style="color:black;background: oklab(0.814 0.011 0.164);">61</span>
<span style="color:black;background: oklab(0.735 -0.146 0.101);">62</span>
</div>
<figcaption>OKLab((n/g² % .25 + .63) (n/g³ % 1 &times; .35 - .175) (n/g % 1 &times; .35 - .175))</figcaption>
</figure>

<p>but this version becomes distinctly worse at intervals of 22.  Which is
respectable, but it’s not as good as the previous version.</p>

<p>Those are all OKLab, so they have pointy saturation corners – though I
did reduce the range a little to compensate.  Let’s try another OKLCh:</p>

<figure>
<div class="example">
<span style="color:black;background: oklch(0.63 calc(sqrt(0.0) * 0.2) 0);">0</span>
<span style="color:black;background: oklch(0.798 calc(sqrt(0.55) * 0.2) 295);">1</span>
<span style="color:black;background: oklch(0.716 calc(sqrt(0.099) * 0.2) 230);">2</span>
<span style="color:black;background: oklch(0.633 calc(sqrt(0.649) * 0.2) 165);">3</span>
<span style="color:black;background: oklch(0.801 calc(sqrt(0.199) * 0.2) 100);">4</span>
<span style="color:black;background: oklch(0.719 calc(sqrt(0.749) * 0.2) 35);">5</span>
<span style="color:black;background: oklch(0.637 calc(sqrt(0.298) * 0.2) 329);">6</span>
<span style="color:black;background: oklch(0.804 calc(sqrt(0.848) * 0.2) 264);">7</span>
<span style="color:black;background: oklch(0.722 calc(sqrt(0.398) * 0.2) 199);">8</span>
<span style="color:black;background: oklch(0.64 calc(sqrt(0.947) * 0.2) 134);">9</span>
<span style="color:black;background: oklch(0.808 calc(sqrt(0.497) * 0.2) 69);">10</span>
<span style="color:black;background: oklch(0.725 calc(sqrt(0.047) * 0.2) 4);">11</span>
<span style="color:black;background: oklch(0.643 calc(sqrt(0.596) * 0.2) 299);">12</span>
<span style="color:black;background: oklch(0.811 calc(sqrt(0.146) * 0.2) 234);">13</span>
<span style="color:black;background: oklch(0.729 calc(sqrt(0.696) * 0.2) 169);">14</span>
<span style="color:black;background: oklch(0.646 calc(sqrt(0.246) * 0.2) 104);">15</span>
<span style="color:black;background: oklch(0.814 calc(sqrt(0.795) * 0.2) 38);">16</span>
<span style="color:black;background: oklch(0.732 calc(sqrt(0.345) * 0.2) 333);">17</span>
<span style="color:black;background: oklch(0.65 calc(sqrt(0.895) * 0.2) 268);">18</span>
<span style="color:black;background: oklch(0.817 calc(sqrt(0.444) * 0.2) 203);">19</span>
<span style="color:black;background: oklch(0.735 calc(sqrt(0.994) * 0.2) 138);">20</span>
<span style="color:black;background: oklch(0.653 calc(sqrt(0.544) * 0.2) 73);">21</span>
<span style="color:black;background: oklch(0.821 calc(sqrt(0.093) * 0.2) 8);">22</span>
<span style="color:black;background: oklch(0.739 calc(sqrt(0.643) * 0.2) 303);">23</span>
<span style="color:black;background: oklch(0.656 calc(sqrt(0.193) * 0.2) 238);">24</span>
<span style="color:black;background: oklch(0.824 calc(sqrt(0.743) * 0.2) 173);">25</span>
<span style="color:black;background: oklch(0.742 calc(sqrt(0.292) * 0.2) 107);">26</span>
<span style="color:black;background: oklch(0.66 calc(sqrt(0.842) * 0.2) 42);">27</span>
<span style="color:black;background: oklch(0.827 calc(sqrt(0.392) * 0.2) 337);">28</span>
<span style="color:black;background: oklch(0.745 calc(sqrt(0.941) * 0.2) 272);">29</span>
<span style="color:black;background: oklch(0.663 calc(sqrt(0.491) * 0.2) 207);">30</span>
<span style="color:black;background: oklch(0.831 calc(sqrt(0.041) * 0.2) 142);">31</span>
<span style="color:black;background: oklch(0.748 calc(sqrt(0.59) * 0.2) 77);">32</span>
<span style="color:black;background: oklch(0.666 calc(sqrt(0.14) * 0.2) 12);">33</span>
<span style="color:black;background: oklch(0.834 calc(sqrt(0.69) * 0.2) 307);">34</span>
<span style="color:black;background: oklch(0.752 calc(sqrt(0.24) * 0.2) 242);">35</span>
<span style="color:black;background: oklch(0.669 calc(sqrt(0.789) * 0.2) 176);">36</span>
<span style="color:black;background: oklch(0.837 calc(sqrt(0.339) * 0.2) 111);">37</span>
<span style="color:black;background: oklch(0.755 calc(sqrt(0.889) * 0.2) 46);">38</span>
<span style="color:black;background: oklch(0.673 calc(sqrt(0.438) * 0.2) 341);">39</span>
<span style="color:black;background: oklch(0.84 calc(sqrt(0.988) * 0.2) 276);">40</span>
<span style="color:black;background: oklch(0.758 calc(sqrt(0.538) * 0.2) 211);">41</span>
<span style="color:black;background: oklch(0.676 calc(sqrt(0.087) * 0.2) 146);">42</span>
<span style="color:black;background: oklch(0.844 calc(sqrt(0.637) * 0.2) 81);">43</span>
<span style="color:black;background: oklch(0.761 calc(sqrt(0.187) * 0.2) 16);">44</span>
<span style="color:black;background: oklch(0.679 calc(sqrt(0.737) * 0.2) 311);">45</span>
<span style="color:black;background: oklch(0.847 calc(sqrt(0.286) * 0.2) 245);">46</span>
<span style="color:black;background: oklch(0.765 calc(sqrt(0.836) * 0.2) 180);">47</span>
<span style="color:black;background: oklch(0.683 calc(sqrt(0.386) * 0.2) 115);">48</span>
<span style="color:black;background: oklch(0.85 calc(sqrt(0.935) * 0.2) 50);">49</span>
<span style="color:black;background: oklch(0.768 calc(sqrt(0.485) * 0.2) 345);">50</span>
<span style="color:black;background: oklch(0.686 calc(sqrt(0.035) * 0.2) 280);">51</span>
<span style="color:black;background: oklch(0.854 calc(sqrt(0.584) * 0.2) 215);">52</span>
<span style="color:black;background: oklch(0.771 calc(sqrt(0.134) * 0.2) 150);">53</span>
<span style="color:black;background: oklch(0.689 calc(sqrt(0.684) * 0.2) 85);">54</span>
<span style="color:black;background: oklch(0.857 calc(sqrt(0.234) * 0.2) 20);">55</span>
<span style="color:black;background: oklch(0.775 calc(sqrt(0.783) * 0.2) 315);">56</span>
<span style="color:black;background: oklch(0.692 calc(sqrt(0.333) * 0.2) 249);">57</span>
<span style="color:black;background: oklch(0.86 calc(sqrt(0.883) * 0.2) 184);">58</span>
<span style="color:black;background: oklch(0.778 calc(sqrt(0.432) * 0.2) 119);">59</span>
<span style="color:black;background: oklch(0.696 calc(sqrt(0.982) * 0.2) 54);">60</span>
<span style="color:black;background: oklch(0.863 calc(sqrt(0.532) * 0.2) 349);">61</span>
<span style="color:black;background: oklch(0.781 calc(sqrt(0.081) * 0.2) 284);">62</span>
</div>
<figcaption>OKLCh((n/g² % 1 &times; .25 + .63) (sqrt(n/g³ % 1) &times; .2) (n/g % 1 &times; 360&deg;))</figcaption>
</figure>

<p>And back to HSL:</p>

<figure>
<div class="example">
<span style="color:black;background: hsl(0deg, calc(sqrt(0.0) * 70%), 55%);">0</span>
<span style="color:black;background: hsl(295deg, calc(sqrt(0.55) * 70%), 72%);">1</span>
<span style="color:black;background: hsl(230deg, calc(sqrt(0.099) * 70%), 64%);">2</span>
<span style="color:black;background: hsl(165deg, calc(sqrt(0.649) * 70%), 55%);">3</span>
<span style="color:black;background: hsl(100deg, calc(sqrt(0.199) * 70%), 72%);">4</span>
<span style="color:black;background: hsl(35deg, calc(sqrt(0.749) * 70%), 64%);">5</span>
<span style="color:black;background: hsl(329deg, calc(sqrt(0.298) * 70%), 56%);">6</span>
<span style="color:black;background: hsl(264deg, calc(sqrt(0.848) * 70%), 72%);">7</span>
<span style="color:black;background: hsl(199deg, calc(sqrt(0.398) * 70%), 64%);">8</span>
<span style="color:black;background: hsl(134deg, calc(sqrt(0.947) * 70%), 56%);">9</span>
<span style="color:black;background: hsl(69deg, calc(sqrt(0.497) * 70%), 73%);">10</span>
<span style="color:black;background: hsl(4deg, calc(sqrt(0.047) * 70%), 65%);">11</span>
<span style="color:black;background: hsl(299deg, calc(sqrt(0.596) * 70%), 56%);">12</span>
<span style="color:black;background: hsl(234deg, calc(sqrt(0.146) * 70%), 73%);">13</span>
<span style="color:black;background: hsl(169deg, calc(sqrt(0.696) * 70%), 65%);">14</span>
<span style="color:black;background: hsl(104deg, calc(sqrt(0.246) * 70%), 57%);">15</span>
<span style="color:black;background: hsl(38deg, calc(sqrt(0.795) * 70%), 73%);">16</span>
<span style="color:black;background: hsl(333deg, calc(sqrt(0.345) * 70%), 65%);">17</span>
<span style="color:black;background: hsl(268deg, calc(sqrt(0.895) * 70%), 57%);">18</span>
<span style="color:black;background: hsl(203deg, calc(sqrt(0.444) * 70%), 74%);">19</span>
<span style="color:black;background: hsl(138deg, calc(sqrt(0.994) * 70%), 66%);">20</span>
<span style="color:black;background: hsl(73deg, calc(sqrt(0.544) * 70%), 57%);">21</span>
<span style="color:black;background: hsl(8deg, calc(sqrt(0.093) * 70%), 74%);">22</span>
<span style="color:black;background: hsl(303deg, calc(sqrt(0.643) * 70%), 66%);">23</span>
<span style="color:black;background: hsl(238deg, calc(sqrt(0.193) * 70%), 58%);">24</span>
<span style="color:black;background: hsl(173deg, calc(sqrt(0.743) * 70%), 74%);">25</span>
<span style="color:black;background: hsl(107deg, calc(sqrt(0.292) * 70%), 66%);">26</span>
<span style="color:black;background: hsl(42deg, calc(sqrt(0.842) * 70%), 58%);">27</span>
<span style="color:black;background: hsl(337deg, calc(sqrt(0.392) * 70%), 75%);">28</span>
<span style="color:black;background: hsl(272deg, calc(sqrt(0.941) * 70%), 67%);">29</span>
<span style="color:black;background: hsl(207deg, calc(sqrt(0.491) * 70%), 58%);">30</span>
<span style="color:black;background: hsl(142deg, calc(sqrt(0.041) * 70%), 75%);">31</span>
<span style="color:black;background: hsl(77deg, calc(sqrt(0.59) * 70%), 67%);">32</span>
<span style="color:black;background: hsl(12deg, calc(sqrt(0.14) * 70%), 59%);">33</span>
<span style="color:black;background: hsl(307deg, calc(sqrt(0.69) * 70%), 75%);">34</span>
<span style="color:black;background: hsl(242deg, calc(sqrt(0.24) * 70%), 67%);">35</span>
<span style="color:black;background: hsl(176deg, calc(sqrt(0.789) * 70%), 59%);">36</span>
<span style="color:black;background: hsl(111deg, calc(sqrt(0.339) * 70%), 76%);">37</span>
<span style="color:black;background: hsl(46deg, calc(sqrt(0.889) * 70%), 67%);">38</span>
<span style="color:black;background: hsl(341deg, calc(sqrt(0.438) * 70%), 59%);">39</span>
<span style="color:black;background: hsl(276deg, calc(sqrt(0.988) * 70%), 76%);">40</span>
<span style="color:black;background: hsl(211deg, calc(sqrt(0.538) * 70%), 68%);">41</span>
<span style="color:black;background: hsl(146deg, calc(sqrt(0.087) * 70%), 60%);">42</span>
<span style="color:black;background: hsl(81deg, calc(sqrt(0.637) * 70%), 76%);">43</span>
<span style="color:black;background: hsl(16deg, calc(sqrt(0.187) * 70%), 68%);">44</span>
<span style="color:black;background: hsl(311deg, calc(sqrt(0.737) * 70%), 60%);">45</span>
<span style="color:black;background: hsl(245deg, calc(sqrt(0.286) * 70%), 77%);">46</span>
<span style="color:black;background: hsl(180deg, calc(sqrt(0.836) * 70%), 68%);">47</span>
<span style="color:black;background: hsl(115deg, calc(sqrt(0.386) * 70%), 60%);">48</span>
<span style="color:black;background: hsl(50deg, calc(sqrt(0.935) * 70%), 77%);">49</span>
<span style="color:black;background: hsl(345deg, calc(sqrt(0.485) * 70%), 69%);">50</span>
<span style="color:black;background: hsl(280deg, calc(sqrt(0.035) * 70%), 61%);">51</span>
<span style="color:black;background: hsl(215deg, calc(sqrt(0.584) * 70%), 77%);">52</span>
<span style="color:black;background: hsl(150deg, calc(sqrt(0.134) * 70%), 69%);">53</span>
<span style="color:black;background: hsl(85deg, calc(sqrt(0.684) * 70%), 61%);">54</span>
<span style="color:black;background: hsl(20deg, calc(sqrt(0.234) * 70%), 78%);">55</span>
<span style="color:black;background: hsl(315deg, calc(sqrt(0.783) * 70%), 69%);">56</span>
<span style="color:black;background: hsl(249deg, calc(sqrt(0.333) * 70%), 61%);">57</span>
<span style="color:black;background: hsl(184deg, calc(sqrt(0.883) * 70%), 78%);">58</span>
<span style="color:black;background: hsl(119deg, calc(sqrt(0.432) * 70%), 70%);">59</span>
<span style="color:black;background: hsl(54deg, calc(sqrt(0.982) * 70%), 62%);">60</span>
<span style="color:black;background: hsl(349deg, calc(sqrt(0.532) * 70%), 78%);">61</span>
<span style="color:black;background: hsl(284deg, calc(sqrt(0.081) * 70%), 70%);">62</span>
</div>
<figcaption>HSL((n/g % 1 &times; 360&deg;), (sqrt(n/g³ % 1) &times; 70%), (n/g² % 1 &times; 25% + 55%))</figcaption>
</figure>

<p>And HSL with the axes rearranged:</p>

<figure>
<div class="example">
<span style="color:black;background: hsl(0deg, calc(sqrt(0.0) * 70%), 55%);">0</span>
<span style="color:black;background: hsl(198deg, calc(sqrt(0.819) * 70%), 72%);">1</span>
<span style="color:black;background: hsl(36deg, calc(sqrt(0.638) * 70%), 64%);">2</span>
<span style="color:black;background: hsl(234deg, calc(sqrt(0.458) * 70%), 55%);">3</span>
<span style="color:black;background: hsl(72deg, calc(sqrt(0.277) * 70%), 72%);">4</span>
<span style="color:black;background: hsl(269deg, calc(sqrt(0.096) * 70%), 64%);">5</span>
<span style="color:black;background: hsl(107deg, calc(sqrt(0.915) * 70%), 56%);">6</span>
<span style="color:black;background: hsl(305deg, calc(sqrt(0.734) * 70%), 72%);">7</span>
<span style="color:black;background: hsl(143deg, calc(sqrt(0.553) * 70%), 64%);">8</span>
<span style="color:black;background: hsl(341deg, calc(sqrt(0.373) * 70%), 56%);">9</span>
<span style="color:black;background: hsl(179deg, calc(sqrt(0.192) * 70%), 73%);">10</span>
<span style="color:black;background: hsl(17deg, calc(sqrt(0.011) * 70%), 65%);">11</span>
<span style="color:black;background: hsl(215deg, calc(sqrt(0.83) * 70%), 56%);">12</span>
<span style="color:black;background: hsl(53deg, calc(sqrt(0.649) * 70%), 73%);">13</span>
<span style="color:black;background: hsl(250deg, calc(sqrt(0.468) * 70%), 65%);">14</span>
<span style="color:black;background: hsl(88deg, calc(sqrt(0.288) * 70%), 57%);">15</span>
<span style="color:black;background: hsl(286deg, calc(sqrt(0.107) * 70%), 73%);">16</span>
<span style="color:black;background: hsl(124deg, calc(sqrt(0.926) * 70%), 65%);">17</span>
<span style="color:black;background: hsl(322deg, calc(sqrt(0.745) * 70%), 57%);">18</span>
<span style="color:black;background: hsl(160deg, calc(sqrt(0.564) * 70%), 74%);">19</span>
<span style="color:black;background: hsl(358deg, calc(sqrt(0.383) * 70%), 66%);">20</span>
<span style="color:black;background: hsl(196deg, calc(sqrt(0.203) * 70%), 57%);">21</span>
<span style="color:black;background: hsl(34deg, calc(sqrt(0.022) * 70%), 74%);">22</span>
<span style="color:black;background: hsl(232deg, calc(sqrt(0.841) * 70%), 66%);">23</span>
<span style="color:black;background: hsl(69deg, calc(sqrt(0.66) * 70%), 58%);">24</span>
<span style="color:black;background: hsl(267deg, calc(sqrt(0.479) * 70%), 74%);">25</span>
<span style="color:black;background: hsl(105deg, calc(sqrt(0.298) * 70%), 66%);">26</span>
<span style="color:black;background: hsl(303deg, calc(sqrt(0.118) * 70%), 58%);">27</span>
<span style="color:black;background: hsl(141deg, calc(sqrt(0.937) * 70%), 75%);">28</span>
<span style="color:black;background: hsl(339deg, calc(sqrt(0.756) * 70%), 67%);">29</span>
<span style="color:black;background: hsl(177deg, calc(sqrt(0.575) * 70%), 58%);">30</span>
<span style="color:black;background: hsl(15deg, calc(sqrt(0.394) * 70%), 75%);">31</span>
<span style="color:black;background: hsl(213deg, calc(sqrt(0.214) * 70%), 67%);">32</span>
<span style="color:black;background: hsl(50deg, calc(sqrt(0.033) * 70%), 59%);">33</span>
<span style="color:black;background: hsl(248deg, calc(sqrt(0.852) * 70%), 75%);">34</span>
<span style="color:black;background: hsl(86deg, calc(sqrt(0.671) * 70%), 67%);">35</span>
<span style="color:black;background: hsl(284deg, calc(sqrt(0.49) * 70%), 59%);">36</span>
<span style="color:black;background: hsl(122deg, calc(sqrt(0.309) * 70%), 76%);">37</span>
<span style="color:black;background: hsl(320deg, calc(sqrt(0.129) * 70%), 67%);">38</span>
<span style="color:black;background: hsl(158deg, calc(sqrt(0.948) * 70%), 59%);">39</span>
<span style="color:black;background: hsl(356deg, calc(sqrt(0.767) * 70%), 76%);">40</span>
<span style="color:black;background: hsl(194deg, calc(sqrt(0.586) * 70%), 68%);">41</span>
<span style="color:black;background: hsl(31deg, calc(sqrt(0.405) * 70%), 60%);">42</span>
<span style="color:black;background: hsl(229deg, calc(sqrt(0.224) * 70%), 76%);">43</span>
<span style="color:black;background: hsl(67deg, calc(sqrt(0.044) * 70%), 68%);">44</span>
<span style="color:black;background: hsl(265deg, calc(sqrt(0.863) * 70%), 60%);">45</span>
<span style="color:black;background: hsl(103deg, calc(sqrt(0.682) * 70%), 77%);">46</span>
<span style="color:black;background: hsl(301deg, calc(sqrt(0.501) * 70%), 68%);">47</span>
<span style="color:black;background: hsl(139deg, calc(sqrt(0.32) * 70%), 60%);">48</span>
<span style="color:black;background: hsl(337deg, calc(sqrt(0.139) * 70%), 77%);">49</span>
<span style="color:black;background: hsl(175deg, calc(sqrt(0.959) * 70%), 69%);">50</span>
<span style="color:black;background: hsl(13deg, calc(sqrt(0.778) * 70%), 61%);">51</span>
<span style="color:black;background: hsl(210deg, calc(sqrt(0.597) * 70%), 77%);">52</span>
<span style="color:black;background: hsl(48deg, calc(sqrt(0.416) * 70%), 69%);">53</span>
<span style="color:black;background: hsl(246deg, calc(sqrt(0.235) * 70%), 61%);">54</span>
<span style="color:black;background: hsl(84deg, calc(sqrt(0.054) * 70%), 78%);">55</span>
<span style="color:black;background: hsl(282deg, calc(sqrt(0.874) * 70%), 69%);">56</span>
<span style="color:black;background: hsl(120deg, calc(sqrt(0.693) * 70%), 61%);">57</span>
<span style="color:black;background: hsl(318deg, calc(sqrt(0.512) * 70%), 78%);">58</span>
<span style="color:black;background: hsl(156deg, calc(sqrt(0.331) * 70%), 70%);">59</span>
<span style="color:black;background: hsl(354deg, calc(sqrt(0.15) * 70%), 62%);">60</span>
<span style="color:black;background: hsl(191deg, calc(sqrt(0.97) * 70%), 78%);">61</span>
<span style="color:black;background: hsl(29deg, calc(sqrt(0.789) * 70%), 70%);">62</span>
</div>
<figcaption>HSL((n/g³ % 1 &times; 360&deg;), (sqrt(n/g % 1) &times; 70%), (n/g² % 1 &times; 25% + 55%))</figcaption>
</figure>

<p>So many choices… also, you can add some arbitrary starting value to
pick a handful of colours you like the look of, and the subsequent
colours will only come up in extreme cases.</p>

<h2 id="putting-it-into-context">Putting it into context</h2>

<p>Given some function or other to turn an index into a colour, that colour
still has to make sense for the way it’s being used.  Coloured lines
want contrast with the background while being distinguishable from each
other, but if you fill in a box you probably want that fill to have
contrast with any text that goes inside, so it should be close to the
background colour.</p>

<p>In my totally unscientific tinkering I’ve found that low-saturation
light colours (pastels) work well for lines on dark backgrounds and for
fill colours behind dark text, and that high-saturation dark colours
(“deep” colours) work well for lines on light backgrounds and fill
colours behind light text.</p>

<p>Also, fills turn out to be easier to distinguish from each other than
lines, so lines might need their saturation amplified a bit to
compensate.  Maybe.  I don’t want to go that deep right now.</p>

<p>All that said; one should have other means to distinguish things because
not everybody sees colour the same way.</p>

<h2 id="code-plz">Code plz!</h2>

<p>In CSS you can deduce a contrasting background colour with something
like: <code>HSL(from currentColor 0, 0, clamp(0, l * -100 + 50, 1))</code> This
negates the luminance and amplifies 100-fold so as to hit the limits
imposed by <code>clamp()</code> right away.  Resulting in either black or white
being chosen.</p>

<p>One can also deduce that a low saturation might be desired when
<code>currentColor</code> has a low lightness value, and high saturation is desired
when <code>currentColor</code> has a high lightness value.</p>

<p>It’s easier to do this in two steps, first making a “mask” colour, and
then using that mask as the basis for palette colours:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">*</span> <span class="p">{</span>
  <span class="py">--stroke-mask</span><span class="p">:</span> <span class="n">oklab</span><span class="p">(</span><span class="n">from</span> <span class="n">currentColor</span>
      <span class="n">clamp</span><span class="p">(</span><span class="m">.40</span><span class="p">,</span> <span class="n">l</span> <span class="err">*</span>  <span class="m">100</span> <span class="n">-</span> <span class="m">50</span><span class="p">,</span> <span class="m">.9</span><span class="p">)</span>
      <span class="n">clamp</span><span class="p">(</span><span class="m">.15</span><span class="p">,</span> <span class="n">l</span> <span class="err">*</span> <span class="m">-100</span> <span class="err">+</span> <span class="m">50</span><span class="p">,</span> <span class="m">.3</span><span class="p">)</span>
      <span class="n">clamp</span><span class="p">(</span><span class="m">.15</span><span class="p">,</span> <span class="n">l</span> <span class="err">*</span> <span class="m">-100</span> <span class="err">+</span> <span class="m">50</span><span class="p">,</span> <span class="m">.3</span><span class="p">));</span>
  <span class="py">--fill-mask</span><span class="p">:</span> <span class="n">oklab</span><span class="p">(</span><span class="n">from</span> <span class="n">currentColor</span>
      <span class="n">clamp</span><span class="p">(</span><span class="m">.40</span><span class="p">,</span> <span class="n">l</span> <span class="err">*</span> <span class="m">-100</span> <span class="err">+</span> <span class="m">50</span><span class="p">,</span> <span class="m">.9</span><span class="p">)</span>
      <span class="n">clamp</span><span class="p">(</span><span class="m">.15</span><span class="p">,</span> <span class="n">l</span> <span class="err">*</span>  <span class="m">100</span> <span class="n">-</span> <span class="m">50</span><span class="p">,</span> <span class="m">.3</span><span class="p">)</span>
      <span class="n">clamp</span><span class="p">(</span><span class="m">.15</span><span class="p">,</span> <span class="n">l</span> <span class="err">*</span>  <span class="m">100</span> <span class="n">-</span> <span class="m">50</span><span class="p">,</span> <span class="m">.3</span><span class="p">))</span>

  <span class="n">--colour-stroke</span><span class="p">:</span> <span class="n">oklab</span><span class="p">(</span><span class="n">from</span> <span class="n">var</span><span class="p">(</span><span class="n">--stroke-mask</span><span class="p">)</span>
    <span class="n">calc</span><span class="p">(</span><span class="n">calc</span><span class="p">(</span><span class="n">mod</span><span class="p">(</span><span class="m">.6710436067</span> <span class="err">*</span> <span class="n">var</span><span class="p">(</span><span class="n">--n</span><span class="p">),</span> <span class="m">1</span><span class="p">)</span> <span class="n">-</span> <span class="n">l</span><span class="p">)</span> <span class="err">*</span> <span class="m">.25</span> <span class="err">+</span> <span class="n">l</span><span class="p">)</span>
    <span class="n">calc</span><span class="p">(</span><span class="n">calc</span><span class="p">(</span><span class="n">mod</span><span class="p">(</span><span class="m">.5497004779</span> <span class="err">*</span> <span class="n">var</span><span class="p">(</span><span class="n">--n</span><span class="p">),</span> <span class="m">1</span><span class="p">)</span> <span class="n">-</span> <span class="m">0.5</span><span class="p">)</span> <span class="err">*</span> <span class="n">a</span><span class="p">)</span>
    <span class="n">calc</span><span class="p">(</span><span class="n">calc</span><span class="p">(</span><span class="n">mod</span><span class="p">(</span><span class="m">.8191725134</span> <span class="err">*</span> <span class="n">var</span><span class="p">(</span><span class="n">--n</span><span class="p">),</span> <span class="m">1</span><span class="p">)</span> <span class="n">-</span> <span class="m">0.5</span><span class="p">)</span> <span class="err">*</span> <span class="n">b</span><span class="p">));</span>
  <span class="py">--colour-fill</span><span class="p">:</span> <span class="n">oklab</span><span class="p">(</span><span class="n">from</span> <span class="n">var</span><span class="p">(</span><span class="n">--fill-mask</span><span class="p">)</span>
    <span class="n">calc</span><span class="p">(</span><span class="n">calc</span><span class="p">(</span><span class="n">mod</span><span class="p">(</span><span class="m">.6710436067</span> <span class="err">*</span> <span class="n">var</span><span class="p">(</span><span class="n">--n</span><span class="p">),</span> <span class="m">1</span><span class="p">)</span> <span class="n">-</span> <span class="n">l</span><span class="p">)</span> <span class="err">*</span> <span class="m">.25</span> <span class="err">+</span> <span class="n">l</span><span class="p">)</span>
    <span class="n">calc</span><span class="p">(</span><span class="n">calc</span><span class="p">(</span><span class="n">mod</span><span class="p">(</span><span class="m">.5497004779</span> <span class="err">*</span> <span class="n">var</span><span class="p">(</span><span class="n">--n</span><span class="p">),</span> <span class="m">1</span><span class="p">)</span> <span class="n">-</span> <span class="m">0.5</span><span class="p">)</span> <span class="err">*</span> <span class="n">a</span><span class="p">)</span>
    <span class="n">calc</span><span class="p">(</span><span class="n">calc</span><span class="p">(</span><span class="n">mod</span><span class="p">(</span><span class="m">.8191725134</span> <span class="err">*</span> <span class="n">var</span><span class="p">(</span><span class="n">--n</span><span class="p">),</span> <span class="m">1</span><span class="p">)</span> <span class="n">-</span> <span class="m">0.5</span><span class="p">)</span> <span class="err">*</span> <span class="n">b</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Where <code>--n</code> is an integer colour index.  Just set <code>--n</code> to different
numbers for each group of objects which should have the same colour, and
use <code>var(--colour-stroke)</code> and/or <code>var(--colour-fill)</code> as appropriate
within that.</p>

<p>TODO:
here’s where I’d demonstrate boxes and lines in different colours, and
on different backgrounds, but I don’t really have time right now.</p>]]></content><author><name>sh1boot</name></author><category term="web" /><category term="svg" /><category term="css" /><summary type="html"><![CDATA[One way to generate a palette of colours for distinguishing different lines and objects in diagrams is to take regular steps around the hue parameter of the HSL colour wheel. If you know how many you’ll need then you can subdivide the space evenly, or if you do not then you can use 1/φ as the interval instead. But this has limitations…]]></summary></entry><entry><title type="html">Designing a Lego card shuffler</title><link href="https://www.xn--tkuka-m3a3v.dev/lego-card-shuffler/" rel="alternate" type="text/html" title="Designing a Lego card shuffler" /><published>2025-11-20T00:00:00+00:00</published><updated>2026-07-03T18:53:07+00:00</updated><id>https://www.xn--tkuka-m3a3v.dev/lego-card-shuffler</id><content type="html" xml:base="https://www.xn--tkuka-m3a3v.dev/lego-card-shuffler/"><![CDATA[<p>A problem with mechanical card shufflers is that they do things like
riffles with mechanical precision, and mechanical precision tends to produce
predictable outcomes (at least in theory).  Thinking about this gave me the idea that I could
do my own but with deliberate and controlled use of robust random numbers in
order to produce a true shuffle.</p>

<p>I figured the thing to do would be to enumerate the cards randomly and
then radix-sort them into place.  This seemed like a comparatively
(<em>comparatively</em>) easy mechanism to build.  As a side effect,
enumerating and reordering means that you could also add a camera and
then identify and sort the cards by their actual value.  It’s also
much easier to verify that sorting has been done correctly than it is to
verify that shuffling has been done correctly.</p>

<p>In fact, the user could choose whether to sort or to shuffle simply by
placing the cards face-up or face-down.  Or if it’s a real mess then the
deck can be separated into face-up and face-down stacks in one pass.</p>

<h2 id="equivalence-of-shuffling-sorting-and-riffling">Equivalence of shuffling, sorting, and riffling</h2>

<h3 id="whats-a-riffle">What’s a riffle?</h3>

<p>A <a href="https://en.wikipedia.org/wiki/Shuffling#Riffle">riffle</a> can be modelled as dividing the cards into two stacks and
randomly picking either the left or right stack to deliver the next card
to the result, over and over until there are no more cards.  Each
choice is based on probabilities proportional to the number of cards in
each stack, and this model implies the dealer tries to mix the two
stacks evenly rather than letting one side expire early and then simply
dropping the rest of the cards from the other stack on top.</p>

<p>Unfortunately if you have too much precision then the outcome is that
you interleave the cards in a regular left-right-left-right pattern,
which is completely predictable.  Some people can do this deliberately!</p>

<p>If it is ideally unpredictable then you need to do at least six of these
to get a fair shuffle in a deck of 52 cards.  Probably more, but
certainly not less.</p>

<h3 id="whats-a-radix-sort">What’s a radix sort?</h3>

<p><a href="https://en.wikipedia.org/wiki/Radix_sort">Radix sorting</a> is a multi-pass binning operation, where the
cards are sent to one of n (n will be two in this build) different bins
depending on whether they should be at the front or the back of the
sorted list.  Doing this in multiple stages means making the decision
based on different conditions on each pass.  You might separate even and
odd cards, then place one pile on the other for the next round, then low
and high numbered cards mod-4, then mod-8, etc., with the final pass
separating the red and black cards.</p>

<p>Technically separating the cards into two bins is the <em>opposite</em> of
riffling; but the overall effect in either case is a permutation which
can be identified by the binary decisions made along the way.</p>

<p>To use a sorting machine as a shuffler you can randomly assign unique
numbers to each card, and then sort the cards by their associated
numbers.</p>

<p>What you <em>should not</em> do is to take a sorting algorithm and then make random
decisions at each comparison.  That rarely works.
Radix sorting might perform comparatively well in this arrangement, but
it’s still wrong.  In fact it’s radix sort’s good (but not perfect)
performance that makes riffle shuffling converge on a strong shuffle
after only one or two extra rounds beyond the theoretical minimum.</p>

<h3 id="how-are-they-similar">How are they similar?</h3>

<p>If every card remembers whether it came from the left pile or the right
pile, for every riffle step in a shuffle, then it would come out with
six or seven boolean values, which you can combine as bits into a
number, which represents its index in the shuffled pile.  In essence
the process gives each card a random 6-bit number and then sorts them by
those numbers.</p>

<p>A radix sort replays that same string of decisions but in reverse order.  Reading those same index numbers from the other end.</p>

<p>But look out.  Just assigning numbers this way allows the possibility that
two cards could have the same number, and then their final order
won’t be changed from their initial order.  More riffles add more bits
to the numbers, decreasing the chances of two numbers having the same
number and being “stuck together” for the whole shuffle, but it’s a
coarse approximation of picking predictably unique numbers.</p>

<p>An ideal shuffle chooses <em>unique indices</em> for each card, and then sorts
by that value.  Moreover; an ideal shuffle chooses one of the 52! possible
permutations and then puts the cards in that order, and that order can
be described by numbering the cards according to where they land.  A
pair of cards could still come out in the same order as they went in,
but only with a suitably low probability.</p>

<p>That’s a thing we can do trivially in a microcontroller, but
not-at-all-trivially in a Victorian-era mechanical contraption.</p>

<h2 id="how-to-build-a-thing-for-that">How to build a thing for that?</h2>

<p>To build a radix-sorting machine I need to be able to take cards one at
a time from the source deck and to deliver them to one of two (or n)
other bins according to logic of some sort, and once all the cards are
redistributed, to combine those two piles and bring them back to the
starting point for the next round.</p>

<h3 id="binning">Binning</h3>

<p>Starting with the easiest bit; capturing the cards in multiple bins and
bringing them back together into a single pile for the next round.</p>

<p>For this I decided on a vertical column with three shelves.  The source
pile at the top, and two output piles below that.  A shuttle (also
acting as the bottom shelf) can then lift the cards to the top, but as
it lifts the cards, the shelves above have to get out of the way while
depositing their cards on top of those already on the shuttle.</p>

<p>To achieve this, I made the shelves a pair of forks, on diagonal
sliders.  Upward pressure from below would push the forks backwards out
of the way, while the wall they retreated into would keep the cards
lined up with the shuttle as it rose.  When the shuttle passed the forks
they would drop back into place behind it.</p>

<p>Then the shuttle needs to deposit the cards on the top shelf and go back
to the bottom.  To achieve this it’s made of overlapping wings which
lift up and slip between the forks on the way back down, leaving the
cards on top of those forks.</p>

<p>And that actually worked!  Hurrah!</p>

<p>Here I would offer a picture, but the kids stripped my build for parts
and now we have a Lego Porsche 911 instead of a card shuffler.  So I’m
going to offer a quick and dirty 3D mockup instead:</p>

<style>
    .click-embed {
      background-color: transparent;
      border-style: none;
      width: 100%;
    }
  </style>

<iframe class="click-embed" id="tinkercad-bdk1j8czacm" name="tinkercad-bdk1j8czacm" style="aspect-ratio:16/9;" scrolling="no" allowfullscreen="" sandbox="allow-scripts allow-same-origin allow-popups" srcdoc="&lt;style&gt; html,body {
    overflow: clip;
    margin: 0;
    background-color: transparent;
    justify-content: center center;
    text-align: center;
    height: 100%;
  }
  .maximised-image {
    object-fit: contain;
    width: 100%;
    height: 100%;
  }
  .button {
    position: absolute;
    top: 5%;
    left: 5%;
    padding: 6px 6px;
    border: 1px outset buttonborder;
    color: buttontext;
    background-color: buttonface;
    font-family: sans-serif;
    text-decoration: none;
  } &lt;/style&gt;
  &lt;img class=&quot;maximised-image&quot; src=&quot;/images/radix-shelf.png&quot; /&gt;
  &lt;a href=&quot;https://www.tinkercad.com/embed/bdK1J8czaCm?editbtn=1&quot;&gt;&lt;div class=&quot;button&quot;&gt;Click to view in 3D&lt;/div&gt;&lt;/a&gt;">
  <a href="https://www.tinkercad.com/things/bdK1J8czaCm">
    <img src="/images/radix-shelf.png" alt="Click to view in 3D" />
  </a>
</iframe>

<h3 id="dealing">Dealing</h3>

<p>Next we have to deal the cards one at a time from the top pile, and
decide where they should be delivered.  Dealing cards with Lego is a
problem that seems really hard.  How do I ensure only one card is drawn
at a time?  How does a printer do that?</p>

<p>I ran out of time for the project before I could build any prototype,
but I had thoughts and I hope to revisit the problem imminently.</p>

<p>My thinking, such as it is, involves a roller (motorised Lego wheel) on
top of the deck pushes at least the top card out, while a brush sits
beneath where the top card protrudes and tries to sweep back any other
cards which got dragged along with the top card.  Not sure if it’s
necessary, but I feel like I at least have a plan if it turns out it is
necessary.</p>

<p>Once the top card is protruding far enough that I think the brush has
isolated it, slightly faster rollers can pick it up and get it moving on
its way.</p>

<p>This mechanism has to have a bit of vertical freedom so that it can
adapt to the shrinking pile, obviously, and I guess the smart thing
would be to sense when the pile is empty (ie., when there’s no card
supporting the roller, and it falls beyond a threshold).  It also has to
get out of the way when the shuttle is trying to refill the pile.  I
figure that the refill action should lift both mechanisms together, and
then replace both mechanisms together.</p>

<p>I intend to use the same motor to drive the rollers and also to raise
and lower the shuttle and rollers.  Why?  Because I only bought two
motors and two motor controllers.  This means turning the motor in one
direction will lift things up and disengage the rollers, and then
turning that motor in the other direction will lower everything and at
the point it’s seated further motion on the motor toggles over to
driving the rollers.</p>

<p>Slightly fiddly, but probably easier overall than adding more motors.</p>

<p>TODO: draw a diagram</p>

<h3 id="routing">Routing</h3>

<p>Every card drawn has to be directed to one of the two bins, and so some
kind of switch is in order.  I figure that’s basically just a slide
which can be raised or lowered to point at the appropriate bin.  The
main complication comes from wanting to make sure that timing errors
don’t cause a card to get jammed in a destructive way, so there has to
be clearance for the card to find a safe escape path if things move at
the wrong time, and it has to jump over this gap in normal operation.</p>

<p>Also, I need a sensor to regulate the timing of the switching.  One
which will tell me when the next card is passing by.  Or, in the fancy
version a sensor to read the face value of the cards, and also that the
next card is passing by.</p>

<p>There’s no Lego sensor for the second version, so I’m sticking with the
first (though I do have a thing in a box somewhere…?).</p>

<p>TODO: more diagrams?</p>

<h3 id="actuation">Actuation</h3>

<h4 id="controller">controller</h4>

<p>For the actual control logic I went with a <a href="https://microbit.org/">micro:bit</a>, because it’s
cheap and because my employer gave me one to celebrate an anniversary.
Also my boss gave me another one because he thought he’d never have time
to use his.</p>

<p>Moreover, at the time I felt that the <a href="https://www.bricklink.com/v2/catalog/catalogitem.page?P=95646c01">EV3 brick</a> was unreasonably
expensive and I wanted to do my part in making that cheaper so that Lego
education kits could be stretched a bit further.  But that whole thing
is for another blog post.</p>

<p>Here’s all the bits I needed on some breadboard:</p>

<p><img src="/images/lego-interface-board.jpg" alt="interface logic" /></p>

<p>Parts: <a href="https://shop.pimoroni.com/products/edge-connector-breakout-board-for-bbc-micro-bit">edge connector</a>, <a href="https://www.pololu.com/product/2130">driver</a>, <a href="http://www.mindsensors.com/ev3-and-nxt/58-breadboard-connector-kit-for-nxt-or-ev3">connector</a>.</p>

<p>Since then Lego has changed its connector standard (again).  I have the
older motors right now, but I think I should re-do the build for the
modern connectors at some point.  Maybe Lego will stop changing the
connector standard now?</p>

<h4 id="motors">motors</h4>

<p>Lego Mindstorms “servo” motors are a combination of 9V DC motor and
<a href="https://en.wikipedia.org/wiki/Incremental_encoder#Quadrature_outputs">quadrature encoder</a>.  That and a PWM output from the controller are
enough for a <a href="https://en.wikipedia.org/wiki/Proportional-integral-derivative_controller">PID control loop</a> to manage speed and position, but
it won’t know its absolute position at boot.</p>

<p>One solution to this is to have a bump switch to confirm the zero
position at start-up.  Alternatively, Lego has a <a href="https://www.bricklink.com/v2/catalog/catalogitem.page?P=60c01">clutch gear</a> and at
boot time you can just over-extend the position and let that slip to
reset the zero position.  This introduces the risk of drift and may
require periodic re-homing, but (depending on levers and stuff) it may
also lessen the damage if a card gets jammed in the wrong place.</p>

<p>I have to say, playing with a PID loop on a Lego motor is fun and
everybody should try it at least once.  It’s interesting to feel how the
poll rate and parameters affect the feel of the motor as it resists you
pushing on it.  It can feel disconcertingly solid in contrast with the
elastic feeling of a motor without control – depending on parameters.</p>

<h4 id="logic">logic</h4>

<p>With all the machinery in place I have to actually write some code.
Well, I wrote some code early on.  Starting with a driver for the
quadrature decoder which is provided by the <a href="https://www.nordicsemi.com/Products/nRF51822">nRF51822</a> on the
micro:bit, and the PID controller… but none of that means much without
a machine to attach it to.  Which I don’t have.  I just have a racing
car, a bunch of rubber-band launchers, and some stuff the dog’s been
chewing on.</p>

<p>But what I <em>would</em> do is:</p>

<p>With the cards set on the top shelf, turn the first motor forwards
continuously, feeding cards one at a time towards the ramp.  In the
first pass it doesn’t matter where the ramp points, because we’re just
counting the cards (or checking the current face value order if we’re
fancy).  Keep rolling until a sensor tells us we’re out of cards.</p>

<p>Here we stop and do some thinking to decide what order we want to put
the cards into.  If we saw n cards we enumerate them in random
order from 1..n, and that’s going to be our target order.  Knowing how
many cards we have we also know we’ll have to do <code>ceil(log2(n))</code> passes.</p>

<p>Next reverse the motor, which stops the rollers and lifts the shuttle
and rollers.  Keep doing that until [TBD], then turn the motor forwards
again to begin lowering everything.  At this point rollers are still
disengaged and the cards are on the shuttle which is above the top
shelf.</p>

<p>On the way down the shuttle deposits the cards on the top shelf and
passes between the forks.  Once the shuttle hits the bottom, the [TBD]
mechanism disengages from the shuttle and begins turning the rollers
again.  As each card passes by, move the ramp up or down to direct the
card appropriately for its planned position in the final sort.</p>

<p>This is just an LSD radix sort.  Odd numbered cards go up, even numbered
cards go down, or whatever.  Keep going until we run out of cards.  Make
sure the count is consistent with last time, or we’ve done a whoopsie.</p>

<p>Shuttle up, shuttle down.  Repeat.  This time the shuttle position is
determined by the next bit in the cards’ indices.</p>

<p>Over and over until we’ve done enough passes to fully shuffle the cards.
All done.  Yay!</p>

<p>Now I just have to rebuild what I used to have, build and test the bits
I didn’t already have, write the code, ???, and profit!</p>

<p>One day.  When I’m retired, or whatever.</p>]]></content><author><name>sh1boot</name></author><category term="electronics" /><category term="not-just-software" /><summary type="html"><![CDATA[A problem with mechanical card shufflers is that they do things like riffles with mechanical precision, and mechanical precision tends to produce predictable outcomes (at least in theory). Thinking about this gave me the idea that I could do my own but with deliberate and controlled use of robust random numbers in order to produce a true shuffle.]]></summary></entry><entry><title type="html">Getting even light from long LED strips</title><link href="https://www.xn--tkuka-m3a3v.dev/led-strip-wiring-tip/" rel="alternate" type="text/html" title="Getting even light from long LED strips" /><published>2025-09-27T00:00:00+00:00</published><updated>2026-07-06T19:47:44+00:00</updated><id>https://www.xn--tkuka-m3a3v.dev/led-strip-wiring-tip</id><content type="html" xml:base="https://www.xn--tkuka-m3a3v.dev/led-strip-wiring-tip/"><![CDATA[<p>Something you may notice for very long runs of LED strip is that they
can be bright at one end and dim at the other.  That’s because the
strips are two long power rails with a bit of internal resistance and
current through the LEDs at the far end have more of that resistance to
travel through.</p>

<p>Here’s how LED strips are typically wired:</p>
<svg width="100%" viewbox="0 0 800 320">
<style>
@-webkit-keyframes currentAnimation {
  from { stroke-dashoffset: 12; }
  to { stroke-dashoffset: 0; }
}
.component {
    fill: oklab(from currentColor clamp(.05, 1 - l, .95) a b);
    fill-opacity:100%;
}
.hookup-plus {
    stroke: color-mix(in oklab, currentColor, red);
    stroke-width: 3px;
    stroke-linecap: round;
}
.hookup-minus {
    stroke: color-mix(in oklab, currentColor, blue);
    stroke-width: 3px;
    stroke-linecap: round;
}
.current {
    visibility:hidden;
    opacity: 0%;
    stroke-dasharray: 6;
}
.ledcurrent:hover .current {
    visibility:visible;
    opacity: 100%;
    -webkit-animation-name: currentAnimation;
    -webkit-animation-iteration-count: infinite;
    -webkit-animation-duration: 1.5s;
    -webkit-animation-timing-function: linear;
}
</style>
<defs>
        <g id="pos"><path d="m-5,0h10m-5,-5v10" /></g>
        <g id="neg"><path d="m-5,0h10" /></g>
        <g id="batt"><path d="M0,0v20 M-30,20h60 M-20,30h40 M-30,40h60 M-20,50h40 M0,50v20 M15,5v10 M10,10h10" /></g>
        <g id="power"><circle cx="0" cy="35" r="25" /><path d="M0,0v10 M0,60v10" /><use href="#pos" x="0" y="22" /><use href="#neg" x="0" y="48" /></g>
        <g id="powerh"><circle cx="35" cy="0" r="25" /><path d="M0,0h10 M60,0h10" /><use href="#pos" x="22" y="0" /><use href="#neg" x="48" y="0" /></g>
        <g id="led"><path d="M0,0v14 M0,56l-25,-42h50z M-25,56h50 M36,29l2,6l-6,2m6,-2l-12,-7  M31,39l2,6l-6,2m6,-2l-12,-7   M0,56v14" class="component" /></g>
        <g id="lamp"><circle cx="0" cy="35" r="25" /><path d="M-17.6,17.4L17.6,52.6 M17.6,17.4L-17.6,52.6 M0,0v10 M0,60v10 " /></g>
        <g id="resistor"><rect x="-10" y="10" width="20" height="50" /><path d="M0,0v10 M0,60v10 " /></g>
        <g id="ledstack"><use x="0" y="0" href="#led" /><use x="0" y="70" href="#led" /><use x="0" y="140" href="#led" /><use x="0" y="210" href="#resistor" /></g>
</defs>
        <use href="#power" x="40" y="120" />
        <path d="M40,120 C40,50  0, 20 140, 20" class="hookup-plus" />
        <path d="M40,190 C40,250 0,300 140,300" class="hookup-minus" />
        <line x1="140" y1="20" x2="750" y2="20" />
        <use href="#pos" x="145" y="10" />
        <use href="#pos" x="745" y="10" />
        <line x1="140" y1="300" x2="750" y2="300" />
        <use href="#neg" x="145" y="290" />
        <use href="#neg" x="745" y="290" />
        <use href="#ledstack" x="200" y="20" />
        <use href="#ledstack" x="300" y="20" />
        <use href="#ledstack" x="400" y="20" />
        <use href="#ledstack" x="500" y="20" />
        <use href="#ledstack" x="600" y="20" />
        <use href="#ledstack" x="700" y="20" />
</svg>

<p>The vertical stack of LEDs is distributed along the strip somewhat,
which is why you’re restricted to cutting the strip at regular intervals
of every three or six LEDs.</p>

<p>Let’s simplify that by treating the LED circuits as lamps:</p>

<svg width="100%" viewbox="0 -10 800 130">
        <use href="#power" x="40" y="20" />
        <path d="M40,20 C40,-20  90,20 140,20" class="hookup-plus" />
        <path d="M40,90 C40,130  90,90 140,90" class="hookup-minus" />
        <line x1="140" y1="20" x2="750" y2="20" />
        <use href="#pos" x="145" y="10" />
        <use href="#pos" x="745" y="10" />
        <line x1="140" y1="90" x2="750" y2="90" />
        <use href="#neg" x="145" y="80" />
        <use href="#neg" x="745" y="80" />
        <g class="ledcurrent">
        <use href="#lamp" x="200" y="20" />
        <path d="M30,20 C30,-35 90,10 140,10
            H170
            c25,0 30,20 30,45 0,25 -5,45 -30,45
            H140 C90,100, 30,145 30,90" class="current" />
        </g>
        <g class="ledcurrent">
        <use href="#lamp" x="300" y="20" />
        <path d="M30,20 C30,-35 90,10 140,10
            H270
            c25,0 30,20 30,45 0,25 -5,45 -30,45
            H140 C90,100, 30,145 30,90" class="current" />
        </g>
        <g class="ledcurrent">
        <use href="#lamp" x="400" y="20" />
        <path d="M30,20 C30,-35 90,10 140,10
            H370
            c25,0 30,20 30,45 0,25 -5,45 -30,45
            H140 C90,100, 30,145 30,90" class="current" />
        </g>
        <g class="ledcurrent">
        <use href="#lamp" x="500" y="20" />
        <path d="M30,20 C30,-35 90,10 140,10
            H470
            c25,0 30,20 30,45 0,25 -5,45 -30,45
            H140 C90,100, 30,145 30,90" class="current" />
        </g>
        <g class="ledcurrent">
        <use href="#lamp" x="600" y="20" />
        <path d="M30,20 C30,-35 90,10 140,10
            H570
            c25,0 30,20 30,45 0,25 -5,45 -30,45
            H140 C90,100, 30,145 30,90" class="current" />
        </g>
        <g class="ledcurrent">
        <use href="#lamp" x="700" y="20" />
        <path d="M30,20 C30,-35 90,10 140,10
            H670
            c25,0 30,20 30,45 0,25 -5,45 -30,45
            H140 C90,100, 30,145 30,90" class="current" />
        </g>
</svg>

<p>You can hover over a lamp to see where the current flows.  The further
you go from the power supply the greater the cumulative resistance of
the power rails.</p>

<p>The expedient but costly solution is to reinforce the power rails in the
strip by soldering some heavy hookup wire onto the strip at some of the
cut points where you don’t actually cut it (once every metre should be
plenty; more frequently would be unnecessarily tedious).</p>

<p>But if you happen to be running the strip in a loop, such that the ends
end up somewhat close to each other, or if you’re willing to run one
length of hookup wire alongside the strip, then there’s a simpler fix
for that difference in brightness.</p>

<p>Connect one side of the power supply to the near end of the strip, and
connect the other side of the power supply to the far end of the strip.
Be careful to still connect minus to minus and plus to plus in the usual
way, though.  Like so:</p>

<svg width="100%" viewbox="0 0 800 190">
        <use href="#powerh" x="365" y="150" />
        <path d="M375,150 C-105,150  0,20 100,20" class="hookup-plus" />
        <path d="M435,150 C935,150 800,90 700,90" class="hookup-minus" />
        <line x1="100" y1="20" x2="700" y2="20" />
        <use href="#pos" x="105" y="10" />
        <use href="#pos" x="695" y="10" />
        <line x1="100" y1="90" x2="700" y2="90" />
        <use href="#neg" x="105" y="80" />
        <use href="#neg" x="695" y="80" />
        <g class="ledcurrent">
        <use href="#lamp" x="150" y="20" />
        <path d="M365,160 C-135,160  0,10 100,10
                 H120
                 c25,0 30,20 30,45 0,25 5,45 30,45 H700
                 C 790,100 905,140, 435,140" class="current" />
        </g>
        <g class="ledcurrent">
        <use href="#lamp" x="250" y="20" />
        <path d="M365,160 C-135,160  0,10 100,10
                 H220
                 c25,0 30,20 30,45 0,25 5,45 30,45 H700
                 C 790,100 905,140, 435,140" class="current" />
        </g>
        <g class="ledcurrent">
        <use href="#lamp" x="350" y="20" />
        <path d="M365,160 C-135,160  0,10 100,10
                 H320
                 c25,0 30,20 30,45 0,25 5,45 30,45 H700
                 C 790,100 905,140, 435,140" class="current" />
        </g>
        <g class="ledcurrent">
        <use href="#lamp" x="450" y="20" />
        <path d="M365,160 C-135,160  0,10 100,10
                 H420
                 c25,0 30,20 30,45 0,25 5,45 30,45 H700
                 C 790,100 905,140, 435,140" class="current" />
        </g>
        <g class="ledcurrent">
        <use href="#lamp" x="550" y="20" />
        <path d="M365,160 C-135,160  0,10 100,10
                 H520
                 c25,0 30,20 30,45 0,25 5,45 30,45 H700
                 C 790,100 905,140, 435,140" class="current" />
        </g>
        <g class="ledcurrent">
        <use href="#lamp" x="650" y="20" />
        <path d="M365,160 C-135,160  0,10 100,10
                 H620
                 c25,0 30,20 30,45 0,25 5,45 30,45 H700
                 C 790,100 905,140, 435,140" class="current" />
        </g>
</svg>

<p>This way the length of the circuit through each LED is (approximately)
the same, and so the resistance is the same and they all come out the same
brightness all the way along the strip.</p>

<p>You’ll see this in some prefabricated lighting strings which are <em>not</em>
designed to be cut.  They’ll have a third wire which is not be connected
directly to the LEDs, but at the end it’ll be connected to one of the
other wires, and that will complete the circuit from the far end back
to the power supply to balance things out.  If you cut those then they
won’t work anymore, because you would need to reconnect two of the wires
at the cut point.</p>

<p>It doesn’t matter if the power supply hookup lines are different
lengths.  Having the total length be unnecessarily long will be less
energy efficient, but they affect all the LEDs equally regardless of
whether or not both sides are the same length.</p>

<p>It might be tempting to link both ends of the strip together in tee
intersections with the power supply.  That should work, and you’ll get
more light out of the system overall, but you may still see a bit of
dimming in the middle of the loop.</p>]]></content><author><name>sh1boot</name></author><category term="electronics" /><category term="not-just-software" /><summary type="html"><![CDATA[Something you may notice for very long runs of LED strip is that they can be bright at one end and dim at the other. That’s because the strips are two long power rails with a bit of internal resistance and current through the LEDs at the far end have more of that resistance to travel through.]]></summary></entry><entry><title type="html">Initialisation at declaration considered harmful</title><link href="https://www.xn--tkuka-m3a3v.dev/initialised-variables-considered-harmful/" rel="alternate" type="text/html" title="Initialisation at declaration considered harmful" /><published>2025-08-29T00:00:00+00:00</published><updated>2026-07-03T18:53:07+00:00</updated><id>https://www.xn--tkuka-m3a3v.dev/initialised-variables-considered-harmful</id><content type="html" xml:base="https://www.xn--tkuka-m3a3v.dev/initialised-variables-considered-harmful/"><![CDATA[<p>Suppose you have a variable <code>x</code>.</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">x</span><span class="p">;</span>
</code></pre></div></div>

<p>Hello <code>x</code>.</p>

<p>Now suppose you decide that under some circumstances <code>x</code> should have a
particular value.</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="n">some_circumstances</span><span class="p">)</span> <span class="n">x</span> <span class="o">=</span> <span class="n">particular_value</span><span class="p">;</span>
</code></pre></div></div>

<p>And later on <code>x</code>’s good buddy <code>y</code> wants to have its own value based
on <code>x</code>’s value.</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
</code></pre></div></div>

<p>Hey <code>y</code>, how’s it going?  What’s that?  You say you don’t feel so good?</p>

<p>Oh dear.  It looks like somebody’s coming down with a touch of the
Undefined Behaviour.  Perhaps <code>some_circumstances</code> wasn’t the only case
we should have addressed, here.</p>

<p>Conventional wisdom says you should avoid this situation by always
initialising your variables when you define them.  Ideally you do this
by declaring them only when you know what they should be.  But sometimes
you have only partial information when you want to put a value in there,
and so in the alternate case you can only make something up:</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">some_circumstances</span><span class="p">)</span> <span class="n">x</span> <span class="o">=</span> <span class="n">particular_value</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
</code></pre></div></div>

<p>But what if your intention was not to set <code>y</code> to <code>f(0)</code>?  What if the real
bug was in failing to consider another case and come up with a suitable
result in that case as well?  What if <code>x</code> was actually a <code>uid_t</code>?
Should a uid be initialised to zero as a “safe default” in the case of
logic bugs?</p>

<p>Well, you could initialise <code>x</code> with a value so absurd that the mistake
was bound to be highly visible in some way or other.  Good choices are
signalling NaNs, <code>nullptr</code>, etc., or something you’ll catch in an
<code>assert()</code> eventually (if you remember, and if you have the test
coverage).  That’s problematic if your type can only represent legal and
appropriate values (very often the case for DSP work).</p>

<p>You could use a bigger type as a temporary, or use <code>std::optional&lt;&gt;</code>
which includes an explicit flag saying whether or not the variable has
been initialised.  But these require that extra checks be <em>manually</em>
implemented before the variable is used.  Otherwise they’ll likely
produce silent failures of their own.  And checks might not be put in
all the necessary places, because they’re a <em>manual</em> effort.</p>

<p>The thing is, though, leaving the variable uninitialised <em>is</em> setting it
to an illegal value which the compiler will try to prove cannot escape:</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">x</span><span class="p">;</span>  <span class="c1">// will definitely get overwritten</span>
<span class="k">if</span> <span class="p">(</span><span class="n">some_circumstances</span><span class="p">)</span> <span class="n">x</span> <span class="o">=</span> <span class="n">particular_value</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">some_other_circumstances</span><span class="p">)</span> <span class="n">x</span> <span class="o">=</span> <span class="n">different_value</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">unusual_circumstances</span><span class="p">)</span> <span class="n">x</span> <span class="o">=</span> <span class="n">spooky_value</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
</code></pre></div></div>

<p>Ideally, if <code>(some_circumstances || some_other_circumstances ||
unusual_circumstances)</code> isn’t provably true then the compiler will gripe
about this and you’ll have to revisit the code and make it right.  This
is most valuable if the code was clean before you made changes and
afterwards this warning suddenly turns up.</p>

<p>Sadly, Clang and GCC really only care if they’re going to produce an
undefined value, and with optimisations enabled most of these cases are
obviated by replacing predicates with constants.  That might cover
security vulnerabilities but it’s no help with logic bugs.  To get the
job done properly you need to run Clang with <code>--analyze</code>, or use your
own favourite static analyser.</p>

<p>Clearly the compiler’s still not going to get all of them, and the
static analyser might miss something too, so being the diligent you that
you are you’ll hopefully catch the remaining cases when you run your
unit tests with <code>-fsanitize=memory</code>.</p>

<p>But if you do initialise the variable before you know what should be in
that variable, then those checks will never work.  Consequently you can
introduce bugs which cause the initialiser you chose (before you knew
what the value should be) to become the final value, and neither the
compiler nor the sanitiser will be able to tell you that you’ve done so.
You’d have been better off knowing you just broke something, but instead
you’ll just get that “safe” value you initialised with.</p>

<p>Modern tooling has made an uninitialised variable the implicit
signalling illegal state.  But it’s also long-established bad style, so
people have put time and effort into hiding bugs which would have been
surfaced by the tools had they <em>not</em> tried to improve their code.</p>

<p>It’s unfortunate that there’s no consistent way to <em>explicitly</em> declare
a variable as having an illegal state which should raise an error if
it’s used.  All we have is well-known ad-hoc solutions like <code>nullptr</code>,
<code>NAN</code>, <code>std::numeric_limits&lt;T&gt;::signaling_NaN</code>, maybe <code>T::end()</code>, etc..</p>

<p>I would prefer explicit syntax for “I don’t know yet” initialisers which
still allow the tools to do their job but can drop in default fill
values when the tools reach their limits.  Like C++26’s <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2795r5.html">erroneous
behaviour</a>, but made explicit so as to stave off those generic
“uninitialised variable” warnings.  Perhaps name it <code>undecided&lt;T&gt;{}</code> or
<code>uncommitted&lt;T&gt;{}</code> or <code>provisional&lt;T&gt;{}</code>, with an optional value
argument if you don’t want to leave that choice to the implementation,
reflecting that the developer hasn’t chosen a value and any attempt to
read it before it changes would be a mistake, but without implying that
it could be uninitialised.</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="n">uncommitted</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">{};</span>
<span class="k">if</span> <span class="p">(</span><span class="n">some_circumstances</span><span class="p">)</span> <span class="n">x</span> <span class="o">=</span> <span class="n">particular_value</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">some_other_circumstances</span><span class="p">)</span> <span class="n">x</span> <span class="o">=</span> <span class="n">different_value</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">unusual_circumstances</span><span class="p">)</span> <span class="n">x</span> <span class="o">=</span> <span class="n">spooky_value</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>  <span class="c1">// invoke C++26 erroneous behaviour as needed</span>
</code></pre></div></div>

<p>Ideally the compiler or static analyser would pick up any oversights in
that logic.  If not, <code>-fsanitize=memory</code> might pick it up provided you
have a test case that covers it.  If not, then a default value is
inserted as chosen by <code>uncommitted&lt;int&gt;{}</code>, or the value you specify if
you choose to do so (even though you’ve clearly never tested it).  One
might expect <code>uncommitted&lt;float&gt;{}</code> to choose a signalling NaN and any
pointer type to choose <code>nullptr</code>.</p>

<p>C++26 might achieve that if you leave the variable uninitialised at
definition, but that just looks like a mistake, and it’s a landmine if
you don’t have your compiler configured appropriately.</p>

<p>Additionally, if you could be explicit, you can be <em>more</em> explicit about
other things, like:</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">f</span><span class="p">(</span><span class="n">Result</span><span class="o">&amp;</span> <span class="n">result</span><span class="p">,</span> <span class="kt">int</span> <span class="n">arg</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">result</span> <span class="o">=</span> <span class="n">uncommitted</span><span class="o">&lt;</span><span class="n">Result</span><span class="o">&gt;</span><span class="p">{};</span>
    <span class="c1">// ...</span>
    <span class="n">result</span> <span class="o">=</span> <span class="n">work_in_progress</span><span class="p">;</span>
    <span class="c1">// ...</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">accident_happened</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">result</span> <span class="o">=</span> <span class="n">uncommitted</span><span class="o">&lt;</span><span class="n">Result</span><span class="o">&gt;</span><span class="p">{};</span>
        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="c1">// ...</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And let the tools ensure that result is left untouched when it’s in an
undefined state.</p>]]></content><author><name>sh1boot</name></author><summary type="html"><![CDATA[The trouble with always initialising variables at definition, and how it weakens tools which should be there to help you diagnose logic errors.]]></summary></entry><entry><title type="html">An experimental RISCV instruction compression</title><link href="https://www.xn--tkuka-m3a3v.dev/experimental-riscv-instruction-compression/" rel="alternate" type="text/html" title="An experimental RISCV instruction compression" /><published>2025-08-04T00:00:00+00:00</published><updated>2026-07-03T18:53:07+00:00</updated><id>https://www.xn--tkuka-m3a3v.dev/experimental-riscv-instruction-compression</id><content type="html" xml:base="https://www.xn--tkuka-m3a3v.dev/experimental-riscv-instruction-compression/"><![CDATA[<p>I wanted to experiment with a means of reducing compiled RISCV code size
in a way that did <em>not</em> allow for the creation of un-aligned 32-bit
opcodes, so I had a bit of a tinker with 32-bit packets containing
instruction pairs.</p>

<h2 id="rationale">Rationale</h2>

<p>RISCV sees implementations ranging from lightweight scalar to wide
OOE superscalar, each needing to take very different approaches to how
the instruction stream is ingested.</p>

<p>Things like the large number of instruction entrypoints with unaligned
32-bit opcodes are problematic for out-of-order machines; while the
low-end processors still want to minimise code size and icache burden.</p>

<p>I’ve previously mused over the idea of <a href="/naturally-aligned-instruction-set/">aligned 32-bit packets of 16-bit
instructions</a> with extra constraints to try to make
it easy to ingest the packet as a single opcode, and then to split it
into micro-ops later in the pipeline, where everything else gets split
into micro-ops already.</p>

<p>And at the same time I observed that overlapping <code>rd</code> and <code>rs1</code> operands
is not the only way to overload the bits in an opcode.</p>

<p>So without any real insight into the technicalities of how those things
would work out in practice, I set about making my own little straw-man.</p>

<p>I’ve taken <a href="#references">inspiration</a> from other proposals, and tried to
make such extensions available as pairs of more pedestrian opcodes
within the same 32-bit packet.  So what might look like a CISC
instruction can be dressed up as two compressed RISC instructions
instead; even if one were to implement it as a single fused instruction.</p>

<h2 id="design-objectives">Design objectives</h2>

<ul>
  <li>Support only 32-bit opcode packets, but squeezing pairs of
instructions into those packets for compression.</li>
  <li>Ensure that every such packet can be interpreted in two passes as two
independent instructions, each conforming to the standard RISCV ISA
model (2-in-1-out, etc.).</li>
  <li>Restrict branching to only the final operation of a packet.</li>
  <li>Try to exploit the frequent sharing of common registers in adjacent
instructions to aid compression.</li>
  <li>Capture some proposed instruction extensions which could be
implemented as macro-op fusion instructions and formalise them as
pairs within one 32-bit packet.</li>
  <li>Use no more than 1/4 (30 bits) of the opcode space.</li>
  <li>Make code smaller.</li>
</ul>

<h3 id="references">References</h3>

<p>Qualcomm Znew/Zics:</p>
<ul>
  <li><a href="https://lists.riscv.org/g/tech-profiles/attachment/332/0/code_size_extension_rvi_20231006.pdf">https://lists.riscv.org/g/tech-profiles/attachment/332/0/code_size_extension_rvi_20231006.pdf</a></li>
</ul>

<p>Macro-op fusion stuff:</p>
<ul>
  <li><a href="https://arxiv.org/pdf/1607.02318">https://arxiv.org/pdf/1607.02318</a></li>
  <li><a href="https://en.wikichip.org/wiki/macro-operation_fusion#Proposed_fusion_operations">https://en.wikichip.org/wiki/macro-operation_fusion#Proposed_fusion_operations</a></li>
</ul>

<p>RISCV reference card:</p>
<ul>
  <li><a href="http://riscvbook.com/greencard-20181213.pdf">http://riscvbook.com/greencard-20181213.pdf</a> (warning: non-SSL link)</li>
  <li><a href="https://www.cl.cam.ac.uk/teaching/1617/ECAD+Arch/files/docs/RISCVGreenCardv8-20151013.pdf">https://www.cl.cam.ac.uk/teaching/1617/ECAD+Arch/files/docs/RISCVGreenCardv8-20151013.pdf</a></li>
</ul>

<h2 id="a-provisional-attempt">A provisional attempt</h2>
<p>With no statistical model of instruction-pair frequency, I just guessed
at what might work and came up with the following.</p>

<p>For expediency I’ve only counted the number of instructions in each
class and laid them out sequentially.  It would be folly to try to
arrange the specific bit patterns for efficient decoding before the
supported instruction set is decided.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>       0x0+0x10000000: 14: arith4  rsd,rsd,rs_imm          14: arith4  rsd,rsd,rs_imm          (28 bits)  660 hits
0x10000000+0x10000000: 14: arith4  t6,rs1,rs_imm           14: arith4  rd,t6,rs_imm            (28 bits)  0 hits
0x20000000  +0x800000: 14: arith5i rsd,rsd,imm5             9: arith5i rsd,rsd,{imm}           (23 bits)  79 hits
0x20800000  +0x800000: 14: arith5r rsd,rsd,rs2              9: arith5r rsd,rsd,{rs2}           (23 bits)  1 hits
0x21000000  +0x800000: 14: arith5i rsd,rsd,imm5             9: arith5r rsd,rsd,{rd}            (23 bits)  27 hits
0x21800000  +0x800000: 14: arith5r rsd,rsd,rs2              9: arith5r rsd,rsd,{rd}            (23 bits)  8 hits
0x22000000 +0x2000000: 14: arith4  rsd,rsd,rs_imm          11: beqz    {rd},imm11              (25 bits)  23 hits
0x24000000 +0x2000000: 14: arith4  rsd,rsd,rs_imm          11: bnez    {rd},imm11              (25 bits)  32 hits
0x26000000 +0x1000000: 13: cmpi    t6,rs1,imm5             11: beqz    t6,imm11                (24 bits)  0 hits
0x27000000 +0x1000000: 13: cmpi    t6,rs1,imm5             11: bnez    t6,imm11                (24 bits)  0 hits
0x28000000 +0x2000000: 14: arith4  rsd,rsd,rs_imm          11: j       imm11                   (25 bits)  76 hits
0x2a000000 +0x2000000: 14: arith4  rsd,rsd,rs_imm          11: jal     ra,imm11                (25 bits)  92 hits
0x2c000000  +0x100000: 15: arith5  rsd,rsd,rs_imm           5: jr      rs2                     (20 bits)  16 hits
0x2c100000  +0x100000: 15: arith5  rsd,rsd,rs_imm           5: jalr    ra,rs2                  (20 bits)  7 hits
0x2c200000  +0x200000: 21: --reserved--                    (21 bits)  0 hits
0x2c400000  +0xc00000: 19: pair.a  rd,rs1,rs2               5: {opcode:pair} rd,{rs1},{rs2}    (24 bits)  0 hits
0x2d000000 +0x1000000: 24: ldst    rd,imm10(rs1)            0: {opcode} {rd:next},{imm:next}({rs1})  (24 bits)  341 hits
0x2e000000 +0x8000000: 13: arith3  rsd,rsd,rs_imm          14: ldst    rd,0(rs1)               (27 bits)  364 hits
0x36000000 +0x8000000: 14: ldst    rd,0(rs1)               13: arith3  rsd,rsd,rs_imm          (27 bits)  635 hits
total size: 0x3e000000,  bits: 30
saved=2361, total=10258
</code></pre></div></div>

<p>Or here’s another verison:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>       0x0+0x10000000: 14: arith4  rsd,rsd,rs_imm          14: arith4  rsd,rsd,rs_imm          (28 bits)  658 hits
0x10000000+0x10000000: 14: arith4  t6,rs1,rs_imm           14: arith4  rd,t6,rs_imm            (28 bits)  0 hits
0x20000000  +0x800000: 14: arith5i rsd,rsd,imm5             9: arith5i rsd,rsd,{imm}           (23 bits)  78 hits
0x20800000  +0x800000: 14: arith5r rsd,rsd,rs2              9: arith5r rsd,rsd,{rs2}           (23 bits)  1 hits
0x21000000  +0x800000: 14: arith5i rsd,rsd,imm5             9: arith5r rsd,rsd,{rd}            (23 bits)  27 hits
0x21800000  +0x800000: 14: arith5r rsd,rsd,rs2              9: arith5r rsd,rsd,{rd}            (23 bits)  8 hits
0x22000000 +0x2000000: 14: arith4  rsd,rsd,rs_imm          11: beqz    {rd},imm11              (25 bits)  23 hits
0x24000000 +0x2000000: 14: arith4  rsd,rsd,rs_imm          11: bnez    {rd},imm11              (25 bits)  32 hits
0x26000000 +0x1000000: 13: cmpi    t6,rs1,imm5             11: beqz    t6,imm11                (24 bits)  0 hits
0x27000000 +0x1000000: 13: cmpi    t6,rs1,imm5             11: bnez    t6,imm11                (24 bits)  0 hits
0x28000000 +0x2000000: 14: arith4  rsd,rsd,rs_imm          11: j       imm11                   (25 bits)  79 hits
0x2a000000 +0x2000000: 14: arith4  rsd,rsd,rs_imm          11: jal     ra,imm11                (25 bits)  92 hits
0x2c000000  +0x100000: 15: arith5  rsd,rsd,rs_imm           5: jr      rs2                     (20 bits)  18 hits
0x2c100000  +0x100000: 15: arith5  rsd,rsd,rs_imm           5: jalr    ra,rs2                  (20 bits)  7 hits
0x2c200000  +0x800000: 15: arith5  rsd,rsd,rs_imm           8: sw      {rd},imm8(sp)           (23 bits)  1 hits
0x2ca00000  +0x800000: 15: arith5  rsd,rsd,rs_imm           8: sd      {rd},imm8(sp)           (23 bits)  0 hits
0x2d200000  +0x800000: 13: lw      rd,imm8(sp)             10: arith5  {rd},{rd},rs_imm        (23 bits)  1 hits
0x2da00000  +0x800000: 13: ld      rd,imm8(sp)             10: arith5  {rd},{rd},rs_imm        (23 bits)  1 hits
0x2e200000  +0x200000: 21: --reserved--                    (21 bits)  0 hits
0x2e400000  +0xc00000: 19: pair.a  rd,rs1,rs2               5: {opcode:pair} rd,{rs1},{rs2}    (24 bits)  0 hits
0x2f000000 +0x1000000: 19: ldst    rd,imm5(rs1)             5: {opcode} rd,{imm:next}({rs1})   (24 bits)  402 hits
0x30000000 +0x8000000: 13: arith3  rsd,rsd,rs_imm          14: ldst    rd,0(rs1)               (27 bits)  372 hits
0x38000000 +0x8000000: 14: ldst    rd,0(rs1)               13: arith3  rsd,rsd,rs_imm          (27 bits)  620 hits
total size: 0x40000000,  bits: 30
saved=2420, total=10258
</code></pre></div></div>

<p>Other opcodes like breakpoint can be overloaded in the rd=0 space.  Or fall
back to 32-bit encoding.</p>

<p>The <code>mem,mem</code> operations essentially mimic the load/store pair
instructions proposed by Qualcomm, but lacking pre/post-increment
because that would break the 2-in-1-out contract in a two-round
implementation.  These share the base register and immediate offset
arguments, and the destination register is a consecutive pair.</p>

<p>The <code>arithmetic,mem</code> and <code>mem,arithmetic</code> pairs provide the
pre/post-increment operations proposed by Qualcomm, but are then
generalised to offer other arithmetic operations as well.  There are
details to work out, here, regarding how the implicit shift produced by
a load operation should interact with various types of arithmetic.</p>

<p>The <code>mem,arithmetic</code> pairs should probably be defined to prohibit use of
the load result in the second operation, even though this is probably a
very reasonable thing to expect to do in general.</p>

<p>And the <code>cmp,b</code> pairs produce the <code>beqi</code> and <code>bnqi</code> Qualcomm proposal.</p>

<p>The notes about <code>hits</code> and <code>saved</code> (you need to scroll right) are how
many times that pair was used by a simplistic regex (currently only
considering adjacent pairs) on a trivial benchmark which I ran through
qemu.  In the case of duplication the first viable row takes the credit.</p>

<p>About 2400 intructions out of 10000 instructions were squeezed into the
preceeding instruction.  The original RVC compression used about 5500
16-bit opcodes, so to compare like-for-like that means I used 4800
“16-bit opcodes”.</p>

<p>I don’t think that’s too bad considering that no tuning has been done
either in my opcode selection or in the compiler to put things in a
viable order.  And I’ve put a lot of space into things the compiler
<em>obviously</em> wouldn’t generate without modification.</p>

<p>Big caveat regarding the quality of my regular expressions, though.</p>

<h4 id="loadstore-ops">load/store ops</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| RV32  | RV64  | RV128 |
|-------|-------|-------|
| `lb`  | `lb`  | `lb`  |
| `lh`  | `lh`  | `lh`  |
| `lw`  | `lw`  | `lw`  |
|  --   | `ld`  | `ld`  |
|  --   |  --   | `lq`  |
| `sb`  | `sb`  | `sb`  |
| `sh`  | `sh`  | `sh`  |
| `sw`  | `sw`  | `sw`  |
|  --   | `sd`  | `sd`  |
|  --   |  --   | `sq`  |
| `lbu` | `lbu` | `lbu` |
| `lhu` | `lhu` | `lhu` |
| `flw` | `lwu` | `lwu` |
|  --   | `fld` | `fld` |
| `fsw` |  --   |  --   |
|  --   | `fsd` | `fsd` |
</code></pre></div></div>

<p>The x/y options differ between RV32, RV64, and RV128; if the unsigned
version would be identical to the signed version because that is the
native word size, then this instruction is repurposed as a native-sized
floating-point load or store instead (resulting in RV128 having no
floating-point load or store – oh well).</p>

<h4 id="arithmetic-ops">arithmetic ops</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| 3 bits, 50% immediates|
|-----------------------|
| `addi`     # imm+0    |
| `addi`     # imm+32   |
| `addi`     # imm-64   |
| `addi`     # imm-32   |
| `add`                 |
| `sub`                 |
| `and`                 |
| `or`                  |
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>|4 bits, 50% immediates |
|-----------------------|
| `addi`     # imm+0    |
| `addi`     # imm-32   |
| `addiw`    # imm+0    |
| `addiw`    # imm-32   |
| `addi4spn` # imm+0    |
| `addi4spn` # imm+32   |
| `andi`     # imm+0    |
| `andi`     # imm-32   |
| `add`                 |
| `addw`                |
| `sub`                 |
| `subw`                |
| `and`                 |
| `bic`                 |
| `or`                  |
| `xor`                 |
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>|5 bits, 50% immediates |
|-----------------------|
| `addi`     # imm+0    |
| `addi`     # imm-32   |
| `addiw`    # imm+0    |
| `addiw`    # imm-32   |
| `andi`     # imm+0    |
| `andi`     # imm-32   |
| `addi4spn` # imm+0    |
| `addi4spn` # imm+32   |
| `slli`     # imm+0    |
| `slli`     # imm+32   |
| `srli`     # imm+0    |
| `srli`     # imm+32   |
| `srai`     # imm+0    |
| `srai`     # imm+32   |
| `rsbi`     # imm+0    |
| `rsbi`     # imm+32   |
| `add`                 |
| `addw`                |
| `sub`                 |
| `subw`                |
| `and`                 |
| `bic`                 |
| `or`                  |
| `xor`                 |
| `mul`                 |
| `mulh`                |
| `div`                 |
| `rem`                 |
| `sll`                 |
| `srl`                 |
| `sra`                 |
</code></pre></div></div>

<p>For the <code>addi*4spn</code> instruction, the <code>rsd</code> field is used simply as <code>rd</code>
and <code>sp</code> is used as the new <code>rs1</code>.  Also the immediate is multiplied by
four.  I suppose this should be an <em>unsigned</em> immediate because that’s
where all the useful data is.  A couple of other operations need
unsigned immediates, too.</p>

<p>Where the second operation borrows its <code>rs2_imm</code> argument from the first
operation it doesn’t have free choice between a register or immediate
value.  Consequently one bit of the encoding is redundant.  I’ll fix
that later.  In fact, while sharing the immediate between two
insturctions makes sense (eg., <code>shl</code>/<code>shr</code> patterns), it’s less clear
that the extra bit of free choice for immediate serves any purpose.  But
it’s harder to recycle that bit.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>|cmp (3 bits, all immediates)|
|----------------------------|
| `slti`     # imm+0         |
| `slti`     # imm-32        |
| `sltiu`    # imm+0         |
| `sltiu`    # imm+32        |
| `seqi`     # imm+0         |
| `seqi`     # imm-32        |
| `bittesti` # imm+0         |
| `bittesti` # imm+32        |
</code></pre></div></div>

<p>I don’t think <code>bittest</code> is a thing in any RISCV extension?  But I’m
throwing it in here because it fills a niche.  The immediate operand is
the bit index to test and to branch on.</p>

<p>Some instructions I just made up to fill in gaps while I didn’t want to
think about it.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| pairs (4 bits, no immediates) |
| `add`     | `sltu`    |
| `sub`     | `add`     |
| `min`     | `max`     |
| `minu`    | `maxu`    |
| `and`     | `bic`     |
| `mulhsu`  | `mul`     |
| `mulh`    | `mul`     |
| `mulhu`   | `mul`     |
| `div`     | `rem`     |
| `divu`    | `remu`    |
| `???`     | `???`     |
| `???`     | `???`     |
</code></pre></div></div>

<p>The use of an <code>add,sltu</code> pair forms and add-with-carry, but is
problematic in its definition.  It breaks the pattern of sharing both
source registers, needing the result of the previous add instead,
<em>unless</em> the <code>sltu</code> part is instead redefined to be a different
operation which simply computes the carry from the inputs.</p>

<p>TODO: Extracting carry like this raises questions on whether overflow is also warranted, and also if there should be branching versions of the same ops for efficiently handling small arithmetic with low-overhead escapes to longer arithmetic as needed (signed and/or unsigned, like as needed in in python and JavaScript).</p>

<h3 id="caveats">Caveats</h3>
<ul>
  <li>Arithmetic paired with ldst are affected by the ld/st width (yikes?),
which means that if you overwrite the load with a breakpoint you still 
need to be able to encode the effect on the adjacent op.</li>
  <li>Also, I didn’t think too hard about statistical merits of any of these
choices.  I took some guidance from the existing compressed
instruction extension to keep it in roughly the right place, but my
changes may add their own implications.</li>
  <li>There might be much too much overlap between the different register
sharing modes for arithmeric.  This needs to be looked at still.</li>
</ul>

<h3 id="variations">Variations</h3>
<ul>
  <li>For <code>mem,mem</code> the immediate could be smaller and the pair of
destination registers could be arbitrary, consistent with the
arithmetic instructions which share both source registers.</li>
  <li><code>op.full rd,rs,rs ; =~op.full =rd,=rd,rs</code> is also 25 bits and could
probably be more use in that it doesn’t corrupt the original sources.
just have to pick a sensible <code>~op.full</code>.</li>
  <li>As well as the usual overloading of <code>Rd=x0</code>, it might make sense, for
example, if <code>Rsd=t6</code> then to read that as <code>sp</code> and write <code>t6</code> in the
first opcode, and then read <code>Rsd</code> in the second operation as <code>t6</code> and
write <code>Rsd</code> as the register actually specified.  Or something like
that.</li>
  <li>I notice there’s this <code>RV32E</code> profile, which discards the top 16
registers from the register file.  This might be a reasonable
compromise to repurpose a couple of bits, and presumably it’s easier
to get the compiler to generate test code for it.</li>
</ul>

<h3 id="questions">Questions</h3>
<ul>
  <li>I didn’t do anything about an optimisation using the same rd for both
instructions (implicit discard of the first result after it’s used by
the second).  Why is that?</li>
  <li>When an arithmetic instruction has an implicit shift provided by being
paired with a load or store (which has a data size), when should it
apply?  Should it affect only immediates?  (I say no!)  Should it
affect only add and sub operations?  Should it affect only operations
whose destination register is the same as the base register in the
memory operation?</li>
  <li>What are the alignment constraints of these <code>mem,mem</code> ops?  I don’t
know!</li>
  <li>Did I choose the right basic arithmetic instructions?</li>
</ul>

<h3 id="observations">Observations</h3>

<p>Reserving a portion of the coding space for compressed instructions it’s different from Thumb.  One doesn’t have to squeeze everything in.
If something is difficult it can be ignored and left to the 32-bit encoding, leaving coding space to allow anything else to capture more cases.</p>

<p>On the other hand it’s tempting to hang on to some of the CISC-like tuples on the basis that they are strong candidates for fusion, and sometimes that <em>is</em> a squeeze.
It’s bad form to pre-suppose the implementation in the ISA, but it’s still tempting to make such an optimisation available.</p>

<h2 id="next-steps">Next steps</h2>
<p>I really need more data about why each instruction fails to fall into a pair.  Is it because I chose the wrong shortlist of opcodes, or because the operand constraints don’t fit, or because the immediate is too big?  A lot of this hangs on choices the compiler made, which in turn reflect the instruction set it was aiming for, but I don’t think I’m capable of iterating over the compiler’s notion of available instructions, so I’ll just use proxy configurations instead.</p>

<p>As a general guide I plan to use:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-riscv64-static -d nochain,in_asm,execxx ./benchmark
</code></pre></div></div>
<p>(or something like that) to collect translation blocks of instructions
and count the number of times each block is executed.  These blocks,
compiled in different ways, can be used for a casual measure of the
compression ratio, but it would rely on some re-ordering of instructions
and a contract in the compiler to not use the <code>t6</code> register because I
borrowed it for some operation pairs with throwaway results.</p>

<p>What would be better is to see how different arrangements fare in an
actual compiler trying to optimise for them, but I don’t know if that’s
a realistic thing to experiment with.</p>]]></content><author><name>sh1boot</name></author><category term="computer-architecture" /><summary type="html"><![CDATA[I wanted to experiment with a means of reducing compiled RISCV code size in a way that did not allow for the creation of un-aligned 32-bit opcodes, so I had a bit of a tinker with 32-bit packets containing instruction pairs.]]></summary></entry><entry><title type="html">How I made a gzip encoder faster than memcpy</title><link href="https://www.xn--tkuka-m3a3v.dev/direct-gzip-synthesis/" rel="alternate" type="text/html" title="How I made a gzip encoder faster than memcpy" /><published>2025-07-02T00:00:00+00:00</published><updated>2026-07-06T19:47:44+00:00</updated><id>https://www.xn--tkuka-m3a3v.dev/direct-gzip-synthesis</id><content type="html" xml:base="https://www.xn--tkuka-m3a3v.dev/direct-gzip-synthesis/"><![CDATA[<p>In the compression world it’s usual to compare the time spent
compressing and decompressing data with the time difference in
transmitting the compressed or uncompressed data over a given network.
In this experiment I managed to make the compression faster than the
bandwidth to RAM.  Sort of.  Under special circumstances and with no apologies for
the egregious clickbait headline.</p>

<p>In the simplest possible terms this compression works by maintaining a
dictionary of pre-cooked strings, appending those to the output
stream, and noting when they’ve already been emitted recently (a simple
index to last use with bounds check) and emitting a backreference code
instead of the full string in those cases.</p>

<p>Non-pre-cooked strings are not supported efficiently.  It’s an encoder
restricted to very specific applications.  Probably.</p>

<p>The bit-packing overhead is obviated by contriving Huffman codes which
<a href="/more-efficient-nonsense-text/">always fall on byte boundaries</a>.  This is impossible for a
generic octet stream in the Delete format, but is achievable for UTF-8 text.</p>

<p>The <em>hard part</em> turned out to be the checksum calculation.  When I
thought of the idea I assumed (hoped) it would be an Adler32 checksum
where it is easy to reason about appending precomputed checksums to the
running checksum.  It turned out gzip uses CRC32, and gzip is the
preferred format over zlib in web browsers.  So I had to figure out how to
append CRC checksums as efficiently as possible.</p>

<p>It turns out you can precompute the string checksum and store the string
length as a multiplier to be applied to the running checksum via <a href="Https://en.wikipedia.org/wiki/CLMUL_instruction_set">clmul</a>
and folding that with a 64-bit crc32 operation.</p>

<p>Arm has CPU instructions for both of these operations, but x86 only has
the former (its CRC instruction uses the wrong polynomial), which means
using clmul to calculate the crc as well.  Typically this is optimised
for SIMD use, but a scalar operation is all that’s needed here.  I
suspect the extra work to batch it into SIMD chunks would be worse than
the savings.</p>

<p>TODO: a bunch of extra exposition</p>

<p>Here’s the code: <a href="https://github.com/sh1boot/defl-8bit">defl-8bit</a>.</p>

<h2 id="possible-improvements">possible improvements</h2>
<ul>
  <li>Write a preprocessor to break input text into strings at the most
appropriate boundaries, adding flexibility in random string
generation.</li>
  <li>Implement the higher-level backref operator so multiple backreferences
can be consolidated and their checksums can be computed as the
difference between start and end of previous copy.</li>
  <li>Make larger backrefs using the conventional rolling hash thing, but on
the precomputed string fragments rather than every byte.</li>
  <li>Or, remember previous backref distance and merge them when possible.</li>
  <li>Clean up the code.</li>
  <li>Figure out a proper generic interface with virtual methods in places
that make sense and don’t have scary performance implications.</li>
  <li>Add a practical fallback implementation for CRC for webasm
compatibility (all that work for nothing!).</li>
  <li>Does the Adler-32 implementation even work?</li>
  <li>Tweak the clmul crc for performance.</li>
  <li>Tweak everything else for performance.</li>
  <li>Clean up this post.</li>
</ul>]]></content><author><name>sh1boot</name></author><category term="compression" /><category term="number-theory" /><category term="crc" /><summary type="html"><![CDATA[In the compression world it’s usual to compare the time spent compressing and decompressing data with the time difference in transmitting the compressed or uncompressed data over a given network. In this experiment I managed to make the compression faster than the bandwidth to RAM. Sort of. Under special circumstances and with no apologies for the egregious clickbait headline.]]></summary></entry><entry><title type="html">Hiding messages in machine-generated text</title><link href="https://www.xn--tkuka-m3a3v.dev/steganographic-llm/" rel="alternate" type="text/html" title="Hiding messages in machine-generated text" /><published>2025-06-14T00:00:00+00:00</published><updated>2026-07-06T19:47:44+00:00</updated><id>https://www.xn--tkuka-m3a3v.dev/steganographic-llm</id><content type="html" xml:base="https://www.xn--tkuka-m3a3v.dev/steganographic-llm/"><![CDATA[<p>On a whim I thought I’d try getting <a href="https://chatgpt.com/">ChatGPT</a> to do a bit of
<a href="https://en.wikipedia.org/wiki/steganography">steganography</a> for me.  There are a bazillion ways (give or take) for
hiding a secret message in unrelated cleartext, where there’s a
trade-off of secret bandwidth against cleartext flexibility.  I chose
<a href="https://en.wikipedia.org/wiki/Morse_code">Morse code</a> encoded in the last letter of each word, because it’s
obvious and easy to express as rules that anybody can follow.</p>

<p>Anybody except for ChatGPT, it turns out.</p>

<p>The rules I gave were simple enough:</p>
<ul>
  <li>A word ending with a vowel represents a dot.</li>
  <li>A word ending with a consonant represents a dash.</li>
  <li>A word ending with a y represents the gap between letters.</li>
  <li>Express a message in morse code, using the above substitutions of
words for symbols.</li>
  <li>The words should be chosen to form coherent sentences.</li>
</ul>

<p>That seemed to leave plenty of freedom for choosing words.</p>

<p>It turns out schemes like these are called <a href="https://en.wikipedia.org/wiki/Acrostic">acrostics</a> or telestichs
(the latter in my case).  The extra layer of using morse code and groups
of letters helps to make it less obvious than traditional acrostics, but
it takes several words to make a letter of the hidden message.</p>

<p>I thought an LLM should be able to churn out a coherent paragraph under
those constraints, and I’m sure that it could if it could remember what
it was supposed to do, but I had some difficulties.</p>

<p>For the secret message <code>example</code>, ChatGPT gave me:</p>
<blockquote>
  <p>Alone Henry left a trail then slept quietly, and followed slowly too.</p>
</blockquote>

<p>I don’t know what that means or why it wrote it, but it decodes as:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>. -.--- -- .
</code></pre></div></div>
<p>Which comes back out as <code>E�ME</code>.  That’s not quite right.</p>

<p>Let’s try fixing it by hand:</p>
<blockquote>
  <p>Alone, Henry left the tree. Then sleepily he sat. Slowly following on
my horse. What could he say? The adverbs are too many to try.</p>
</blockquote>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>. -..- .- -- .--. .-.. . 
</code></pre></div></div>

<p>There.  Fixed.  But that wasn’t as much fun as I thought it would be.
The adverbs are indeed too many.  While there are plenty of words ending
in y to choose from, it gets hard to think of things that aren’t adverbs
or adjectives.  And that need comes up too frequently.</p>

<p>Obviously having only words ending in one letter to choose from for that
marker is too restrictive, and y is an especially distracting choice.
Some markers could simply be dropped because there are a lot of cases
where it’s not ambiguous, but unambiguous cases are at the end of
infrequent letters, where it’s frustrating already.</p>

<p>I won’t try to fix this because it’s not my priority.  All I wanted was
something with the simplicity of a children’s game.  Unfortunately I
feel like it’s a bit too tedious for many kids to encode their own
messages.  At least without the support of an editor or thesaurus to
offer up practical synonyms.</p>

<p>Or, of course, one could just make an LLM do it, as was the original
plan.  I’m sure it <em>should</em> have no trouble if it can be put in a
suitable wrapper to keep it on track.  But I’m too lazy, which is why I
went to ChatGPT in the first place.</p>

<p>But there are many related schemes would could be devised, and I think
technology is in a place, right now, where it should be trivial to
automate.  I just can’t be bothered doing that.</p>

<p>Update: I got Claude to do me a <a href="https://github.com/sh1bot/morse_stego">demo implementation</a>.</p>]]></content><author><name>sh1boot</name></author><category term="steganography" /><category term="ai" /><summary type="html"><![CDATA[On a whim I thought I’d try getting ChatGPT to do a bit of steganography for me. There are a bazillion ways (give or take) for hiding a secret message in unrelated cleartext, where there’s a trade-off of secret bandwidth against cleartext flexibility. I chose Morse code encoded in the last letter of each word, because it’s obvious and easy to express as rules that anybody can follow.]]></summary></entry></feed>