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
| Category | Registers | Count | Width |
|---|---|---|---|
| General Purpose | RAX, RBX, RCX, RDX, RSI, RDI, RSP, RBP, R8-R15 | 16 | 64-bit |
| SSE/AVX | XMM0-XMM15 (YMM, ZMM in AVX-512) | 16 | 128/256/512-bit |
| Segment | CS, DS, SS, ES, FS, GS | 6 | 16-bit selector + hidden |
| Control | CR0, CR2, CR3, CR4, CR8 | 5 | 64-bit |
| Flags | RFLAGS (CF, ZF, SF, OF, etc.) | 1 | 64-bit |
| FP/x87 | ST(0)-ST(7), FP status, control | 8+ | 80-bit |
| MSR | EFER, STAR, LSTAR, FS_BASE, etc. | many | 64-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
- Byte Fetch β CPU fetches bytes starting at the current PC. The decoder receives:
48 89 44 24 08(MOV [RSP+8], RAX). - Prefix Parse β
0x48is a REX prefix (REX.W = 1, 64-bit operand size). Decoder records the prefix and advances. - Opcode Decode β
0x89is the MOV r/m64, r64 opcode. Decoder identifies this as a register-to-memory move. - ModR/M β
0x44decodes as Mod=01 (8-bit displacement), Reg=RAX, R/M=100 (SIB follows). - SIB Byte β
0x24decodes as Scale=1, Index=none, Base=RSP. The effective address is RSP + displacement. - Displacement β
0x08is the 8-bit signed displacement. Final address: [RSP + 8]. - StaticInst Creation β A
MovStaticInst is created with source=RAX, dest=memory(RSP+8). Since this is a simple MOV, no microcode decomposition is needed.
Key Components Reference
| Component | File | Purpose |
|---|---|---|
Decoder | decoder.hh | Variable-length instruction decoder with state machine |
X86ISA::ISA | isa.hh | x86 ISA register state (MSRs, control regs) |
Interrupts | interrupts.hh | Local APIC interrupt controller model |
X86MMU | X86MMU.py | 4-level page table walker + TLB |
X86Fault | faults.hh | x86 exception/fault hierarchy |
EmulEnv | emulenv.hh | Instruction emulation environment (mode, operand size) |
MacroopBase | insts/ | Complex instruction microcode sequences |
cpuid() | cpuid.cc | CPUID response database |
Glossary
| Term | Definition |
|---|---|
| REX Prefix | x86-64 prefix byte enabling 64-bit operands and access to extended registers (R8-R15) |
| ModR/M | Byte encoding the addressing mode, register operand, and memory operand for an instruction |
| SIB | Scale-Index-Base byte for complex memory addressing (base + indexΓscale + disp) |
| Macroop | A complex x86 instruction decomposed into multiple simpler microops |
| Microop | A simple internal operation executed by the CPU pipeline |
| APIC | Advanced Programmable Interrupt Controller β manages hardware interrupts |
| MSR | Model-Specific Register β processor configuration and feature control registers |
| CPUID | Instruction returning processor identification and feature information |