Pārlūkot izejas kodu

UserspaceEmulator: Sketch out a SoftFPU interface

Hendiadyoin1 4 gadi atpakaļ
vecāks
revīzija
09a1a0b319

+ 248 - 0
Userland/DevTools/UserspaceEmulator/SoftFPU.cpp

@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "SoftFPU.h"
+#include "Emulator.h"
+#include "SoftCPU.h"
+#include "ValueWithShadow.h"
+
+#include <AK/BitCast.h>
+#include <AK/NumericLimits.h>
+#include <AK/UFixedBigInt.h>
+
+#include <unistd.h>
+
+#if defined(__GNUC__) && !defined(__clang__)
+#    pragma GCC optimize("O3")
+#endif
+
+#define TODO_INSN()                                                                   \
+    do {                                                                              \
+        reportln("\n=={}== Unimplemented instruction: {}\n", getpid(), __FUNCTION__); \
+        m_emulator.dump_backtrace();                                                  \
+        _exit(0);                                                                     \
+    } while (0)
+
+template<typename T>
+ALWAYS_INLINE void warn_if_uninitialized(T value_with_shadow, const char* message)
+{
+    if (value_with_shadow.is_uninitialized()) [[unlikely]] {
+        reportln("\033[31;1mWarning! Use of uninitialized value: {}\033[0m\n", message);
+        UserspaceEmulator::Emulator::the().dump_backtrace();
+    }
+}
+
+namespace UserspaceEmulator {
+
+ALWAYS_INLINE void SoftFPU::warn_if_fpu_not_set_absolute(u8 index) const
+{
+    if (!fpu_is_set(index)) [[unlikely]] {
+        // FIXME: Are we supposed to set a flag here?
+        //        We might need to raise a stack underflow here
+        reportln("\033[31;1mWarning! Read of uninitialized value on the FPU Stack ({} abs)\033[0m\n", index);
+        m_emulator.dump_backtrace();
+    }
+}
+ALWAYS_INLINE void SoftFPU::warn_if_mmx_absolute(u8 index) const
+{
+    if (m_reg_is_mmx[index]) [[unlikely]] {
+        reportln("\033[31;1mWarning! Use of an MMX register as an FPU value ({} abs)\033[0m\n", index);
+        m_emulator.dump_backtrace();
+    }
+}
+ALWAYS_INLINE void SoftFPU::warn_if_fpu_absolute(u8 index) const
+{
+    if (!m_reg_is_mmx[index]) [[unlikely]] {
+        reportln("\033[31;1mWarning! Use of an FPU value ({} abs)  as an MMX register\033[0m\n", index);
+        m_emulator.dump_backtrace();
+    }
+}
+
+ALWAYS_INLINE long double SoftFPU::fpu_get(u8 index) const
+{
+    VERIFY(index < 8);
+    warn_if_fpu_not_set_absolute(index);
+    warn_if_mmx_absolute(index);
+
+    u8 effective_index = (m_fpu_stack_top + index) % 8;
+
+    return m_storage[effective_index].fp;
+}
+ALWAYS_INLINE void SoftFPU::fpu_set_absolute(u8 index, long double value)
+{
+    VERIFY(index < 8);
+    set_tag_from_value_absolute(index, value);
+    m_storage[index].fp = value;
+    m_reg_is_mmx[index] = false;
+}
+ALWAYS_INLINE void SoftFPU::fpu_set(u8 index, long double value)
+{
+    VERIFY(index < 8);
+    fpu_set_absolute((m_fpu_stack_top + index) % 8, value);
+}
+ALWAYS_INLINE MMX SoftFPU::mmx_get(u8 index) const
+{
+    VERIFY(index < 8);
+    warn_if_fpu_absolute(index);
+    return m_storage[index].mmx;
+}
+ALWAYS_INLINE void SoftFPU::mmx_set(u8 index, MMX value)
+{
+    m_storage[index].mmx = value;
+    // The high bytes are set to 0b11... to make the floatingpoint value NaN.
+    // This way we are technically able to find out if we are reading the wrong
+    // type, but this is still difficult, so we use our own lookup for that
+    // We set the alignment bytes to all 1's, too, just in case
+    m_storage[index].__high = ~(decltype(m_storage[index].__high))0u;
+    m_reg_is_mmx[index] = true;
+}
+
+ALWAYS_INLINE void SoftFPU::fpu_push(long double value)
+{
+    if (fpu_is_set(7))
+        fpu_set_stack_overflow();
+    m_fpu_stack_top = (m_fpu_stack_top - 1u) % 8;
+
+    fpu_set(0, value);
+}
+
+ALWAYS_INLINE long double SoftFPU::fpu_pop()
+{
+    warn_if_mmx_absolute(m_fpu_stack_top);
+
+    if (!fpu_is_set(0))
+        fpu_set_stack_underflow();
+
+    auto ret = fpu_get(0);
+    fpu_set_tag(0, FPU_Tag::Empty);
+    m_fpu_stack_top = (m_fpu_stack_top + 1u) % 8;
+    return ret;
+}
+
+ALWAYS_INLINE void SoftFPU::fpu_set_exception(FPU_Exception ex)
+{
+    switch (ex) {
+    case FPU_Exception::StackFault:
+        m_fpu_error_stackfault = 1;
+        m_fpu_error_invalid = 1; // Implies InvalidOperation
+        break;
+    case FPU_Exception::InvalidOperation:
+        m_fpu_error_invalid = 1;
+        if (!m_fpu_mask_invalid)
+            break;
+        return;
+    case FPU_Exception::DenormalizedOperand:
+        m_fpu_error_denorm = 1;
+        if (!m_fpu_mask_denorm)
+            break;
+        return;
+    case FPU_Exception::ZeroDivide:
+        m_fpu_error_zero_div = 1;
+        if (!m_fpu_mask_zero_div)
+            break;
+        return;
+    case FPU_Exception::Overflow:
+        m_fpu_error_overflow = 1;
+        if (!m_fpu_mask_overflow)
+            break;
+        return;
+    case FPU_Exception::Underflow:
+        m_fpu_error_underflow = 1;
+        if (!m_fpu_mask_underflow)
+            break;
+        return;
+    case FPU_Exception::Precision:
+        m_fpu_error_precision = 1;
+        if (!m_fpu_mask_precision)
+            break;
+        return;
+    }
+
+    // set exception bit
+    m_fpu_error_summary = 1;
+
+    // FIXME: set traceback
+    // For that we need to get the currently executing instruction and
+    // the previous eip
+
+    // FIXME: Call FPU Exception handler
+    reportln("Trying to call Exception handler from {}", fpu_exception_string(ex));
+    fpu_dump_env();
+    m_emulator.dump_backtrace();
+    TODO();
+}
+
+template<Arithmetic T>
+__attribute__((pure)) ALWAYS_INLINE T SoftFPU::fpu_round(long double value) const
+{
+    // FIXME: may need to set indefinite values manually
+    switch (fpu_get_round_mode()) {
+    case RoundingMode::NEAREST:
+        return static_cast<T>(roundl(value));
+    case RoundingMode::DOWN:
+        return static_cast<T>(floorl(value));
+    case RoundingMode::UP:
+        return static_cast<T>(ceill(value));
+    case RoundingMode::TRUNK:
+        return static_cast<T>(truncl(value));
+    default:
+        VERIFY_NOT_REACHED();
+    }
+}
+
+template<Arithmetic T>
+__attribute__((pure)) ALWAYS_INLINE T SoftFPU::fpu_round_checked(long double value)
+{
+    T result = fpu_round<T>(value);
+    if (auto rnd = value - result) {
+        if (rnd > 0)
+            set_c1(1);
+        else
+            set_c1(0);
+        fpu_set_exception(FPU_Exception::Precision);
+    }
+    return result;
+}
+
+template<FloatingPoint T>
+__attribute__((pure)) ALWAYS_INLINE T SoftFPU::fpu_convert(long double value) const
+{
+    // FIXME: actually round the right way
+    return static_cast<T>(value);
+}
+template<FloatingPoint T>
+__attribute__((pure)) ALWAYS_INLINE T SoftFPU::fpu_convert_checked(long double value)
+{
+    T result = fpu_convert<T>(value);
+    if (auto rnd = value - result) {
+        if (rnd > 0)
+            set_c1(1);
+        else
+            set_c1(0);
+        fpu_set_exception(FPU_Exception::Precision);
+    }
+    return result;
+}
+
+template<Signed R, Signed I>
+__attribute__((const)) ALWAYS_INLINE R signed_saturate(I input)
+{
+    if (input > NumericLimits<R>::max())
+        return NumericLimits<R>::max();
+    if (input < NumericLimits<R>::min())
+        return NumericLimits<R>::min();
+    return static_cast<R>(input);
+}
+template<Unsigned R, Unsigned I>
+__attribute__((const)) ALWAYS_INLINE R unsigned_saturate(I input)
+{
+    if (input > NumericLimits<R>::max())
+        return NumericLimits<R>::max();
+    return static_cast<R>(input);
+}
+
+}

+ 612 - 0
Userland/DevTools/UserspaceEmulator/SoftFPU.h

@@ -0,0 +1,612 @@
+/*
+ * Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include "Report.h"
+#include <AK/Concepts.h>
+#include <AK/SIMD.h>
+#include <LibX86/Instruction.h>
+#include <LibX86/Interpreter.h>
+
+#include <math.h>
+#include <string.h>
+
+namespace UserspaceEmulator {
+using namespace AK::SIMD;
+class Emulator;
+class SoftCPU;
+
+union MMX {
+    u64 raw;
+    c8x8 v8;
+    i16x4 v16;
+    i32x2 v32;
+    i16x4 v16u;
+    i32x2 v32u;
+};
+static_assert(sizeof(MMX) == sizeof(u64));
+
+class SoftFPU final {
+public:
+    SoftFPU(Emulator& emulator, SoftCPU& cpu)
+        : m_emulator(emulator)
+        , m_cpu(cpu)
+    {
+    }
+
+    ALWAYS_INLINE bool c0() const { return m_fpu_c0; }
+    ALWAYS_INLINE bool c1() const { return m_fpu_c1; }
+    ALWAYS_INLINE bool c2() const { return m_fpu_c2; }
+    ALWAYS_INLINE bool c3() const { return m_fpu_c3; }
+
+    ALWAYS_INLINE void set_c0(bool val) { m_fpu_c0 = val; }
+    ALWAYS_INLINE void set_c1(bool val) { m_fpu_c1 = val; }
+    ALWAYS_INLINE void set_c2(bool val) { m_fpu_c2 = val; }
+    ALWAYS_INLINE void set_c3(bool val) { m_fpu_c3 = val; }
+
+    long double fpu_get(u8 index) const;
+
+    void fpu_push(long double value);
+    long double fpu_pop();
+    void fpu_set_absolute(u8 index, long double value);
+    void fpu_set(u8 index, long double value);
+
+    MMX mmx_get(u8 index) const;
+    void mmx_set(u8 index, MMX value);
+
+private:
+    friend class SoftCPU;
+
+    Emulator& m_emulator;
+    SoftCPU& m_cpu;
+
+    enum class FPU_Exception : u8 {
+        InvalidOperation,
+        DenormalizedOperand,
+        ZeroDivide,
+        Overflow,
+        Underflow,
+        Precision,
+        StackFault,
+    };
+
+    enum class FPU_Tag : u8 {
+        Valid = 0b00,
+        Zero = 0b01,
+        Special = 0b10,
+        Empty = 0b11
+    };
+
+    enum class RoundingMode : u8 {
+        NEAREST = 0b00,
+        DOWN = 0b01,
+        UP = 0b10,
+        TRUNK = 0b11
+    };
+
+    void fpu_dump_env()
+    {
+        reportln("Exceptions: #I:{} #D:{} #O:{} #D:{} #U:{} #P:{} #SF:{} Summary:{}",
+            m_fpu_error_invalid,
+            m_fpu_error_denorm,
+            m_fpu_error_zero_div,
+            m_fpu_error_overflow,
+            m_fpu_error_underflow,
+            m_fpu_error_precision,
+            m_fpu_error_stackfault,
+            m_fpu_error_summary);
+        reportln("Masks: #I:{} #D:{} #O:{} #D:{} #U:{} #P:{}",
+            m_fpu_mask_invalid,
+            m_fpu_mask_denorm,
+            m_fpu_mask_zero_div,
+            m_fpu_mask_overflow,
+            m_fpu_mask_underflow,
+            m_fpu_mask_precision);
+        reportln("C0:{} C1:{} C2:{} C3:{}", c0(), c1(), c2(), c3());
+        reportln("fpu-stacktop: {}", m_fpu_stack_top);
+        reportln("fpu-stack /w stacktop (real):");
+        for (u8 i = 0; i < 8; ++i) {
+            reportln("\t{} ({}): fp {} ({}), mmx (:x016)", i, (u8)((m_fpu_stack_top + i) % 8), m_storage[(m_fpu_stack_top + i) % 8].fp, fpu_is_set(i) ? "set" : "free", m_storage[(m_fpu_stack_top + i) % 8].mmx.raw);
+        }
+    }
+
+    String fpu_exception_string(FPU_Exception ex)
+    {
+        switch (ex) {
+        case FPU_Exception::StackFault:
+            return "Stackfault";
+        case FPU_Exception::InvalidOperation:
+            return "Invalid Operation";
+        case FPU_Exception::DenormalizedOperand:
+            return "Denormalized Operant";
+        case FPU_Exception::ZeroDivide:
+            return "Divide by Zero";
+        case FPU_Exception::Overflow:
+            return "Overflow";
+        case FPU_Exception::Underflow:
+            return "Underflow";
+        case FPU_Exception::Precision:
+            return "Precision";
+        }
+        VERIFY_NOT_REACHED();
+    }
+
+    // FIXME: Technically we should check for exceptions after each insn, too,
+    //        this might be important for FLDENV, but otherwise it should
+    //        be fine this way
+    void fpu_set_exception(FPU_Exception ex);
+
+    ALWAYS_INLINE void fpu_set_stack_overflow()
+    {
+        reportln("Stack Overflow");
+        set_c1(1);
+        fpu_set_exception(FPU_Exception::StackFault);
+    }
+
+    ALWAYS_INLINE void fpu_set_stack_underflow()
+    {
+        reportln("Stack Underflow");
+        set_c1(0);
+        fpu_set_exception(FPU_Exception::StackFault);
+    }
+
+    constexpr FPU_Tag fpu_get_tag_absolute(u8 index) const
+    {
+        switch (index) {
+        case 0:
+            return FPU_Tag(m_fpu_status_0);
+        case 1:
+            return FPU_Tag(m_fpu_status_1);
+        case 2:
+            return FPU_Tag(m_fpu_status_2);
+        case 3:
+            return FPU_Tag(m_fpu_status_3);
+        case 4:
+            return FPU_Tag(m_fpu_status_4);
+        case 5:
+            return FPU_Tag(m_fpu_status_5);
+        case 6:
+            return FPU_Tag(m_fpu_status_6);
+        case 7:
+            return FPU_Tag(m_fpu_status_7);
+        default:
+            VERIFY_NOT_REACHED();
+        }
+    }
+
+    constexpr FPU_Tag fpu_get_tag(u8 index) const
+    {
+        VERIFY(index < 8);
+        return fpu_get_tag_absolute((m_fpu_stack_top + index) % 8);
+    }
+
+    ALWAYS_INLINE void fpu_set_tag_absolute(u8 index, FPU_Tag tag)
+    {
+        switch (index) {
+        case 0:
+            m_fpu_status_0 = (u8)tag;
+            break;
+        case 1:
+            m_fpu_status_1 = (u8)tag;
+            break;
+        case 2:
+            m_fpu_status_2 = (u8)tag;
+            break;
+        case 3:
+            m_fpu_status_3 = (u8)tag;
+            break;
+        case 4:
+            m_fpu_status_4 = (u8)tag;
+            break;
+        case 5:
+            m_fpu_status_5 = (u8)tag;
+            break;
+        case 6:
+            m_fpu_status_6 = (u8)tag;
+            break;
+        case 7:
+            m_fpu_status_7 = (u8)tag;
+            break;
+        default:
+            VERIFY_NOT_REACHED();
+        }
+    }
+
+    ALWAYS_INLINE void fpu_set_tag(u8 index, FPU_Tag tag)
+    {
+        VERIFY(index < 8);
+        fpu_set_tag_absolute((m_fpu_stack_top + index) % 8, tag);
+    }
+
+    ALWAYS_INLINE void set_tag_from_value_absolute(u8 index, long double val)
+    {
+        switch (fpclassify(val)) {
+        case FP_ZERO:
+            fpu_set_tag_absolute(index, FPU_Tag::Zero);
+            break;
+        case FP_NAN:
+        case FP_INFINITE:
+        case FP_SUBNORMAL:
+            fpu_set_tag_absolute(index, FPU_Tag::Special);
+            break;
+        case FP_NORMAL:
+            fpu_set_tag_absolute(index, FPU_Tag::Valid);
+            break;
+        default:
+            VERIFY_NOT_REACHED();
+        }
+    }
+
+    ALWAYS_INLINE void set_tag_from_value(u8 index, long double val)
+    {
+        set_tag_from_value_absolute((m_fpu_stack_top + index) % 8, val);
+    }
+
+    ALWAYS_INLINE bool fpu_isnan(u8 index) const
+    {
+        return isnan(fpu_get(index));
+    }
+
+    ALWAYS_INLINE bool fpu_is_set(u8 index) const
+    {
+        return fpu_get_tag_absolute((m_fpu_stack_top + index) % 8) != FPU_Tag::Empty;
+    }
+
+    ALWAYS_INLINE RoundingMode fpu_get_round_mode() const
+    {
+        return RoundingMode(m_fpu_round_mode);
+    }
+
+    template<Arithmetic T>
+    T fpu_round(long double) const;
+    template<Arithmetic T>
+    T fpu_round_checked(long double);
+
+    template<FloatingPoint T>
+    T fpu_convert(long double) const;
+    template<FloatingPoint T>
+    T fpu_convert_checked(long double);
+
+    ALWAYS_INLINE void fpu_set_unordered()
+    {
+        set_c0(1);
+        set_c2(1);
+        set_c3(1);
+    }
+    void warn_if_mmx_absolute(u8 index) const;
+    void warn_if_fpu_absolute(u8 index) const;
+    void warn_if_fpu_not_set_absolute(u8 index) const;
+
+    void mmx_common() { m_fpu_tw = 0; }
+
+    bool m_reg_is_mmx[8] { false };
+
+    union {
+        long double fp;
+        struct {
+            MMX mmx;
+            Conditional<sizeof(long double) == 16,
+                u64,
+                Conditional<sizeof(long double) == 12,
+                    u32,
+                    u16>>
+                __high;
+        };
+    } m_storage[8];
+
+    union {
+        u16 m_fpu_cw { 0x037F };
+        struct {
+            u16 m_fpu_mask_invalid : 1;
+            u16 m_fpu_mask_denorm : 1;
+            u16 m_fpu_mask_zero_div : 1;
+            u16 m_fpu_mask_overflow : 1;
+            u16 m_fpu_mask_underflow : 1;
+            u16 m_fpu_mask_precision : 1;
+            u16 : 2; // unused
+            u16 m_fpu_precission : 2;
+            u16 m_fpu_round_mode : 2;
+            u16 m_fpu_infinity_control : 1;
+            u16 : 3; // unused
+        };
+    };
+
+    union {
+        u16 m_fpu_sw { 0 };
+        struct {
+            u16 m_fpu_error_invalid : 1;    // pre | IE -> #I (#IS, #IA)
+            u16 m_fpu_error_denorm : 1;     // pre | DE -> #D
+            u16 m_fpu_error_zero_div : 1;   // pre | ZE -> #Z
+            u16 m_fpu_error_overflow : 1;   // post| OE -> #O
+            u16 m_fpu_error_underflow : 1;  // post| UE -> #U
+            u16 m_fpu_error_precision : 1;  // post| PE -> #P
+            u16 m_fpu_error_stackfault : 1; // SF
+            u16 m_fpu_error_summary : 1;
+            u16 m_fpu_c0 : 1;
+            u16 m_fpu_c1 : 1;
+            u16 m_fpu_c2 : 1;
+            u16 m_fpu_stack_top : 3;
+            u16 m_fpu_c3 : 1;
+            u16 m_fpu_busy : 1;
+        };
+    };
+
+    union {
+        u16 m_fpu_tw { 0xFFFF };
+        struct {
+            u16 m_fpu_status_0 : 2;
+            u16 m_fpu_status_1 : 2;
+            u16 m_fpu_status_2 : 2;
+            u16 m_fpu_status_3 : 2;
+            u16 m_fpu_status_4 : 2;
+            u16 m_fpu_status_5 : 2;
+            u16 m_fpu_status_6 : 2;
+            u16 m_fpu_status_7 : 2;
+        };
+    };
+
+    u32 m_fpu_ip { 0 };
+    u16 m_fpu_cs { 0 };
+
+    u32 m_fpu_dp { 0 };
+    u16 m_fpu_ds { 0 };
+
+    u16 m_fpu_iop { 0 };
+
+    // Instructions
+
+    // DATA TRANSFER
+    void FLD_RM32(const X86::Instruction&);
+    void FLD_RM64(const X86::Instruction&);
+    void FLD_RM80(const X86::Instruction&);
+
+    void FST_RM32(const X86::Instruction&);
+    void FST_RM64(const X86::Instruction&);
+    void FSTP_RM32(const X86::Instruction&);
+    void FSTP_RM64(const X86::Instruction&);
+    void FSTP_RM80(const X86::Instruction&);
+
+    void FILD_RM32(const X86::Instruction&);
+    void FILD_RM16(const X86::Instruction&);
+    void FILD_RM64(const X86::Instruction&);
+
+    void FIST_RM16(const X86::Instruction&);
+    void FIST_RM32(const X86::Instruction&);
+    void FISTP_RM16(const X86::Instruction&);
+    void FISTP_RM32(const X86::Instruction&);
+    void FISTP_RM64(const X86::Instruction&);
+    void FISTTP_RM16(const X86::Instruction&);
+    void FISTTP_RM32(const X86::Instruction&);
+    void FISTTP_RM64(const X86::Instruction&);
+
+    void FBLD_M80(const X86::Instruction&);
+    void FBSTP_M80(const X86::Instruction&);
+
+    void FXCH(const X86::Instruction&);
+
+    void FCMOVE(const X86::Instruction&);
+    void FCMOVNE(const X86::Instruction&);
+    void FCMOVB(const X86::Instruction&);
+    void FCMOVBE(const X86::Instruction&);
+    void FCMOVNB(const X86::Instruction&);
+    void FCMOVNBE(const X86::Instruction&);
+    void FCMOVU(const X86::Instruction&);
+    void FCMOVNU(const X86::Instruction&);
+
+    // BASIC ARITHMETIC
+    void FADD_RM32(const X86::Instruction&);
+    void FADD_RM64(const X86::Instruction&);
+    void FADDP(const X86::Instruction&);
+
+    void FIADD_RM16(const X86::Instruction&);
+    void FIADD_RM32(const X86::Instruction&);
+
+    void FSUB_RM32(const X86::Instruction&);
+    void FSUB_RM64(const X86::Instruction&);
+    void FSUBP(const X86::Instruction&);
+    void FSUBR_RM32(const X86::Instruction&);
+    void FSUBR_RM64(const X86::Instruction&);
+    void FSUBRP(const X86::Instruction&);
+
+    void FISUB_RM16(const X86::Instruction&);
+    void FISUB_RM32(const X86::Instruction&);
+    void FISUBR_RM16(const X86::Instruction&);
+    void FISUBR_RM32(const X86::Instruction&);
+
+    void FMUL_RM32(const X86::Instruction&);
+    void FMUL_RM64(const X86::Instruction&);
+    void FMULP(const X86::Instruction&);
+
+    void FIMUL_RM16(const X86::Instruction&);
+    void FIMUL_RM32(const X86::Instruction&);
+
+    void FDIV_RM32(const X86::Instruction&);
+    void FDIV_RM64(const X86::Instruction&);
+    void FDIVP(const X86::Instruction&);
+    void FDIVR_RM32(const X86::Instruction&);
+    void FDIVR_RM64(const X86::Instruction&);
+    void FDIVRP(const X86::Instruction&);
+
+    void FIDIV_RM16(const X86::Instruction&);
+    void FIDIV_RM32(const X86::Instruction&);
+    void FIDIVR_RM16(const X86::Instruction&);
+    void FIDIVR_RM32(const X86::Instruction&);
+
+    void FPREM(const X86::Instruction&);
+    void FPREM1(const X86::Instruction&);
+
+    void FABS(const X86::Instruction&);
+    void FCHS(const X86::Instruction&);
+
+    void FRNDINT(const X86::Instruction&);
+
+    void FSCALE(const X86::Instruction&);
+
+    void FSQRT(const X86::Instruction&);
+
+    void FXTRACT(const X86::Instruction&);
+
+    // COMPARISON
+    void FCOM_RM32(const X86::Instruction&);
+    void FCOM_RM64(const X86::Instruction&);
+    void FCOMP_RM32(const X86::Instruction&);
+    void FCOMP_RM64(const X86::Instruction&);
+    void FCOMPP(const X86::Instruction&);
+    void FCOMI(const X86::Instruction&);
+    void FCOMIP(const X86::Instruction&);
+
+    void FUCOM(const X86::Instruction&);
+    void FUCOMP(const X86::Instruction&);
+    void FUCOMPP(const X86::Instruction&);
+    void FUCOMI(const X86::Instruction&);
+    void FUCOMIP(const X86::Instruction&);
+
+    void FICOM_RM16(const X86::Instruction&);
+    void FICOM_RM32(const X86::Instruction&);
+    void FICOMP_RM16(const X86::Instruction&);
+    void FICOMP_RM32(const X86::Instruction&);
+
+    void FTST(const X86::Instruction&);
+    void FXAM(const X86::Instruction&);
+
+    // TRANSCENDENTAL
+    void FSIN(const X86::Instruction&);
+    void FCOS(const X86::Instruction&);
+    void FSINCOS(const X86::Instruction&);
+    void FPTAN(const X86::Instruction&);
+    void FPATAN(const X86::Instruction&);
+
+    void F2XM1(const X86::Instruction&);
+    void FYL2X(const X86::Instruction&);
+    void FYL2XP1(const X86::Instruction&);
+
+    // CONSTANT LOAD
+    void FLD1(const X86::Instruction&);
+    void FLDZ(const X86::Instruction&);
+    void FLDPI(const X86::Instruction&);
+    void FLDL2E(const X86::Instruction&);
+    void FLDLN2(const X86::Instruction&);
+    void FLDL2T(const X86::Instruction&);
+    void FLDLG2(const X86::Instruction&);
+
+    // CONTROL
+    void FINCSTP(const X86::Instruction&);
+    void FDECSTP(const X86::Instruction&);
+    void FFREE(const X86::Instruction&);
+    void FFREEP(const X86::Instruction&); // undocumented
+
+    // FIXME: Non N- versions?
+    void FNINIT(const X86::Instruction&);
+    void FNCLEX(const X86::Instruction&);
+
+    void FNSTCW(const X86::Instruction&);
+    void FLDCW(const X86::Instruction&);
+
+    void FNSTENV(const X86::Instruction&);
+    void FLDENV(const X86::Instruction&);
+
+    void FNSAVE(const X86::Instruction&);
+    void FRSTOR(const X86::Instruction&);
+
+    void FNSTSW(const X86::Instruction&);
+    void FNSTSW_AX(const X86::Instruction&);
+
+    // FIXME: WAIT && FWAIT
+    void FNOP(const X86::Instruction&);
+
+    // FPU & SIMD MANAGEMENT
+    // FIXME: FXSAVE && FXRSTOR
+
+    // DO NOTHING?
+    // FIXME: FENI, FDISI, FSETPM
+    void FNENI(const X86::Instruction&);
+    void FNDISI(const X86::Instruction&);
+    void FNSETPM(const X86::Instruction&);
+
+    // MMX
+    // ARITHMETIC
+    void PADDB_mm1_mm2m64(const X86::Instruction&);
+    void PADDW_mm1_mm2m64(const X86::Instruction&);
+    void PADDD_mm1_mm2m64(const X86::Instruction&);
+    void PADDSB_mm1_mm2m64(const X86::Instruction&);
+    void PADDSW_mm1_mm2m64(const X86::Instruction&);
+    void PADDUSB_mm1_mm2m64(const X86::Instruction&);
+    void PADDUSW_mm1_mm2m64(const X86::Instruction&);
+
+    void PSUBB_mm1_mm2m64(const X86::Instruction&);
+    void PSUBW_mm1_mm2m64(const X86::Instruction&);
+    void PSUBD_mm1_mm2m64(const X86::Instruction&);
+    void PSUBSB_mm1_mm2m64(const X86::Instruction&);
+    void PSUBSW_mm1_mm2m64(const X86::Instruction&);
+    void PSUBUSB_mm1_mm2m64(const X86::Instruction&);
+    void PSUBUSW_mm1_mm2m64(const X86::Instruction&);
+
+    void PMULHW_mm1_mm2m64(const X86::Instruction&);
+    void PMULLW_mm1_mm2m64(const X86::Instruction&);
+
+    void PMADDWD_mm1_mm2m64(const X86::Instruction&);
+
+    // COMPARISON
+    void PCMPEQB_mm1_mm2m64(const X86::Instruction&);
+    void PCMPEQW_mm1_mm2m64(const X86::Instruction&);
+    void PCMPEQD_mm1_mm2m64(const X86::Instruction&);
+
+    void PCMPGTB_mm1_mm2m64(const X86::Instruction&);
+    void PCMPGTW_mm1_mm2m64(const X86::Instruction&);
+    void PCMPGTD_mm1_mm2m64(const X86::Instruction&);
+
+    // CONVERSION
+    void PACKSSDW_mm1_mm2m64(const X86::Instruction&);
+    void PACKSSWB_mm1_mm2m64(const X86::Instruction&);
+    void PACKUSWB_mm1_mm2m64(const X86::Instruction&);
+
+    // UNPACK
+    void PUNPCKHBW_mm1_mm2m64(const X86::Instruction&);
+    void PUNPCKHWD_mm1_mm2m64(const X86::Instruction&);
+    void PUNPCKHDQ_mm1_mm2m64(const X86::Instruction&);
+    void PUNPCKLBW_mm1_mm2m32(const X86::Instruction&);
+    void PUNPCKLWD_mm1_mm2m32(const X86::Instruction&);
+    void PUNPCKLDQ_mm1_mm2m32(const X86::Instruction&);
+
+    // LOGICAL
+    void PAND_mm1_mm2m64(const X86::Instruction&);
+    void PANDN_mm1_mm2m64(const X86::Instruction&);
+    void POR_mm1_mm2m64(const X86::Instruction&);
+    void PXOR_mm1_mm2m64(const X86::Instruction&);
+
+    // SHIFT
+    void PSLLW_mm1_mm2m64(const X86::Instruction&);
+    void PSLLW_mm1_imm8(const X86::Instruction&);
+    void PSLLD_mm1_mm2m64(const X86::Instruction&);
+    void PSLLD_mm1_imm8(const X86::Instruction&);
+    void PSLLQ_mm1_mm2m64(const X86::Instruction&);
+    void PSLLQ_mm1_imm8(const X86::Instruction&);
+    void PSRAW_mm1_mm2m64(const X86::Instruction&);
+    void PSRAW_mm1_imm8(const X86::Instruction&);
+    void PSRAD_mm1_mm2m64(const X86::Instruction&);
+    void PSRAD_mm1_imm8(const X86::Instruction&);
+    void PSRLW_mm1_mm2m64(const X86::Instruction&);
+    void PSRLW_mm1_imm8(const X86::Instruction&);
+    void PSRLD_mm1_mm2m64(const X86::Instruction&);
+    void PSRLD_mm1_imm8(const X86::Instruction&);
+    void PSRLQ_mm1_mm2m64(const X86::Instruction&);
+    void PSRLQ_mm1_imm8(const X86::Instruction&);
+
+    // DATA TRANSFER
+    void MOVD_mm1_rm32(const X86::Instruction&);
+    void MOVD_rm32_mm2(const X86::Instruction&);
+
+    void MOVQ_mm1_mm2m64(const X86::Instruction&);
+    void MOVQ_mm1m64_mm2(const X86::Instruction&);
+    void MOVQ_mm1_rm64(const X86::Instruction&); // long mode
+    void MOVQ_rm64_mm2(const X86::Instruction&); // long mode
+
+    // EMPTY MMX STATE
+    void EMMS(const X86::Instruction&);
+};
+
+}