---
title: Instruction Formats and Operands
module: Instruction Set Architecture
moduleNumber: 2
lessonNumber: 2
order: 202
summary: >
  An instruction is an opcode plus a way to name its operands. We count operands —
  3-address, 2-address, 1-address accumulator, and 0-address stack machines — by
  writing the same C = A + B four ways, weigh register operands against memory
  operands, then lay out the same add byte by byte in x86-64
  (REX prefix, opcode, ModRM) and in Y86-64, and what fixed versus variable
  length costs at fetch time.
topics: [Instruction Set Architecture]
sources:
  - book: Bryant & O'Hallaron
    ref: "CS:APP — §4.1 The Y86-64 Instruction Set Architecture"
  - book: Bistriceanu
    ref: "Computer Architecture Notes — §3 Instruction Set Design"
---

Every instruction answers two questions: _what operation_ and _on which data_. The
first is the **opcode**, a small code naming the operation: add, load, jump. The
second is the **operands**, the names of the inputs and the destination. The
deepest structural choice an ISA designer makes is how many operands an instruction
is even allowed to name, because that single number determines the size of every
instruction, the number of instructions a program needs, and how much hardware the
machine must carry. This lesson counts operands by running one tiny computation
through four machines, then makes the abstraction physical by laying out real
instructions byte by byte.

## Opcode plus operands

An instruction is a packed binary word. Conceptually it splits into fields: a field
for the opcode and one field per operand. The opcode field's width fixes how many
distinct operations the ISA can have ($k$ bits name $2^k$ opcodes); each operand
field's width fixes how many things that operand can name. A register operand needs
only enough bits to index the register file — four bits for sixteen registers — so
register operands are cheap to encode. A memory operand may need a full address, and
addresses are wide.

$$
% caption: An instruction word as fields. The opcode names the operation; the
% caption: operand fields name the inputs and destination. A register operand is a
% caption: small index (4 bits for 16 registers); a memory or immediate operand may
% caption: need many more bits, which is what makes operand count expensive.
\begin{tikzpicture}[font=\footnotesize,
  f/.style={draw, minimum height=8mm, inner sep=0pt}]
  \definecolor{acc}{HTML}{2348F2}
  \node[f, minimum width=22mm, fill=acc!8, text=acc] (op) at (0,0) {opcode};
  \node[f, minimum width=14mm] (d) at (1.8,0) {dest};
  \node[f, minimum width=14mm] (s1) at (3.2,0) {src 1};
  \node[f, minimum width=14mm] (s2) at (4.6,0) {src 2};
  \node[anchor=south, font=\scriptsize] at (0,0.45) {$k$ bits};
  \node[anchor=south, font=\scriptsize] at (1.8,0.45) {4};
  \node[anchor=south, font=\scriptsize] at (3.2,0.45) {4};
  \node[anchor=south, font=\scriptsize] at (4.6,0.45) {4};
  \node[anchor=west, text=acc] at (5.7,0) {one register-machine layout};
\end{tikzpicture}
$$

How many operand fields there are is the **operand count**, and it defines a whole
class of machine.

## One computation, four machines

Take the most ordinary statement imaginable, `C = A + B`, where `A`, `B`, and `C`
live in memory. Each machine class below computes exactly this, and the contrast is
the entire point: as operands per instruction shrink, instructions grow more
numerous and lean harder on hidden state. One notational caution: the generic
pseudo-assembly here writes the destination _first_ (`add C, A, B` means
$C = A + B$), the usual textbook convention, which is the opposite of the AT&T
destination-last order used everywhere else in this course.

$$
% caption: C = A + B on four machine classes. A 3-address instruction names all
% caption: operands explicitly; a 2-address machine overwrites a source; a 1-address
% caption: machine keeps one operand in a hidden accumulator; a 0-address machine
% caption: keeps operands on a stack and names none. Fewer operands, more instructions.
\begin{tikzpicture}[font=\footnotesize,
  hd/.style={draw, fill=acc!8, text=acc, thick, minimum width=33mm,
             minimum height=7mm, align=center},
  cell/.style={align=left, text width=31mm, font=\ttfamily\footnotesize}]
  \definecolor{acc}{HTML}{2348F2}
  \node[hd] (a) at (0,0)    {3-address};
  \node[hd] (b) at (3.6,0)  {2-address};
  \node[hd] (c) at (7.2,0)  {1-address (accum.)};
  \node[hd] (d) at (10.8,0) {0-address (stack)};
  \node[cell] at (0,-1.3)    {add C, A, B};
  \node[cell] at (3.6,-1.15)  {mov C, A\\add C, B};
  \node[cell] at (7.2,-1.15)  {load A\\add B\\store C};
  \node[cell] at (10.8,-1.45) {push A\\push B\\add\\pop C};
  \draw (-1.8,-0.5) -- (12.6,-0.5);
\end{tikzpicture}
$$

**3-address** machines name every operand. `add C, A, B` reads `A` and `B`, adds
them, writes `C` — one instruction, three operands. This is the most expressive and
the most expensive to encode, since three operand fields must fit in the
instruction word. Most RISC ISAs are register 3-address: `add %rd, %rs1, %rs2`.

**2-address** machines drop the separate destination by making one source double as
the destination. `add C, B` means `C = C + B`, so reaching `C = A + B` first
requires `mov C, A`. Two instructions, but each is shorter. x86 is a 2-address
machine: `addq %rsi, %rdi` computes `%rdi = %rdi + %rsi`, clobbering `%rdi`.

**1-address** machines name a single operand and keep the other in a dedicated
hidden register, the **accumulator**. Every arithmetic instruction implicitly reads
and writes the accumulator: `load A` puts `A` there, `add B` adds `B` into it,
`store C` writes it out. Instructions are tiny, one operand each, but a program is
a long chain of loads and stores shuttling values through the one accumulator.

**0-address** machines name _no_ operands at all. Operands live on a **stack**;
arithmetic instructions pop their inputs from the top and push the result back.
`push A` and `push B` load the stack, `add` pops two and pushes their sum, `pop C`
stores it. This is how a [stack-based virtual machine](https://en.wikipedia.org/wiki/Stack_machine)
like the JVM works, and why its bytecode is so compact.

> **Definition (Accumulator).** A single, implicit register that one operand class
> of instructions always reads from and writes to. A 1-address machine names one
> explicit operand and takes the other — and the destination — to be the accumulator.

The trade is consistent: fewer operand fields make each instruction smaller and
simpler to decode, but force the program to spell the computation out in more
steps, leaning on implicit state (an accumulator, a stack) the instruction no
longer names.

The one-line example understates the effect; it grows with the expression. Take
something slightly larger, `E = (A + B) * (C + D)`, with all five in memory, and
count instructions per class. A 3-address machine needs four: two adds into
temporaries and a multiply, plus one store — actually three arithmetic and one
store if the destination is memory, four in all. A 2-address machine must first
`mov` each accumuland into place before it can overwrite it, roughly doubling the
count to seven. The 1-address accumulator threads everything through one register,
so it pays a `load`/`store` pair around each subexpression: `load A`, `add B`,
`store t1`; `load C`, `add D`; `mul t1`; `store E` — eight. The 0-address stack
machine names nothing but pushes every leaf: `push A`, `push B`, `add`, `push C`,
`push D`, `add`, `mul`, `pop E` — eight again, but each instruction is tiny.

$$
% caption: Instruction count for E = (A+B)*(C+D) across the four classes. More
% caption: operand fields per instruction means fewer, larger instructions; fewer
% caption: fields means more, smaller ones threaded through implicit state. The two
% caption: extremes trade instruction size against instruction count.
\begin{tikzpicture}[font=\footnotesize, >=stealth,
  hd/.style={draw, fill=acc!8, text=acc, thick, minimum width=24mm,
             minimum height=7mm, align=center},
  bar/.style={fill=acc!8, draw=acc}]
  \definecolor{acc}{HTML}{2348F2}
  \foreach \x/\lbl/\n/\h in {
    0/{3-addr}/{4}/{1.2},
    3.0/{2-addr}/{7}/{2.1},
    6.0/{1-addr}/{8}/{2.4},
    9.0/{0-addr}/{8}/{2.4}} {
    \node[hd] at (\x,-0.9) {\lbl};
    \draw[bar] (\x-0.6,-1.3) rectangle (\x+0.6,-1.3-\h);
    \node[anchor=north, font=\scriptsize, text=acc] at (\x,-1.3-\h-0.05) {\n\ instr.};
  }
  \node[anchor=west, font=\scriptsize] at (-1.4,-4.35)
        {(instruction \emph{size} shrinks left to right; \emph{count} grows)};
\end{tikzpicture}
$$

## Register operands versus memory operands

Cutting across operand count is a second axis: may an operand live in **memory**, or
must it first be in a **register**? It matters because the two are not remotely the
same speed. A register access is essentially free — the value is right there in the
datapath — while a memory access costs many cycles even on a hit and far more on a
[cache miss](/computer-architecture/memory-hierarchy/cache-memories-direct-mapped).

This is why the **register machine** won. Modern ISAs give the programmer a
generous bank of fast registers and steer almost all work through them. The extreme
form is the RISC **load/store** discipline from the
[previous lesson](/computer-architecture/instruction-set-architecture/what-an-isa-is):
arithmetic instructions take _only_ register operands, and the _only_ instructions
that touch memory are explicit `load` and `store`. The compiler keeps hot values in
registers across long stretches of code, paying for memory traffic only when it must.

$$
% caption: Why register operands dominate. An arithmetic instruction reading
% caption: register operands gets its inputs from the fast register file in the same
% caption: cycle; a memory operand must first travel up the slow path from memory,
% caption: which is why register machines keep hot values in registers.
\begin{tikzpicture}[font=\footnotesize, >=stealth,
  box/.style={draw, minimum width=20mm, minimum height=9mm, align=center}]
  \definecolor{acc}{HTML}{2348F2}
  \node[box, fill=acc!8, text=acc, thick] (alu) at (0,0) {ALU};
  \node[box] (rf) at (-3.4,0) {register\\f\/ile};
  \node[box] (mem) at (-3.4,-2.0) {memory};
  \draw[->, thick] (rf.east) -- (alu.west)
        node[midway, above, font=\scriptsize, text=acc] {1 cycle};
  \draw[->] (mem.north) -- (rf.south)
        node[midway, right, font=\scriptsize] {load (many cycles)};
  \node[anchor=west, align=left, text width=42mm] at (1.4,0)
        {register operands feed the ALU directly; memory operands must be loaded
         f\/irst};
\end{tikzpicture}
$$

## Real bytes: one add, two encodings

Fields and operand counts stay abstract until you watch an assembler pack them.
Here is the same 2-address register add in the two ISAs this course uses, byte for
byte.

In x86-64, `addq %rsi, %rdi` assembles to exactly three bytes: `48 01 f7`. Each
byte is a field with a job.

- **`48`, the REX prefix.** Its high nibble `0100` marks it as REX; its low four
  bits are flags `W R X B`. Here only `W = 1` is set, which promotes the operation
  to 64 bits. (The `R` and `B` bits, zero here, extend the register fields to reach
  `%r8`–`%r15`, the encoding's escape hatch for having sixteen registers but only
  3-bit register fields, a seam left by x86's 8-register past.)
- **`01`, the opcode**: "add a 64-bit register into a register or memory operand."
- **`f7`, the ModRM byte**, which names both operands in one byte: two bits
  `mod`, three bits `reg`, three bits `rm`. `f7` is `11 110 111`: `mod = 11` says
  the `rm` operand is a plain register (not memory), `reg = 110` names the source
  `%rsi` (register 6), and `rm = 111` names the destination `%rdi` (register 7).

$$
% caption: addq %rsi,%rdi in x86-64 is 48 01 f7. The REX prefix's W bit widens the
% caption: op to 64 bits; 01 is the add opcode; the ModRM byte 11 110 111 packs
% caption: both operands — mod 11 means register-direct, reg 110 is the source %rsi,
% caption: rm 111 is the destination %rdi.
\begin{tikzpicture}[font=\footnotesize,
  by/.style={draw, minimum width=22mm, minimum height=9mm, inner sep=0pt},
  bit/.style={draw, minimum width=12mm, minimum height=7mm, inner sep=0pt,
              font=\scriptsize}]
  \definecolor{acc}{HTML}{2348F2}
  % byte row
  \node[by] (b0) at (0,0)   {$\mathtt{48}$};
  \node[by] (b1) at (2.4,0) {$\mathtt{01}$};
  \node[by, draw=acc, thick, text=acc] (b2) at (4.8,0) {$\mathtt{f7}$};
  \node[anchor=south, font=\scriptsize] at (0,0.5)   {REX pref\/ix};
  \node[anchor=south, font=\scriptsize] at (2.4,0.5) {opcode: add};
  \node[anchor=south, font=\scriptsize, text=acc] at (4.8,0.5) {ModRM};
  % ModRM expansion
  \node[bit] (m0) at (2.9,-1.7) {mod $\mathtt{11}$};
  \node[bit] (m1) at (4.8,-1.7) {reg $\mathtt{110}$};
  \node[bit] (m2) at (6.7,-1.7) {rm $\mathtt{111}$};
  \draw[black] (b2.south west) -- (m0.north west);
  \draw[black] (b2.south east) -- (m2.north east);
  \node[anchor=north, font=\scriptsize] at (2.9,-2.15) {register-direct};
  \node[anchor=north, font=\scriptsize] at (4.8,-2.15) {src $= \mathtt{\%rsi}$ (6)};
  \node[anchor=north, font=\scriptsize] at (6.7,-2.15) {dst $= \mathtt{\%rdi}$ (7)};
\end{tikzpicture}
$$

The Y86-64 encoding of the same operation, `addq %rsi, %rdi`, is _two_ bytes:
`60 67`. The first byte's high nibble `6` is the instruction code for the
arithmetic family, its low nibble `0` selects add; the second byte packs the two
4-bit register IDs, source `%rsi` = 6 and destination `%rdi` = 7. Nothing is
implicit: with 4-bit register fields there is no REX-style
escape hatch to bolt on, and the two operands take exactly the byte they need.

$$
% caption: The same add in Y86-64 is two bytes, 60 67: high nibble 6 is the
% caption: arithmetic family, low nibble 0 selects add, and the second byte holds
% caption: the two 4-bit register IDs rA = %rsi (6) and rB = %rdi (7). Regularity
% caption: here is not more expensive: it is one byte cheaper than x86-64.
\begin{tikzpicture}[font=\footnotesize,
  ni/.style={draw, minimum width=11mm, minimum height=9mm, inner sep=0pt}]
  \definecolor{acc}{HTML}{2348F2}
  \node[ni, fill=acc!8, text=acc] (i0) at (0,0)    {$\mathtt{6}$};
  \node[ni, fill=acc!8, text=acc] (i1) at (1.1,0)  {$\mathtt{0}$};
  \node[ni] (r0) at (2.5,0)  {$\mathtt{6}$};
  \node[ni] (r1) at (3.6,0)  {$\mathtt{7}$};
  \node[anchor=south, font=\scriptsize, text=acc] at (0,0.5)   {icode};
  \node[anchor=south, font=\scriptsize, text=acc] at (1.1,0.5) {ifun};
  \node[anchor=south, font=\scriptsize] at (2.5,0.5) {rA};
  \node[anchor=south, font=\scriptsize] at (3.6,0.5) {rB};
  \node[anchor=north, font=\scriptsize] at (0.55,-0.5) {OPq: add};
  \node[anchor=north, font=\scriptsize] at (2.5,-0.5) {$\mathtt{\%rsi}$};
  \node[anchor=north, font=\scriptsize] at (3.6,-0.5) {$\mathtt{\%rdi}$};
  \node[anchor=west] at (4.9,0) {2 bytes total, vs.\ 3 for x86-64};
\end{tikzpicture}
$$

The comparison flips the moment a large constant enters. x86-64's
`movq $0x100, %rax` is seven bytes: REX, opcode, ModRM, then the constant as a
4-byte immediate that the hardware sign-extends to 64 bits, because the encoder
exploits the fact that most constants are small. Y86-64's equivalent
`irmovq $0x100, %rax` is ten bytes: its one immediate format always carries a full
8-byte constant, small value or not. That is the density tax of regularity in one
example: Y86-64 spends three extra bytes to keep its format count at a handful,
while x86-64 spends encoder and decoder complexity to shave them. Neither is
wrong; the two designs optimize different costs.

The ModRM byte does more than name two registers, and one more x86-64 example shows
where the CISC complexity actually accumulates. A _memory_ operand with an index —
`movq (%rbx,%rcx,8), %rax`, loading `a[i]` where `%rbx` is the base and `%rcx` the
index — cannot fit in ModRM alone, because ModRM has three bits for the `rm` operand
and there is no room to name a second register and a scale. So the encoding grows a
fourth byte, the **SIB** byte (scale-index-base), which ModRM's `rm = 100` escapes
to. The instruction assembles to `48 8b 04 cb`: `48` REX, `8b` the load opcode, `04`
the ModRM (`mod = 00`, `reg = 000` for `%rax`, `rm = 100` meaning "SIB follows"), and
`cb` the SIB byte (`scale = 11` for $\times 8$, `index = 001` for `%rcx`, `base =
011` for `%rbx`). Four bytes to encode one addressing calculation the hardware then
performs — base plus index times scale — in the address-generation unit.

$$
% caption: movq (%rbx,%rcx,8),%rax in x86-64 is four bytes 48 8b 04 cb. ModRM's rm
% caption: field 100 is an escape meaning "a SIB byte follows"; the SIB byte then
% caption: encodes scale (11 = x8), index (%rcx), and base (%rbx). The whole scaled-
% caption: index address lives in the encoding, which Y86-64 has no format for at all.
\begin{tikzpicture}[font=\footnotesize,
  by/.style={draw, minimum width=18mm, minimum height=9mm, inner sep=0pt},
  bit/.style={draw, minimum width=13mm, minimum height=7mm, inner sep=0pt,
              font=\scriptsize}]
  \definecolor{acc}{HTML}{2348F2}
  \node[by] (b0) at (0,0)   {$\mathtt{48}$};
  \node[by] (b1) at (2.0,0) {$\mathtt{8b}$};
  \node[by] (b2) at (4.0,0) {$\mathtt{04}$};
  \node[by, draw=acc, thick, text=acc] (b3) at (6.0,0) {$\mathtt{cb}$};
  \node[anchor=south, font=\scriptsize] at (0,0.55)   {REX};
  \node[anchor=south, font=\scriptsize] at (2.0,0.55) {opcode: load};
  \node[anchor=south, font=\scriptsize] at (4.0,0.9)  {ModRM};
  \node[anchor=south, font=\scriptsize] at (4.0,0.55) {$\mathtt{rm}=\mathtt{100}$};
  \node[anchor=south, font=\scriptsize, text=acc] at (6.0,0.55) {SIB};
  % SIB expansion
  \node[bit] (s0) at (4.1,-1.8) {sc $\mathtt{11}$};
  \node[bit] (s1) at (6.0,-1.8) {idx $\mathtt{001}$};
  \node[bit] (s2) at (7.9,-1.8) {base $\mathtt{011}$};
  \draw[black] (b3.south west) -- (s0.north west);
  \draw[black] (b3.south east) -- (s2.north east);
  \node[anchor=north, font=\scriptsize] at (4.1,-2.25) {scale $= 8$};
  \node[anchor=north, font=\scriptsize] at (6.0,-2.25) {$\mathtt{\%rcx}$};
  \node[anchor=north, font=\scriptsize] at (7.9,-2.25) {$\mathtt{\%rbx}$};
\end{tikzpicture}
$$

## Fixed versus variable encoding

The last format choice is whether all instructions are the **same length** or not,
and the x86/Y86 pair above already shows both answers in miniature. A fixed-length
encoding — say every instruction exactly 4 bytes — makes the hardware that fetches
and decodes instructions simple and fast. A variable-length encoding lets each
instruction be exactly as long as it needs: 1 byte for a `push`, three for an add,
seven or more for an instruction hauling a constant.

The cost of variable length is paid at **fetch**, before decoding proper even
starts. The front end pulls a block of bytes from the instruction cache and must
find where each instruction begins; with variable lengths, instruction $n{+}1$'s
start is a function of instruction $n$'s length, so boundary-finding is serial by
nature, and hardware that decodes several instructions per cycle has to guess
boundaries speculatively and discard the misses.

$$
% caption: What fetch sees in a 16-byte block. Fixed 4-byte instructions start at
% caption: byte 0, 4, 8, 12 — known before decoding, so four decoders start at once.
% caption: With variable lengths (here 1, 3, 7, 5 bytes) each boundary is known only
% caption: after the previous instruction's length is decoded.
\begin{tikzpicture}[font=\scriptsize,
  c/.style={draw, minimum width=6mm, minimum height=6mm, inner sep=0pt}]
  \definecolor{acc}{HTML}{2348F2}
  % fixed row
  \node[anchor=east] at (-0.45,0) {f\/ixed};
  \foreach \i in {0,...,15} \node[c] at (\i*0.6,0) {};
  \foreach \x in {0, 2.4, 4.8, 7.2} \draw[acc, very thick] (\x-0.3,-0.3) -- (\x-0.3,0.3);
  \foreach \x/\n in {0.9/1, 3.3/2, 5.7/3, 8.1/4}
    \node[text=acc] at (\x,0.62) {\n};
  \node[anchor=west, text=acc] at (9.4,0) {all starts known at once};
  % variable row
  \node[anchor=east] at (-0.45,-1.6) {variable};
  \foreach \i in {0,...,15} \node[c] at (\i*0.6,-1.6) {};
  \foreach \x in {0, 0.6, 2.4, 6.6} \draw[acc, very thick] (\x-0.3,-1.9) -- (\x-0.3,-1.3);
  \foreach \x/\n in {0/1, 1.2/2, 4.2/3, 8.4/4}
    \node[text=acc] at (\x,-0.98) {\n};
  \draw[->, black] (0.0,-2.15) -- (0.55,-2.15);
  \draw[->, black] (1.4,-2.15) -- (2.35,-2.15);
  \draw[->, black] (3.2,-2.15) -- (6.55,-2.15);
  \node[anchor=west] at (9.4,-1.6) {each start depends on the last};
\end{tikzpicture}
$$

What variable length buys back is density, and the previous section priced it:
one byte for the hottest instructions, three for a typical add, with long forms
reserved for the rare constant-heavy cases. Fixed length pays four bytes for
everything and wastes bits on a `nop`; in exchange the fetch stage is trivial and
the decoders run fully in parallel. This is precisely the RISC/CISC split of the
[previous lesson](/computer-architecture/instruction-set-architecture/what-an-isa-is)
showing up at byte granularity. The Y86-64 ISA we build in this module takes a
middle road (variable lengths, but only a small set of formats, from 1 to 10
bytes, so an instruction's first byte alone determines its length), and the
[next lesson](/computer-architecture/instruction-set-architecture/addressing-modes)
fills in the remaining piece: once an operand field names a memory location,
exactly how does it compute the address it reaches?

## Operand count in modern ISAs

CS:APP presents the operand-count taxonomy on small classical machines. It maps
cleanly onto ISAs shipping today, and one modern wrinkle is worth knowing.

**The destructive-two-address tax.** x86's 2-address form, where the destination
overwrites a source, is not free: a compiler that needs both `a` and `a + b`
afterward must insert a `mov` to preserve `a` before the add clobbers it. On code
heavy with common subexpressions this is a measurable stream of copy instructions.
RISC ISAs avoid it by being 3-address throughout — `add rd, rs1, rs2` leaves both
sources intact — which is one reason ARM64 and RISC-V code, despite fixed 4-byte
instructions, is not as much larger than x86 as the byte-per-instruction figures
alone suggest: it needs fewer instructions because it wastes none on preservation
copies.

**Why vector x86 went 3-address.** The clearest evidence is inside x86 itself.
When Intel and AMD extended the vector unit, the old 2-address SSE form
`mulps xmm1, xmm2` (which computes `xmm1 = xmm1 * xmm2`, destroying `xmm1`) became a
bottleneck for exactly the copy-insertion reason above, in the innermost loops where
it hurts most. So the **AVX** encoding introduced in 2011 added a new prefix — the
**VEX** prefix — whose entire purpose is to carry a _third_ register field, turning
the instruction non-destructive: `vmulps xmm0, xmm1, xmm2` computes `xmm0 = xmm1 *
xmm2` and leaves both sources alone.[^vex] A CISC architecture, having spent decades
2-address, paid real encoding bytes to buy back the 3-address form for its
performance-critical instructions. The operand-count choice this lesson frames as a
one-time design decision turns out to be one an ISA can revisit — at a price — when
the workload makes the old choice expensive.

> **Takeaway.** An instruction is an opcode plus operand fields. The operand
> _count_ defines a machine class — 3-address names everything, 2-address reuses a
> source as destination, 1-address hides one operand in an accumulator, 0-address
> hides all on a stack — and fewer operands means more, smaller instructions.
> Register machines with a load/store discipline dominate because registers are
> far faster than memory. In real bytes, `addq %rsi,%rdi` is `48 01 f7` in x86-64
> (REX, opcode, ModRM) and `60 67` in Y86-64 (icode:ifun, rA:rB); variable-length
> encoding wins bytes on small constants and pays for it at fetch, where fixed
> length gets instruction boundaries for free.

[^vex]: The **VEX** prefix, introduced with **AVX** (Advanced Vector Extensions,
    2011), adds a non-destructive three-operand form to x86's vector instructions;
    its `vvvv` field names a second source register so the destination need no longer
    overwrite one. See Intel, _Intel 64 and IA-32 Architectures Software Developer's
    Manual_, Vol. 2, §2.3 (VEX encoding). The general point — that a destructive
    two-address form forces preservation copies a three-address form avoids — is the
    operand-count trade of CS:APP §4.1 seen in modern vector code.
