x86 β€” x86-64 ISA Implementation

Full x86-64 instruction set support with variable-length decoding, microcode engine, CPUID emulation, segmentation, and Local APIC interrupt controller.

📚 What is this & why does it exist?

πŸ’‘ Think of it like…

The x86 ISA implementation is like a vintage clock repair shop β€” each instruction is a complex mechanism with variable-size gears (prefix bytes, opcodes, ModR/M, SIB, displacement, immediate). The decoder acts as a master watchmaker who first identifies each gear, then decomposes complex movements into Atomic and Timing Simple CPUs β€” gem5 Wiki" href="../../cpu_doc/simple/index.html">simple micro-operations that the CPU's simpler internal engine can execute one tick at a time.

Architecture Overview

The x86 ISA is one of gem5's most complex architecture implementations due to the CISC nature of x86-64. Key challenges include variable-length instruction encoding (1-15 bytes), the microcode decomposition of complex instructions, legacy segmentation support, and the full Local APIC interrupt controller model.

graph TB
    subgraph "Decoder Pipeline"
      Fetch["Byte Stream"]
      Prefix["Prefix Parsing\n(REX, VEX, EVEX)"]
      Opcode["Opcode Decode"]
      ModRM["ModR/M + SIB"]
      Micro["Microcode ROM\n(complex insts)"]
      SI["StaticInst"]
    end

    subgraph "ISA State"
      Regs["Register File\n(16 GPRs, XMM, flags)"]
      Segs["Segment Registers\n(CS, DS, SS, ES, FS, GS)"]
      CPUID_["CPUID Database"]
      MSRs["Model-Specific Regs"]
    end

    subgraph "System"
      APIC["Local APIC\n(interrupt controller)"]
      MMU["x86 MMU\n(4-level page tables)"]
      TLB["x86 TLB"]
    end

    Fetch --> Prefix --> Opcode --> ModRM --> SI
    Opcode -->|"complex"| Micro --> SI
    SI --> Regs
    APIC --> Regs
    MMU --> TLB
    

Instruction Decoder

The x86 decoder is a state-machine-based parser that handles the full complexity of x86 instruction encoding. It processes bytes sequentially through states: prefix parsing, opcode identification, ModR/M/SIB decoding, and displacement/immediate extraction.

namespace X86ISA {

class Decoder : public InstDecoder
{
  public:
    // Decoder states
    enum State {
        ResetState,
        PrefixState,
        TwoByteOpcodeState,
        ThreeByte0F38State,
        ThreeByte0F3AState,
        VexTwoByteState,
        VexThreeByteState,
        OneByteOpcodeState,
        ModRMState,
        SIBState,
        DisplacementState,
        ImmediateState,
        // ... additional states
    };

    void moreBytes(const PCStateBase &pc, Addr fetchPC) override;
    bool instReady() const override;
    StaticInstPtr decode(PCStateBase &nextPC) override;
    void reset() override;

  private:
    State state;
    ExtMachInst emi;        // Extended machine instruction
    uint8_t prefixes;        // Accumulated prefix bytes
    uint8_t rexPrefix;       // REX prefix for 64-bit mode
    int immediateSize;
    int displacementSize;

    // Microcode ROM for complex instructions
    StaticInstPtr decodeInst(ExtMachInst mach_inst);
};

Microcode Engine

Complex x86 instructions (e.g., REP MOVSB, CPUID, XSAVE) are decomposed into sequences of micro-operations. The microcode ROM maps each macroop to a sequence of microops that the CPU pipeline processes individually.

// A macroop decomposes into microops
class MacroopBase : public StaticInst
{
  protected:
    std::vector<StaticInstPtr> microops;
    uint32_t numMicroops;

  public:
    StaticInstPtr fetchMicroop(MicroPC upc) const override {
        return microops[upc];
    }
    bool isMacroop() const override { return true; }
    bool isMicroop() const override { return false; }
};

// Individual microop
class MicroopBase : public X86ISA::X86StaticInst
{
  public:
    bool isMicroop() const override { return true; }
    bool isLastMicroop() const override;
    bool isFirstMicroop() const override;
};

Register File

CategoryRegistersCountWidth
General PurposeRAX, RBX, RCX, RDX, RSI, RDI, RSP, RBP, R8-R151664-bit
SSE/AVXXMM0-XMM15 (YMM, ZMM in AVX-512)16128/256/512-bit
SegmentCS, DS, SS, ES, FS, GS616-bit selector + hidden
ControlCR0, CR2, CR3, CR4, CR8564-bit
FlagsRFLAGS (CF, ZF, SF, OF, etc.)164-bit
FP/x87ST(0)-ST(7), FP status, control8+80-bit
MSREFER, STAR, LSTAR, FS_BASE, etc.many64-bit

Fault Handling

x86 defines a complex fault hierarchy mapping to processor exceptions. Each fault carries a vector number and optional error code pushed onto the stack during exception delivery.

namespace X86ISA {

class X86Fault : public FaultBase
{
  protected:
    const char *faultName;
    uint8_t vector;       // IDT vector number
    bool errorCode;       // Whether error code is pushed

  public:
    void invoke(ThreadContext *tc, const StaticInstPtr &inst) override;
};

// Specific faults
class DivideError : public X86Fault { /* vector 0 */ };
class DebugException : public X86Fault { /* vector 1 */ };
class Breakpoint : public X86Fault { /* vector 3 */ };
class InvalidOpcode : public X86Fault { /* vector 6 */ };
class PageFault : public X86Fault {
    Addr addr;
    uint32_t errorCode;  // P, W/R, U/S, RSVD, I/D bits
};
class GeneralProtection : public X86Fault { /* vector 13 */ };

} // namespace X86ISA

Local APIC

The Local APIC (Advanced Programmable Interrupt Controller) model handles interrupt delivery, IPI (Inter-Processor Interrupt) routing, timer functionality, and interrupt prioritization.

class Interrupts : public BaseInterrupts
{
  public:
    // APIC registers
    uint32_t regs[NUM_APIC_REGS];

    // Interrupt request/service registers (256 bits each)
    ApicRegister IRR;  // Interrupt Request Register
    ApicRegister ISR;  // In-Service Register

    // Timer
    uint32_t initialCount;
    uint32_t currentCount;
    Tick timerPeriod;

    // Check for pending interrupts
    bool checkInterrupts() const override;
    Fault getInterrupt() override;
    void updateIntrInfo() override;

    // IPI interface
    void sendIPI(uint8_t destId, uint8_t vector, uint8_t deliveryMode);

    // MMIO interface for APIC register access
    Tick read(PacketPtr pkt) override;
    Tick write(PacketPtr pkt) override;
};

CPUID Emulation

gem5 emulates CPUID responses to correctly identify the simulated processor's capabilities to guest software. The CPUID database returns appropriate feature flags, Cache Hierarchy | gem5 Wiki" href="../../mem_doc/cache/index.html">cache topology, and vendor information.

// CPUID leaf 0: Vendor string
// Returns "GenuineIntel" or "AuthenticAMD" based on config

// CPUID leaf 1: Feature flags
// Reports supported extensions: SSE, SSE2, SSE3, SSSE3,
// SSE4.1, SSE4.2, POPCNT, CLFLUSH, etc.

// CPUID leaf 4: Cache topology
// Reports L1I, L1D, L2, L3 cache parameters

// CPUID leaf 0x80000000+: Extended features
// Reports long mode support, NX bit, etc.

πŸ” Narrated Walkthrough: Decoding a MOV Instruction

  1. Byte Fetch β€” CPU fetches bytes starting at the current PC. The decoder receives: 48 89 44 24 08 (MOV [RSP+8], RAX).
  2. Prefix Parse β€” 0x48 is a REX prefix (REX.W = 1, 64-bit operand size). Decoder records the prefix and advances.
  3. Opcode Decode β€” 0x89 is the MOV r/m64, r64 opcode. Decoder identifies this as a register-to-memory move.
  4. ModR/M β€” 0x44 decodes as Mod=01 (8-bit displacement), Reg=RAX, R/M=100 (SIB follows).
  5. SIB Byte β€” 0x24 decodes as Scale=1, Index=none, Base=RSP. The effective address is RSP + displacement.
  6. Displacement β€” 0x08 is the 8-bit signed displacement. Final address: [RSP + 8].
  7. StaticInst Creation β€” A Mov StaticInst is created with source=RAX, dest=memory(RSP+8). Since this is a simple MOV, no microcode decomposition is needed.

Key Components Reference

ComponentFilePurpose
Decoderdecoder.hhVariable-length instruction decoder with state machine
X86ISA::ISAisa.hhx86 ISA register state (MSRs, control regs)
Interruptsinterrupts.hhLocal APIC interrupt controller model
X86MMUX86MMU.py4-level page table walker + TLB
X86Faultfaults.hhx86 exception/fault hierarchy
EmulEnvemulenv.hhInstruction emulation environment (mode, operand size)
MacroopBaseinsts/Complex instruction microcode sequences
cpuid()cpuid.ccCPUID response database

Glossary

TermDefinition
REX Prefixx86-64 prefix byte enabling 64-bit operands and access to extended registers (R8-R15)
ModR/MByte encoding the addressing mode, register operand, and memory operand for an instruction
SIBScale-Index-Base byte for complex memory addressing (base + indexΓ—scale + disp)
MacroopA complex x86 instruction decomposed into multiple simpler microops
MicroopA simple internal operation executed by the CPU pipeline
APICAdvanced Programmable Interrupt Controller β€” manages hardware interrupts
MSRModel-Specific Register β€” processor configuration and feature control registers
CPUIDInstruction returning processor identification and feature information