Quellcode durchsuchen

LibX86: Apply aggressive inlining to Instruction decoding functions

These functions really benefit from being inlined together instead
of being separated.

This yields roughly a ~2x speedup.
Andreas Kling vor 5 Jahren
Ursprung
Commit
868db2313f
2 geänderte Dateien mit 411 neuen und 404 gelöschten Zeilen
  1. 5 400
      Libraries/LibX86/Instruction.cpp
  2. 406 4
      Libraries/LibX86/Instruction.h

+ 5 - 400
Libraries/LibX86/Instruction.cpp

@@ -35,148 +35,10 @@
 
 namespace X86 {
 
-enum IsLockPrefixAllowed {
-    LockPrefixNotAllowed = 0,
-    LockPrefixAllowed
-};
-
-enum InstructionFormat {
-    InvalidFormat,
-    MultibyteWithSlash,
-    MultibyteWithSubopcode,
-    InstructionPrefix,
-
-    __BeginFormatsWithRMByte,
-    OP_RM16_reg16,
-    OP_reg8_RM8,
-    OP_reg16_RM16,
-    OP_RM16_seg,
-    OP_RM32_seg,
-    OP_RM8_imm8,
-    OP_RM16_imm16,
-    OP_RM16_imm8,
-    OP_RM32_imm8,
-    OP_RM8,
-    OP_RM16,
-    OP_RM32,
-    OP_RM8_reg8,
-    OP_RM32_reg32,
-    OP_reg32_RM32,
-    OP_RM32_imm32,
-    OP_reg16_RM16_imm8,
-    OP_reg32_RM32_imm8,
-    OP_reg16_RM16_imm16,
-    OP_reg32_RM32_imm32,
-    OP_reg16_mem16,
-    OP_reg32_mem32,
-    OP_seg_RM16,
-    OP_seg_RM32,
-    OP_RM8_1,
-    OP_RM16_1,
-    OP_RM32_1,
-    OP_FAR_mem16,
-    OP_FAR_mem32,
-    OP_RM8_CL,
-    OP_RM16_CL,
-    OP_RM32_CL,
-    OP_reg32_CR,
-    OP_CR_reg32,
-    OP_reg32_DR,
-    OP_DR_reg32,
-    OP_reg16_RM8,
-    OP_reg32_RM8,
-    OP_reg32_RM16,
-    OP_RM16_reg16_imm8,
-    OP_RM32_reg32_imm8,
-    OP_RM16_reg16_CL,
-    OP_RM32_reg32_CL,
-    OP_mm1_mm2m64,
-    OP_mm1m64_mm2,
-    __EndFormatsWithRMByte,
-
-    OP_reg32_imm32,
-    OP_AL_imm8,
-    OP_AX_imm16,
-    OP_EAX_imm32,
-    OP_CS,
-    OP_DS,
-    OP_ES,
-    OP_SS,
-    OP_FS,
-    OP_GS,
-    OP,
-    OP_reg16,
-    OP_imm16,
-    OP_relimm16,
-    OP_relimm32,
-    OP_imm8,
-    OP_imm16_imm16,
-    OP_imm16_imm32,
-    OP_AX_reg16,
-    OP_EAX_reg32,
-    OP_AL_moff8,
-    OP_AX_moff16,
-    OP_EAX_moff32,
-    OP_moff8_AL,
-    OP_moff16_AX,
-    OP_moff32_EAX,
-    OP_reg8_imm8,
-    OP_reg16_imm16,
-    OP_3,
-    OP_AX_imm8,
-    OP_EAX_imm8,
-    OP_short_imm8,
-    OP_AL_DX,
-    OP_AX_DX,
-    OP_EAX_DX,
-    OP_DX_AL,
-    OP_DX_AX,
-    OP_DX_EAX,
-    OP_imm8_AL,
-    OP_imm8_AX,
-    OP_imm8_EAX,
-    OP_reg8_CL,
-
-    OP_reg32,
-    OP_imm32,
-    OP_imm16_imm8,
-
-    OP_NEAR_imm,
-};
-
-static const unsigned CurrentAddressSize = 0xB33FBABE;
-
-struct InstructionDescriptor {
-    InstructionHandler handler { nullptr };
-    bool opcode_has_register_index { false };
-    const char* mnemonic { nullptr };
-    InstructionFormat format { InvalidFormat };
-    bool has_rm { false };
-    unsigned imm1_bytes { 0 };
-    unsigned imm2_bytes { 0 };
-    InstructionDescriptor* slashes { nullptr };
-
-    unsigned imm1_bytes_for_address_size(bool a32)
-    {
-        if (imm1_bytes == CurrentAddressSize)
-            return a32 ? 4 : 2;
-        return imm1_bytes;
-    }
-
-    unsigned imm2_bytes_for_address_size(bool a32)
-    {
-        if (imm2_bytes == CurrentAddressSize)
-            return a32 ? 4 : 2;
-        return imm2_bytes;
-    }
-
-    IsLockPrefixAllowed lock_prefix_allowed { LockPrefixNotAllowed };
-};
-
-static InstructionDescriptor s_table16[256];
-static InstructionDescriptor s_table32[256];
-static InstructionDescriptor s_0f_table16[256];
-static InstructionDescriptor s_0f_table32[256];
+InstructionDescriptor s_table16[256];
+InstructionDescriptor s_table32[256];
+InstructionDescriptor s_0f_table16[256];
+InstructionDescriptor s_0f_table32[256];
 
 static bool opcode_has_register_index(u8 op)
 {
@@ -395,7 +257,7 @@ static void build_0f_slash(u8 op, u8 slash, const char* mnemonic, InstructionFor
     build_slash(s_0f_table32, op, slash, mnemonic, format, impl, lock_prefix_allowed);
 }
 
-void build_opcode_tables_if_needed()
+void Instruction::build_opcode_tables_if_needed()
 {
     static bool has_built_tables = false;
     if (has_built_tables)
@@ -848,157 +710,6 @@ void build_opcode_tables_if_needed()
     has_built_tables = true;
 }
 
-Instruction Instruction::from_stream(InstructionStream& stream, bool o32, bool a32)
-{
-    build_opcode_tables_if_needed();
-    return Instruction(stream, o32, a32);
-}
-
-unsigned Instruction::length() const
-{
-    unsigned len = 1;
-    if (m_has_sub_op)
-        ++len;
-    if (m_has_rm) {
-        ++len;
-        if (m_modrm.m_has_sib)
-            ++len;
-        len += m_modrm.m_displacement_bytes;
-    }
-    len += m_imm1_bytes;
-    len += m_imm2_bytes;
-    len += m_prefix_bytes;
-    return len;
-}
-
-static Optional<SegmentRegister> to_segment_prefix(u8 op)
-{
-    switch (op) {
-    case 0x26:
-        return SegmentRegister::ES;
-    case 0x2e:
-        return SegmentRegister::CS;
-    case 0x36:
-        return SegmentRegister::SS;
-    case 0x3e:
-        return SegmentRegister::DS;
-    case 0x64:
-        return SegmentRegister::FS;
-    case 0x65:
-        return SegmentRegister::GS;
-    default:
-        return {};
-    }
-}
-
-Instruction::Instruction(InstructionStream& stream, bool o32, bool a32)
-    : m_a32(a32)
-    , m_o32(o32)
-{
-    for (;; ++m_prefix_bytes) {
-        u8 opbyte = stream.read8();
-        if (opbyte == Prefix::OperandSizeOverride) {
-            m_o32 = !o32;
-            m_has_operand_size_override_prefix = true;
-            continue;
-        }
-        if (opbyte == Prefix::AddressSizeOverride) {
-            m_a32 = !a32;
-            m_has_address_size_override_prefix = true;
-            continue;
-        }
-        if (opbyte == Prefix::REPZ || opbyte == Prefix::REPNZ) {
-            m_rep_prefix = opbyte;
-            continue;
-        }
-        if (opbyte == Prefix::LOCK) {
-            m_has_lock_prefix = true;
-            continue;
-        }
-        auto segment_prefix = to_segment_prefix(opbyte);
-        if (segment_prefix.has_value()) {
-            m_segment_prefix = segment_prefix;
-            continue;
-        }
-        m_op = opbyte;
-        break;
-    }
-
-    if (m_op == 0x0F) {
-        m_has_sub_op = true;
-        m_sub_op = stream.read8();
-        m_descriptor = m_o32 ? &s_0f_table32[m_sub_op] : &s_0f_table16[m_sub_op];
-    } else {
-        m_descriptor = m_o32 ? &s_table32[m_op] : &s_table16[m_op];
-    }
-
-    m_has_rm = m_descriptor->has_rm;
-    if (m_has_rm) {
-        // Consume ModR/M (may include SIB and displacement.)
-        m_modrm.decode(stream, m_a32);
-        m_register_index = (m_modrm.m_rm >> 3) & 7;
-    } else {
-        if (m_has_sub_op)
-            m_register_index = m_sub_op & 7;
-        else
-            m_register_index = m_op & 7;
-    }
-
-    bool hasSlash = m_descriptor->format == MultibyteWithSlash;
-
-    if (hasSlash) {
-        m_descriptor = &m_descriptor->slashes[slash()];
-    }
-
-    if (!m_descriptor->mnemonic) {
-        if (m_has_sub_op) {
-            if (hasSlash)
-                fprintf(stderr, "Instruction %02X %02X /%u not understood\n", m_op, m_sub_op, slash());
-            else
-                fprintf(stderr, "Instruction %02X %02X not understood\n", m_op, m_sub_op);
-        } else {
-            if (hasSlash)
-                fprintf(stderr, "Instruction %02X /%u not understood\n", m_op, slash());
-            else
-                fprintf(stderr, "Instruction %02X not understood\n", m_op);
-        }
-        m_descriptor = nullptr;
-        return;
-    }
-
-    m_imm1_bytes = m_descriptor->imm1_bytes_for_address_size(m_a32);
-    m_imm2_bytes = m_descriptor->imm2_bytes_for_address_size(m_a32);
-
-    // Consume immediates if present.
-    if (m_imm2_bytes)
-        m_imm2 = stream.read(m_imm2_bytes);
-    if (m_imm1_bytes)
-        m_imm1 = stream.read(m_imm1_bytes);
-
-    m_handler = m_descriptor->handler;
-
-#ifdef DISALLOW_INVALID_LOCK_PREFIX
-    if (m_has_lock_prefix && !m_descriptor->lock_prefix_allowed) {
-        fprintf(stderr, "Instruction not allowed with LOCK prefix, this will raise #UD\n");
-        m_descriptor = nullptr;
-    }
-#endif
-}
-
-u32 InstructionStream::read(unsigned count)
-{
-    switch (count) {
-    case 1:
-        return read8();
-    case 2:
-        return read16();
-    case 4:
-        return read32();
-    }
-    ASSERT_NOT_REACHED();
-    return 0;
-}
-
 static const char* register_name(RegisterIndex8);
 static const char* register_name(RegisterIndex16);
 static const char* register_name(RegisterIndex32);
@@ -1806,110 +1517,4 @@ const char* register_name(MMXRegisterIndex register_index)
     return names[register_index & 7];
 }
 
-void MemoryOrRegisterReference::decode(InstructionStream& stream, bool a32)
-{
-    m_a32 = a32;
-    m_rm = stream.read8();
-
-    if (m_a32) {
-        decode32(stream);
-        switch (m_displacement_bytes) {
-        case 0:
-            break;
-        case 1:
-            m_displacement32 = sign_extended_to<u32>(stream.read8());
-            break;
-        case 4:
-            m_displacement32 = stream.read32();
-            break;
-        default:
-            ASSERT_NOT_REACHED();
-            break;
-        }
-    } else {
-        decode16(stream);
-        switch (m_displacement_bytes) {
-        case 0:
-            break;
-        case 1:
-            m_displacement16 = sign_extended_to<u16>(stream.read8());
-            break;
-        case 2:
-            m_displacement16 = stream.read16();
-            break;
-        default:
-            ASSERT_NOT_REACHED();
-            break;
-        }
-    }
-}
-
-void MemoryOrRegisterReference::decode16(InstructionStream&)
-{
-    ASSERT(!m_a32);
-
-    switch (m_rm & 0xc0) {
-    case 0:
-        if ((m_rm & 0x07) == 6)
-            m_displacement_bytes = 2;
-        else
-            ASSERT(m_displacement_bytes == 0);
-        break;
-    case 0x40:
-        m_displacement_bytes = 1;
-        break;
-    case 0x80:
-        m_displacement_bytes = 2;
-        break;
-    case 0xc0:
-        m_register_index = m_rm & 7;
-        break;
-    }
-}
-
-void MemoryOrRegisterReference::decode32(InstructionStream& stream)
-{
-    ASSERT(m_a32);
-
-    switch (m_rm & 0xc0) {
-    case 0:
-        if ((m_rm & 0x07) == 5)
-            m_displacement_bytes = 4;
-        break;
-    case 0x40:
-        m_displacement_bytes = 1;
-        break;
-    case 0x80:
-        m_displacement_bytes = 4;
-        break;
-    case 0xc0:
-        m_register_index = m_rm & 7;
-        return;
-    }
-
-    m_has_sib = (m_rm & 0x07) == 4;
-    if (m_has_sib) {
-        m_sib = stream.read8();
-        if ((m_sib & 0x07) == 5) {
-            switch ((m_rm >> 6) & 0x03) {
-            case 0:
-                ASSERT(!m_displacement_bytes || m_displacement_bytes == 4);
-                m_displacement_bytes = 4;
-                break;
-            case 1:
-                ASSERT(!m_displacement_bytes || m_displacement_bytes == 1);
-                m_displacement_bytes = 1;
-                break;
-            case 2:
-                ASSERT(!m_displacement_bytes || m_displacement_bytes == 4);
-                m_displacement_bytes = 4;
-                break;
-            default:
-                ASSERT_NOT_REACHED();
-                break;
-            }
-        }
-    }
-}
-
 }

+ 406 - 4
Libraries/LibX86/Instruction.h

@@ -30,11 +30,13 @@
 #include <AK/StdLibExtras.h>
 #include <AK/String.h>
 #include <AK/Types.h>
+#include <stdio.h>
 
 namespace X86 {
 
 class Instruction;
-struct InstructionDescriptor;
+class Interpreter;
+typedef void (Interpreter::*InstructionHandler)(const Instruction&);
 
 class SymbolProvider {
 public:
@@ -56,6 +58,149 @@ inline constexpr T sign_extended_to(U value)
     return (TypeTrivia<T>::mask & ~TypeTrivia<U>::mask) | value;
 }
 
+enum IsLockPrefixAllowed {
+    LockPrefixNotAllowed = 0,
+    LockPrefixAllowed
+};
+
+enum InstructionFormat {
+    InvalidFormat,
+    MultibyteWithSlash,
+    MultibyteWithSubopcode,
+    InstructionPrefix,
+
+    __BeginFormatsWithRMByte,
+    OP_RM16_reg16,
+    OP_reg8_RM8,
+    OP_reg16_RM16,
+    OP_RM16_seg,
+    OP_RM32_seg,
+    OP_RM8_imm8,
+    OP_RM16_imm16,
+    OP_RM16_imm8,
+    OP_RM32_imm8,
+    OP_RM8,
+    OP_RM16,
+    OP_RM32,
+    OP_RM8_reg8,
+    OP_RM32_reg32,
+    OP_reg32_RM32,
+    OP_RM32_imm32,
+    OP_reg16_RM16_imm8,
+    OP_reg32_RM32_imm8,
+    OP_reg16_RM16_imm16,
+    OP_reg32_RM32_imm32,
+    OP_reg16_mem16,
+    OP_reg32_mem32,
+    OP_seg_RM16,
+    OP_seg_RM32,
+    OP_RM8_1,
+    OP_RM16_1,
+    OP_RM32_1,
+    OP_FAR_mem16,
+    OP_FAR_mem32,
+    OP_RM8_CL,
+    OP_RM16_CL,
+    OP_RM32_CL,
+    OP_reg32_CR,
+    OP_CR_reg32,
+    OP_reg32_DR,
+    OP_DR_reg32,
+    OP_reg16_RM8,
+    OP_reg32_RM8,
+    OP_reg32_RM16,
+    OP_RM16_reg16_imm8,
+    OP_RM32_reg32_imm8,
+    OP_RM16_reg16_CL,
+    OP_RM32_reg32_CL,
+    OP_mm1_mm2m64,
+    OP_mm1m64_mm2,
+    __EndFormatsWithRMByte,
+
+    OP_reg32_imm32,
+    OP_AL_imm8,
+    OP_AX_imm16,
+    OP_EAX_imm32,
+    OP_CS,
+    OP_DS,
+    OP_ES,
+    OP_SS,
+    OP_FS,
+    OP_GS,
+    OP,
+    OP_reg16,
+    OP_imm16,
+    OP_relimm16,
+    OP_relimm32,
+    OP_imm8,
+    OP_imm16_imm16,
+    OP_imm16_imm32,
+    OP_AX_reg16,
+    OP_EAX_reg32,
+    OP_AL_moff8,
+    OP_AX_moff16,
+    OP_EAX_moff32,
+    OP_moff8_AL,
+    OP_moff16_AX,
+    OP_moff32_EAX,
+    OP_reg8_imm8,
+    OP_reg16_imm16,
+    OP_3,
+    OP_AX_imm8,
+    OP_EAX_imm8,
+    OP_short_imm8,
+    OP_AL_DX,
+    OP_AX_DX,
+    OP_EAX_DX,
+    OP_DX_AL,
+    OP_DX_AX,
+    OP_DX_EAX,
+    OP_imm8_AL,
+    OP_imm8_AX,
+    OP_imm8_EAX,
+    OP_reg8_CL,
+
+    OP_reg32,
+    OP_imm32,
+    OP_imm16_imm8,
+
+    OP_NEAR_imm,
+};
+
+static const unsigned CurrentAddressSize = 0xB33FBABE;
+
+struct InstructionDescriptor {
+    InstructionHandler handler { nullptr };
+    bool opcode_has_register_index { false };
+    const char* mnemonic { nullptr };
+    InstructionFormat format { InvalidFormat };
+    bool has_rm { false };
+    unsigned imm1_bytes { 0 };
+    unsigned imm2_bytes { 0 };
+    InstructionDescriptor* slashes { nullptr };
+
+    unsigned imm1_bytes_for_address_size(bool a32)
+    {
+        if (imm1_bytes == CurrentAddressSize)
+            return a32 ? 4 : 2;
+        return imm1_bytes;
+    }
+
+    unsigned imm2_bytes_for_address_size(bool a32)
+    {
+        if (imm2_bytes == CurrentAddressSize)
+            return a32 ? 4 : 2;
+        return imm2_bytes;
+    }
+
+    IsLockPrefixAllowed lock_prefix_allowed { LockPrefixNotAllowed };
+};
+
+extern InstructionDescriptor s_table16[256];
+extern InstructionDescriptor s_table32[256];
+extern InstructionDescriptor s_0f_table16[256];
+extern InstructionDescriptor s_0f_table32[256];
+
 struct Prefix {
     enum Op {
         OperandSizeOverride = 0x66,
@@ -266,9 +411,6 @@ private:
     bool m_has_sib { false };
 };
 
-class Interpreter;
-typedef void (Interpreter::*InstructionHandler)(const Instruction&);
-
 class Instruction {
 public:
     static Instruction from_stream(InstructionStream&, bool o32, bool a32);
@@ -363,6 +505,8 @@ public:
 private:
     Instruction(InstructionStream&, bool o32, bool a32);
 
+    static void build_opcode_tables_if_needed();
+
     String to_string_internal(u32 origin, const SymbolProvider*, bool x32) const;
 
     const char* reg8_name() const;
@@ -642,4 +786,262 @@ ALWAYS_INLINE u32 MemoryOrRegisterReference::read32(CPU& cpu, const Instruction&
     return cpu.read_memory32(address);
 }
 
+ALWAYS_INLINE Instruction Instruction::from_stream(InstructionStream& stream, bool o32, bool a32)
+{
+    build_opcode_tables_if_needed();
+    return Instruction(stream, o32, a32);
+}
+
+ALWAYS_INLINE unsigned Instruction::length() const
+{
+    unsigned len = 1;
+    if (m_has_sub_op)
+        ++len;
+    if (m_has_rm) {
+        ++len;
+        if (m_modrm.m_has_sib)
+            ++len;
+        len += m_modrm.m_displacement_bytes;
+    }
+    len += m_imm1_bytes;
+    len += m_imm2_bytes;
+    len += m_prefix_bytes;
+    return len;
+}
+
+ALWAYS_INLINE static Optional<SegmentRegister> to_segment_prefix(u8 op)
+{
+    switch (op) {
+    case 0x26:
+        return SegmentRegister::ES;
+    case 0x2e:
+        return SegmentRegister::CS;
+    case 0x36:
+        return SegmentRegister::SS;
+    case 0x3e:
+        return SegmentRegister::DS;
+    case 0x64:
+        return SegmentRegister::FS;
+    case 0x65:
+        return SegmentRegister::GS;
+    default:
+        return {};
+    }
+}
+
+ALWAYS_INLINE Instruction::Instruction(InstructionStream& stream, bool o32, bool a32)
+    : m_a32(a32)
+    , m_o32(o32)
+{
+    for (;; ++m_prefix_bytes) {
+        u8 opbyte = stream.read8();
+        if (opbyte == Prefix::OperandSizeOverride) {
+            m_o32 = !o32;
+            m_has_operand_size_override_prefix = true;
+            continue;
+        }
+        if (opbyte == Prefix::AddressSizeOverride) {
+            m_a32 = !a32;
+            m_has_address_size_override_prefix = true;
+            continue;
+        }
+        if (opbyte == Prefix::REPZ || opbyte == Prefix::REPNZ) {
+            m_rep_prefix = opbyte;
+            continue;
+        }
+        if (opbyte == Prefix::LOCK) {
+            m_has_lock_prefix = true;
+            continue;
+        }
+        auto segment_prefix = to_segment_prefix(opbyte);
+        if (segment_prefix.has_value()) {
+            m_segment_prefix = segment_prefix;
+            continue;
+        }
+        m_op = opbyte;
+        break;
+    }
+
+    if (m_op == 0x0F) {
+        m_has_sub_op = true;
+        m_sub_op = stream.read8();
+        m_descriptor = m_o32 ? &s_0f_table32[m_sub_op] : &s_0f_table16[m_sub_op];
+    } else {
+        m_descriptor = m_o32 ? &s_table32[m_op] : &s_table16[m_op];
+    }
+
+    m_has_rm = m_descriptor->has_rm;
+    if (m_has_rm) {
+        // Consume ModR/M (may include SIB and displacement.)
+        m_modrm.decode(stream, m_a32);
+        m_register_index = (m_modrm.m_rm >> 3) & 7;
+    } else {
+        if (m_has_sub_op)
+            m_register_index = m_sub_op & 7;
+        else
+            m_register_index = m_op & 7;
+    }
+
+    bool hasSlash = m_descriptor->format == MultibyteWithSlash;
+
+    if (hasSlash) {
+        m_descriptor = &m_descriptor->slashes[slash()];
+    }
+
+    if (!m_descriptor->mnemonic) {
+        if (m_has_sub_op) {
+            if (hasSlash)
+                fprintf(stderr, "Instruction %02X %02X /%u not understood\n", m_op, m_sub_op, slash());
+            else
+                fprintf(stderr, "Instruction %02X %02X not understood\n", m_op, m_sub_op);
+        } else {
+            if (hasSlash)
+                fprintf(stderr, "Instruction %02X /%u not understood\n", m_op, slash());
+            else
+                fprintf(stderr, "Instruction %02X not understood\n", m_op);
+        }
+        m_descriptor = nullptr;
+        return;
+    }
+
+    m_imm1_bytes = m_descriptor->imm1_bytes_for_address_size(m_a32);
+    m_imm2_bytes = m_descriptor->imm2_bytes_for_address_size(m_a32);
+
+    // Consume immediates if present.
+    if (m_imm2_bytes)
+        m_imm2 = stream.read(m_imm2_bytes);
+    if (m_imm1_bytes)
+        m_imm1 = stream.read(m_imm1_bytes);
+
+    m_handler = m_descriptor->handler;
+
+#ifdef DISALLOW_INVALID_LOCK_PREFIX
+    if (m_has_lock_prefix && !m_descriptor->lock_prefix_allowed) {
+        fprintf(stderr, "Instruction not allowed with LOCK prefix, this will raise #UD\n");
+        m_descriptor = nullptr;
+    }
+#endif
+}
+
+ALWAYS_INLINE u32 InstructionStream::read(unsigned count)
+{
+    switch (count) {
+    case 1:
+        return read8();
+    case 2:
+        return read16();
+    case 4:
+        return read32();
+    }
+    ASSERT_NOT_REACHED();
+    return 0;
+}
+
+ALWAYS_INLINE void MemoryOrRegisterReference::decode(InstructionStream& stream, bool a32)
+{
+    m_a32 = a32;
+    m_rm = stream.read8();
+
+    if (m_a32) {
+        decode32(stream);
+        switch (m_displacement_bytes) {
+        case 0:
+            break;
+        case 1:
+            m_displacement32 = sign_extended_to<u32>(stream.read8());
+            break;
+        case 4:
+            m_displacement32 = stream.read32();
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+            break;
+        }
+    } else {
+        decode16(stream);
+        switch (m_displacement_bytes) {
+        case 0:
+            break;
+        case 1:
+            m_displacement16 = sign_extended_to<u16>(stream.read8());
+            break;
+        case 2:
+            m_displacement16 = stream.read16();
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+            break;
+        }
+    }
+}
+
+ALWAYS_INLINE void MemoryOrRegisterReference::decode16(InstructionStream&)
+{
+    ASSERT(!m_a32);
+
+    switch (m_rm & 0xc0) {
+    case 0:
+        if ((m_rm & 0x07) == 6)
+            m_displacement_bytes = 2;
+        else
+            ASSERT(m_displacement_bytes == 0);
+        break;
+    case 0x40:
+        m_displacement_bytes = 1;
+        break;
+    case 0x80:
+        m_displacement_bytes = 2;
+        break;
+    case 0xc0:
+        m_register_index = m_rm & 7;
+        break;
+    }
+}
+
+ALWAYS_INLINE void MemoryOrRegisterReference::decode32(InstructionStream& stream)
+{
+    ASSERT(m_a32);
+
+    switch (m_rm & 0xc0) {
+    case 0:
+        if ((m_rm & 0x07) == 5)
+            m_displacement_bytes = 4;
+        break;
+    case 0x40:
+        m_displacement_bytes = 1;
+        break;
+    case 0x80:
+        m_displacement_bytes = 4;
+        break;
+    case 0xc0:
+        m_register_index = m_rm & 7;
+        return;
+    }
+
+    m_has_sib = (m_rm & 0x07) == 4;
+    if (m_has_sib) {
+        m_sib = stream.read8();
+        if ((m_sib & 0x07) == 5) {
+            switch ((m_rm >> 6) & 0x03) {
+            case 0:
+                ASSERT(!m_displacement_bytes || m_displacement_bytes == 4);
+                m_displacement_bytes = 4;
+                break;
+            case 1:
+                ASSERT(!m_displacement_bytes || m_displacement_bytes == 1);
+                m_displacement_bytes = 1;
+                break;
+            case 2:
+                ASSERT(!m_displacement_bytes || m_displacement_bytes == 4);
+                m_displacement_bytes = 4;
+                break;
+            default:
+                ASSERT_NOT_REACHED();
+                break;
+            }
+        }
+    }
+}
+
+
 }