Skip to content
DevMeme
3959 of 7435
When Intel’s opcode docs say a two-byte opcode can be three bytes long
Compilers Post #4306, on Apr 14, 2022 in TG

When Intel’s opcode docs say a two-byte opcode can be three bytes long

Why is this Compilers meme funny?

Level 1: When Two Means Three

Imagine you buy a pair of socks, and the label says, “each pair of socks contains either 2 socks or 3 socks.” Huh? 🤔 You’d probably stop and re-read that, thinking “A pair means two… how can a pair have three socks?” It sounds completely wrong, right? You might stand there confused, with your mind almost showing a little loading spinner – just like the cat in the meme. The joke here is about feeling baffled when instructions or labels use language that seems to defy logic. In the computer manual, Intel called something a “two-byte opcode” but then said it could be three bytes long. That’s like saying “a pair (two) can sometimes be three” – it instantly feels contradictory. The cat with the spinning icon on its forehead is basically us, or anyone, freezing in confusion at that moment. It’s funny because we don’t expect something defined as two to suddenly turn into three. The meme makes you laugh and nod, thinking “Yep, my brain just broke for a second trying to figure that out.” It’s a goofy way to show how reading tech stuff can sometimes feel like a total head-scratcher, just like odd labels in everyday life can make you go “Wait... what?!”

Level 2: Inside an x86 Instruction

Let’s break down what that note is actually saying in plain terms, so it stops sounding like “2 = 3” magic. In x86 assembly language (the human-readable form of the CPU’s machine code), an instruction is represented by one or more bytes in memory. Each instruction has an opcode – essentially a number that tells the CPU what operation to perform (like add, move, square-root, etc.). Now, on simpler architectures, an opcode might always be a fixed size (say one byte or one word). But x86 is a variable-length instruction set, meaning some instructions are short (1 byte), and others are longer, with additional bytes to specify the exact operation or its operands.

Originally, most basic x86 operations had 1-byte opcodes (0x90 means NOP, 0xB8 means MOV into AX, and so on). As Intel introduced more instructions over the years, they needed a way to extend the opcode space beyond what a single byte could encode (only 256 possibilities). Their solution was a special prefix byte 0x0F. If the CPU sees 0x0F, it knows “this isn’t the whole instruction yet – look at the next byte too.” So 0x0F + another byte became an extended opcode. The manual calls those “two-byte opcodes” because the opcode is effectively two bytes long (for example, 0F 85h is a two-byte opcode for the JNE instruction). Up through the 90s, that covered most new instructions.

Then came SSE, SSE2, SSE3 – sets of instructions that let the CPU process multiple pieces of data in parallel (great for graphics, math, etc.). These new instructions often came in groups or variants. For instance, one SSE instruction might do an operation on 4 floats at once, and a variant might do it on 2 doubles, or on a single value. Intel could have invented completely new opcodes for each variant, but there’s a clever shortcut: use the old prefix bytes to distinguish them. In x86, prefix bytes like 0x66, 0xF3, and 0xF2 already existed (for legacy purposes):

  • 0x66 – originally the “operand-size override” prefix (used to switch between 16-bit and 32-bit mode for an instruction).
  • 0xF3 – originally a “REP/REPE” prefix (used to repeat string operations, like copy a string, and also later to indicate certain SSE operations on scalar floats).
  • 0xF2 – originally a “REPNE” prefix (repeat not equal, similar idea, later also repurposed for scalar double operations in SSE2).

For the SSE family, Intel repurposed these prefixes as mandatory parts of certain instructions. That means for some instructions, the presence of (for example) a 0x66 prefix isn’t optional or just modifying something – it’s part of what makes it a different instruction entirely. Let’s say we have an SSE instruction that takes the square root of numbers (like the example above). Without any prefix, the opcode 0F 51 might mean “take square root of four 32-bit floats at once” (that’s an SSE operation called SQRTPS). Add a 66h prefix in front, making it 66 0F 51, and now it means “take square root of two 64-bit floats” (SQRTPD). Add an F3h prefix instead, F3 0F 51, and that means “take square root of a single 32-bit float” (SQRTSS, scalar). So the prefix byte + the two-byte opcode together identify the exact variant. Physically, the machine code for those prefixed versions is 3 bytes long (e.g. 0x66, 0x0F, 0x51).

Now we can decode that confusing sentence. “Two-byte opcodes that are either 2 bytes or 3 bytes in length” is distinguishing two cases:

  • Some instructions have a two-byte opcode and that’s it – just two bytes (like 0F 85h for a jump instruction).
  • Other instructions have a two-byte opcode plus a required prefix byte, making the total length three bytes for the opcode portion (like F3 0F 51h for that SQRTSS instruction).

Intel’s manual is basically saying: “Heads up! Some of these SSE instructions use the extended opcode format (the two-byte 0F escape sequence), but also need a specific prefix in front, effectively making them three bytes worth of opcode.” The phrasing is clunky because they stick to the term “two-byte opcode” to describe the class of instruction (anything that uses the 0x0F escape). So they end up describing a scenario where a “two-byte opcode” actually involves three bytes of code. It sounds like a contradiction if you don’t know their lingo. In truth, it’s just a technical way to say: “Certain SSE instructions require an extra byte before the usual two-byte opcode.”

For a newcomer or a junior developer, it helps to know each part:

  • Opcode: The part of machine code that tells the CPU what operation to do. In x86, this can be 1 byte or multiple bytes.
  • Prefix byte: An extra byte put before the opcode that can modify the instruction. In our context, 0x66, 0xF2, 0xF3 are prefixes that select a particular flavor of an SSE instruction. They’re called “mandatory” here because the instruction definition includes that prefix — you must have it for the instruction to mean what it means.
  • 0x0F: This is a special byte in x86 encoding indicating an extended opcode. Think of it as saying “we need a second opcode byte after this.” Many advanced instructions start with 0x0F.
  • SSE/SSE2/SSE3 instructions: These are groups of CPU instructions for doing things like math on multiple numbers at once (SIMD operations). They were added to Intel CPUs over time (SSE in 1999, SSE2 in 2001, SSE3 a bit later), extending the original instruction set.

So, when the Intel manual snippet says a two-byte opcode can be 3 bytes long, it’s describing exactly the scenario where you have [Prefix][0x0F][Opcode]. The “opcode” part in abstract is two bytes (0F + the opcode byte), but an extra prefix byte in front makes the machine code three bytes total. If you’re new to this, it’s not a dumb question to ask, “How can a ‘two-byte’ opcode be three bytes?” The answer is: Intel is using “two-byte opcode” as a name for a category (all opcodes that use 0x0F), and some of those opcodes also demand a prefix. The documentation is accurate, but it’s like a puzzle until you realize the definitions. Once you know it, it’s logical: an instruction can require one of those specific prefixes as part of its identity.

In summary, this is a quirk of assembly documentation. It’s trying to be precise about a complex encoding scheme. If you read it too literally (as anyone might on first encounter), it’s bewildering – hence the meme. Don’t worry, it’s not you misunderstanding English; it’s the architecture being complicated. The cat with the spinner is a perfect metaphor: sometimes reading low-level docs does feel like your brain is loading… new patches to handle the info. But now that we’ve stepped through it, hopefully the confusion clears: “two-byte opcode” is just Intel’s way of saying extended opcode, and yes, some extended opcodes come with an extra prefix byte attached. Strange but true!

Level 3: Documentation Double-take

From a seasoned developer’s perspective, this meme nails a feeling we know too well: that moment when documentation makes you question your own sanity. The Intel developer manual is famous (infamous?) for being thorough, but sometimes the way it’s written can tie your brain in knots. Reading “two-byte opcodes that are either 2 bytes or 3 bytes in length” feels like reading a riddle. Wait, which is it — two or three? Did I just catch Intel contradicting itself? 🤨 Every low-level programmer who’s combed through these manuals has probably had this reaction, followed by a double-take at the text. The meme’s image of a cat with a loading spinner on its forehead perfectly captures that mental state: your brain goes into a small “does not compute” loop, pausing like a buffering video, trying to reconcile what you just read.

Why is this funny (and a bit traumatic) to us in the know? Because it’s painfully relatable. We’ve been that cat, standing there bewildered after hours of reading dry architecture docs or debugging assembly. The humor comes from the absurd phrasing: it sounds like Intel is saying 2 = 3. For an instant you might think “Is this a typo? Is Intel trolling me?” But then the realization dawns (maybe after scanning the context or that Table 11-3): Intel is actually correct, just describing a very specific quirk of instruction encoding. It’s a classic example of documentation being technically accurate while also mind-bending to parse. In everyday English, calling something a “two-byte opcode” implies it’s two bytes long, so adding “or 3 bytes in length” feels like nonsense. Seasoned engineers recognize this as the kind of thing that happens when jargon and legacy collide. Intel has decades of legacy terminology (like “two-byte opcode” meaning “uses the 0x0F prefix”). They aren’t going to rename that category just because SSE added a twist. So the documentation ends up with a sentence that sounds like a logic puzzle. And indeed, the manual specifically added a highlighted NOTE for this, basically waving a hand to readers: “Heads up, this part is weird, but here’s what it means…” Even so, encountering it without context can trigger a “hold on, run that by me again?” reaction.

In practice, this scenario plays out when developers dive into low-level programming or compiler development. Imagine writing an assembler, disassembler, or a compiler’s code generator. You’re deep in Intel’s manual, implementing support for SSE instructions. You see “two-byte opcode” in one column and then a footnote explaining some of those opcodes need an extra byte. It’s late at night, coffee in hand, and suddenly you’re that cat with a frozen stare. 😵 Shared experience alert: many of us have hit similar WTF moments in docs – whether it’s an RFC, a vendor’s API docs, or the C++ standard – where a sentence just short-circuits our brain. This meme resonates because it condenses that feeling into one image and one hilariously contradictory quote.

There’s also an element of industry inside-joke here. Veteran systems programmers often joke about x86 architecture being a beautiful mess. Over the years, x86 accumulated oddities to maintain backward compatibility (you don’t just throw away 20 years of software). The result is things like “two-byte” opcodes that sometimes have a secret third byte. We chuckle (or groan) because it exemplifies the weird complexity of the platform we deal with. It’s the kind of thing that would be a bug if you saw it in normal writing, but in Intel’s world it’s actually intentional. In short, the meme is funny because it spotlights the dissonance between formal documentation-speak and a programmer’s literal interpretation. The bewildered cat is us, veteran devs included, whenever the docs do a double-take on us. We’ve learned to decipher these things – “Oh, they mean some of those 0x0F opcodes come with a prefix byte” – but our first reaction is still a bemused “...what?” followed by either a hearty laugh or a facepalm. The cat’s mental loading spinner is a gentle reminder that no matter how experienced you are, there are moments when computing documentation will turn your brain into mush for a few seconds. And you just have to laugh, nod, and say, “Yep, that’s Intel for you – even their paradoxes are backward-compatible.”

Level 4: Schrödinger's Opcode

At the deepest technical level, this apparent contradiction comes straight from the arcana of x86 instruction encoding. The Intel Architecture has evolved over decades, and one side effect is seemingly paradoxical phrasing like “two-byte opcodes that are either 2 bytes or 3 bytes in length.” In Intel’s documentation, the term “two-byte opcode” doesn’t mean the instruction is always two bytes long; it refers to an opcode that uses a 0x0F “escape” byte followed by a second opcode byte. Originally, this scheme was introduced to extend the limited 1-byte opcode space of the 8086. Think of 0x0F as a signal saying “the real opcode follows in the next byte.” Historically, any instruction starting with 0Fh (hex 0x0F) was called a “two-byte opcode” because the core operation code comprised two bytes (0F + another).

Now enter SSE, SSE2, SSE3 (the Streaming SIMD Extensions and their sequels) in the early 2000s. These brought new instructions for parallel math, but Intel faced a design challenge: how to encode dozens of new ops without breaking backward compatibility. The solution? Reuse existing prefix bytes (like 0x66, 0xF2, 0xF3) as mandatory parts of certain opcodes. Originally, those bytes were prefixes for things like altering data size or repeating string instructions. But in SSE, they became a required part of the instruction’s identity. In effect, some SSE instructions have an opcode defined by three bytes: e.g. a prefix (F2h, F3h, or 66h), then the 0Fh byte, then the actual opcode byte. The manual’s note is pointing out that within the category of “two-byte opcodes,” some have this extra prefix byte — hence physically totaling three bytes in the machine code. It’s a bit like an opcode-with-a-prefix masquerading as a longer opcode. The phrase reads like a Zen koan or a physics paradox: a thing defined as two bytes that in reality occupies three bytes. At first glance, it feels like a Schrödinger’s opcode — both two-byte and three-byte until observed! But once you grok Intel’s terminology, the meaning clicks.

Why did Intel do this? Fundamentally, it’s about extending the instruction set without redesigning it from scratch. They had only so many single-byte opcodes (256 possible values), extended them with the 0x0F escape (another 256, for two-byte combos), and then stretched further by repurposing prefixes to squeeze in variations. It’s a testament to both clever design and cumulative complexity. Each mandatory prefix + 0F sequence creates a distinct instruction that couldn’t be expressed with just a two-byte opcode. The trade-off is clarity: the manual must distinguish between a “base” two-byte opcode and an effective three-byte opcode with prefix. It’s technically precise but linguistically confusing. To see how this works, consider a real example of SSE instructions encoding the square root operation in different flavors:

; Machine code bytes -> Assembly instruction (and what it does)
0F 51                ; SQRTPS XMM, XMM/M128   ; sqrt of 4 packed single-precision floats
66 0F 51             ; SQRTPD XMM, XMM/M128   ; sqrt of 2 packed double-precision floats (0x66 prefix)
F3 0F 51             ; SQRTSS XMM, XMM/M32    ; sqrt of 1 scalar single-precision float (0xF3 prefix)
F2 0F 51             ; SQRTSD XMM, XMM/M64    ; sqrt of 1 scalar double-precision float (0xF2 prefix)

Here 0F 51 by itself is a two-byte opcode for computing square roots on four 32-bit floats (PS = Packed Singles). But add a 66h prefix in front, and now 66 0F 51 means do it on two 64-bit floats (PD = Packed Doubles). Add F3h or F2h instead, and the same base opcode 0F 51 now means take the sqrt of just one value (SS = Scalar Single, SD = Scalar Double). These prefix bytes are not optional in these cases; they’re an integral part of the instruction code – thus Intel calls them mandatory prefixes. So each of those is conceptually a “two-byte opcode” (because of the 0F 51 core) but with a required extra byte, making the total opcode length three bytes. In other words, the category is named after the base opcode length (two bytes), even though a specific instruction in that category might have an extra prefix byte attached. If that twists your brain at first, you’re not alone – the Intel manual literally has a NOTE about it! The reference to “See Table 11-3” hints at a big table mapping these prefixes to instructions, underscoring that this isn’t an error but a feature of the encoding scheme. It’s the kind of quirk that emerges from years of instruction set evolution and the insistence on backward compatibility. Much like reading an old manuscript with footnotes, you have to understand the definition of terms in Intel’s context: “two-byte opcode” is a defined category, not a strict byte count. Once you grasp that, the paradox resolves – but the wording still can make any engineer do a double-take with a buffering brain, just like our feline friend in the meme.

Description

A photo meme shows a bewildered house cat standing on a wooden floor with a gray "loading" spinner GIF centered on its forehead, implying mental overload. Above the cat, a cropped page from the Intel Architecture Software Developer’s Manual is pasted. The blue-titled section reads “NOTE” followed by: “Some SSE/SSE2/SSE3 instructions have two-byte opcodes that are either 2 bytes or 3 bytes in length. Two-byte opcodes that are 3 bytes in length consist of: a mandatory prefix (F2H, F3H, or 66H), 0FH, and an opcode byte. See Table 11-3.” The specific phrase “two-byte opcodes that are either 2 bytes or 3 bytes in length” is additionally highlighted in a white callout bar, emphasizing the contradiction. The juxtaposition humorously captures a low-level programmer’s confusion when parsing x86 instruction encoding documentation, likening the reader’s brain to a buffering spinner

Comments

7
Anonymous ★ Top Pick x86 docs: “Some two-byte opcodes are three bytes.” Nice to see the ISA using the same estimation model as our product roadmap - just add a prefix and call it scope creep
  1. Anonymous ★ Top Pick

    x86 docs: “Some two-byte opcodes are three bytes.” Nice to see the ISA using the same estimation model as our product roadmap - just add a prefix and call it scope creep

  2. Anonymous

    Reading Intel's opcode documentation is like being told your two-week sprint is actually three weeks, but only if you add the mandatory F2H prefix and check Table 11-3 for exceptions

  3. Anonymous

    x86 encoding is the original schemaless format: variable length, mandatory optional prefixes, and a spec that gaslights you - and somehow it's still backward compatible with 1978

  4. Anonymous

    When Intel's documentation says 'two-byte opcodes' can be 2 OR 3 bytes, you realize the real variable-length encoding was the friends we made along the way - and also why your disassembler keeps segfaulting. At least the cat's loading spinner is more honest about its confusion than the x86 instruction manual

  5. Anonymous

    Two-byte opcodes that are three bytes long - x86: technically correct, linguistically hostile. 66/F2/F3 + 0F + opcode, and my decoder grows a loading spinner

  6. Anonymous

    x86 SSE: where three prefixes solve the 'ambiguous vector op' problem by inventing four decode paths

  7. Anonymous

    In x86, “two-byte opcode” is a category, not a constant - add F2/F3/66 and your decoder FSM grows a state while your brain spins in busy-wait

Use J and K for navigation