diff mbox

[16/17] x86emul: make all FPU emulation use the stub

Message ID 594A7DE402000078001653B6@prv-mh.provo.novell.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jan Beulich June 21, 2017, 12:08 p.m. UTC
While this means quite some reduction of (source) code, the main
purpose is to no longer have exceptions raised from other than stubs.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
x86emul: make all FPU emulation use the stub

While this means quite some reduction of (source) code, the main
purpose is to no longer have exceptions raised from other than stubs.

Signed-off-by: Jan Beulich <jbeulich@suse.com>

--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -1261,28 +1261,25 @@ static inline bool fpu_check_write(void)
     return !(fsw & FSW_ES);
 }
 
-#define emulate_fpu_insn(_op)                           \
-    asm volatile (                                      \
-        "movb $2f-1f,%0 \n"                             \
-        "1: " _op "     \n"                             \
-        "2:             \n"                             \
-        : "=m" (fic.insn_bytes) : : "memory" )
-
-#define emulate_fpu_insn_memdst(_op, _arg)              \
-    asm volatile (                                      \
-        "movb $2f-1f,%0 \n"                             \
-        "1: " _op " %1  \n"                             \
-        "2:             \n"                             \
-        : "=m" (fic.insn_bytes), "=m" (_arg)            \
-        : : "memory" )
-
-#define emulate_fpu_insn_memsrc(_op, _arg)              \
-    asm volatile (                                      \
-        "movb $2f-1f,%0 \n"                             \
-        "1: " _op " %1  \n"                             \
-        "2:             \n"                             \
-        : "=m" (fic.insn_bytes)                         \
-        : "m" (_arg) : "memory" )
+#define emulate_fpu_insn_memdst(opc, ext, arg)                          \
+do {                                                                    \
+    /* ModRM: mod=0, reg=ext, rm=0, i.e. a (%rax) operand */            \
+    fic.insn_bytes = 2;                                                 \
+    memcpy(get_stub(stub),                                              \
+           ((uint8_t[]){ opc, ((ext) & 7) << 3, 0xc3 }), 3);            \
+    invoke_stub("", "", "+m" (fic), "+m" (arg) : "a" (&(arg)));         \
+    put_stub(stub);                                                     \
+} while (0)
+
+#define emulate_fpu_insn_memsrc(opc, ext, arg)                          \
+do {                                                                    \
+    /* ModRM: mod=0, reg=ext, rm=0, i.e. a (%rax) operand */            \
+    fic.insn_bytes = 2;                                                 \
+    memcpy(get_stub(stub),                                              \
+           ((uint8_t[]){ opc, ((ext) & 7) << 3, 0xc3 }), 3);            \
+    invoke_stub("", "", "+m" (fic) : "m" (arg), "a" (&(arg)));          \
+    put_stub(stub);                                                     \
+} while (0)
 
 #define emulate_fpu_insn_stub(bytes...)                                 \
 do {                                                                    \
@@ -3834,8 +3831,7 @@ x86_emulate(
     case 0x9b:  /* wait/fwait */
         host_and_vcpu_must_have(fpu);
         get_fpu(X86EMUL_FPU_wait, &fic);
-        fic.insn_bytes = 1;
-        asm volatile ( "fwait" ::: "memory" );
+        emulate_fpu_insn_stub(b);
         check_fpu_exn(&fic);
         break;
 
@@ -4253,37 +4249,13 @@ x86_emulate(
             emulate_fpu_insn_stub(0xd8, modrm);
             break;
         default:
+        fpu_memsrc32:
             ASSERT(ea.type == OP_MEM);
             if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
                                  4, ctxt)) != X86EMUL_OKAY )
                 goto done;
-            switch ( modrm_reg & 7 )
-            {
-            case 0: /* fadd */
-                emulate_fpu_insn_memsrc("fadds", src.val);
-                break;
-            case 1: /* fmul */
-                emulate_fpu_insn_memsrc("fmuls", src.val);
-                break;
-            case 2: /* fcom */
-                emulate_fpu_insn_memsrc("fcoms", src.val);
-                break;
-            case 3: /* fcomp */
-                emulate_fpu_insn_memsrc("fcomps", src.val);
-                break;
-            case 4: /* fsub */
-                emulate_fpu_insn_memsrc("fsubs", src.val);
-                break;
-            case 5: /* fsubr */
-                emulate_fpu_insn_memsrc("fsubrs", src.val);
-                break;
-            case 6: /* fdiv */
-                emulate_fpu_insn_memsrc("fdivs", src.val);
-                break;
-            case 7: /* fdivr */
-                emulate_fpu_insn_memsrc("fdivrs", src.val);
-                break;
-            }
+            emulate_fpu_insn_memsrc(b, modrm_reg, src.val);
+            break;
         }
         check_fpu_exn(&fic);
         break;
@@ -4330,52 +4302,46 @@ x86_emulate(
             break;
         default:
             generate_exception_if(ea.type != OP_MEM, EXC_UD);
-            dst = ea;
             switch ( modrm_reg & 7 )
             {
             case 0: /* fld m32fp */
-                if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
-                                     4, ctxt)) != X86EMUL_OKAY )
-                    goto done;
-                emulate_fpu_insn_memsrc("flds", src.val);
-                dst.type = OP_NONE;
-                break;
+                goto fpu_memsrc32;
             case 2: /* fst m32fp */
-                emulate_fpu_insn_memdst("fsts", dst.val);
-                dst.bytes = 4;
-                break;
             case 3: /* fstp m32fp */
-                emulate_fpu_insn_memdst("fstps", dst.val);
+            fpu_memdst32:
+                dst = ea;
                 dst.bytes = 4;
+                emulate_fpu_insn_memdst(b, modrm_reg, dst.val);
                 break;
             case 4: /* fldenv - TODO */
                 state->fpu_ctrl = true;
                 goto cannot_emulate;
             case 5: /* fldcw m2byte */
                 state->fpu_ctrl = true;
+            fpu_memsrc16:
                 if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
                                      2, ctxt)) != X86EMUL_OKAY )
                     goto done;
-                emulate_fpu_insn_memsrc("fldcw", src.val);
-                dst.type = OP_NONE;
+                emulate_fpu_insn_memsrc(b, modrm_reg, src.val);
                 break;
             case 6: /* fnstenv - TODO */
                 state->fpu_ctrl = true;
                 goto cannot_emulate;
             case 7: /* fnstcw m2byte */
                 state->fpu_ctrl = true;
-                emulate_fpu_insn_memdst("fnstcw", dst.val);
+            fpu_memdst16:
+                dst = ea;
                 dst.bytes = 2;
+                emulate_fpu_insn_memdst(b, modrm_reg, dst.val);
                 break;
             default:
                 generate_exception(EXC_UD);
             }
             /*
              * Control instructions can't raise FPU exceptions, so we need
-             * to consider suppressing writes only for non-control ones. All
-             * of them in this group have data width 4.
+             * to consider suppressing writes only for non-control ones.
              */
-            if ( dst.type == OP_MEM && dst.bytes == 4 && !fpu_check_write() )
+            if ( dst.type == OP_MEM && !state->fpu_ctrl && !fpu_check_write() )
                 dst.type = OP_NONE;
         }
         check_fpu_exn(&fic);
@@ -4398,36 +4364,7 @@ x86_emulate(
             break;
         default:
             generate_exception_if(ea.type != OP_MEM, EXC_UD);
-            if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
-                                 4, ctxt)) != X86EMUL_OKAY )
-                goto done;
-            switch ( modrm_reg & 7 )
-            {
-            case 0: /* fiadd m32i */
-                emulate_fpu_insn_memsrc("fiaddl", src.val);
-                break;
-            case 1: /* fimul m32i */
-                emulate_fpu_insn_memsrc("fimull", src.val);
-                break;
-            case 2: /* ficom m32i */
-                emulate_fpu_insn_memsrc("ficoml", src.val);
-                break;
-            case 3: /* ficomp m32i */
-                emulate_fpu_insn_memsrc("ficompl", src.val);
-                break;
-            case 4: /* fisub m32i */
-                emulate_fpu_insn_memsrc("fisubl", src.val);
-                break;
-            case 5: /* fisubr m32i */
-                emulate_fpu_insn_memsrc("fisubrl", src.val);
-                break;
-            case 6: /* fidiv m32i */
-                emulate_fpu_insn_memsrc("fidivl", src.val);
-                break;
-            case 7: /* fidivr m32i */
-                emulate_fpu_insn_memsrc("fidivrl", src.val);
-                break;
-            }
+            goto fpu_memsrc32;
         }
         check_fpu_exn(&fic);
         break;
@@ -4457,50 +4394,35 @@ x86_emulate(
             break;
         default:
             generate_exception_if(ea.type != OP_MEM, EXC_UD);
-            dst = ea;
             switch ( modrm_reg & 7 )
             {
             case 0: /* fild m32i */
-                if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
-                                     4, ctxt)) != X86EMUL_OKAY )
-                    goto done;
-                emulate_fpu_insn_memsrc("fildl", src.val);
-                dst.type = OP_NONE;
-                break;
+                goto fpu_memsrc32;
             case 1: /* fisttp m32i */
                 host_and_vcpu_must_have(sse3);
-                emulate_fpu_insn_memdst("fisttpl", dst.val);
-                dst.bytes = 4;
-                break;
+                /* fall through */
             case 2: /* fist m32i */
-                emulate_fpu_insn_memdst("fistl", dst.val);
-                dst.bytes = 4;
-                break;
             case 3: /* fistp m32i */
-                emulate_fpu_insn_memdst("fistpl", dst.val);
-                dst.bytes = 4;
-                break;
+                goto fpu_memdst32;
             case 5: /* fld m80fp */
+            fpu_memsrc80:
                 if ( (rc = ops->read(ea.mem.seg, ea.mem.off, mmvalp,
                                      10, ctxt)) != X86EMUL_OKAY )
                     goto done;
-                emulate_fpu_insn_memsrc("fldt", *mmvalp);
-                dst.type = OP_NONE;
+                emulate_fpu_insn_memsrc(b, modrm_reg, *mmvalp);
                 break;
             case 7: /* fstp m80fp */
+            fpu_memdst80:
                 fail_if(!ops->write);
-                emulate_fpu_insn_memdst("fstpt", *mmvalp);
+                emulate_fpu_insn_memdst(b, modrm_reg, *mmvalp);
                 if ( fpu_check_write() &&
                      (rc = ops->write(ea.mem.seg, ea.mem.off, mmvalp,
                                       10, ctxt)) != X86EMUL_OKAY )
                     goto done;
-                dst.type = OP_NONE;
                 break;
             default:
                 generate_exception(EXC_UD);
             }
-            if ( dst.type == OP_MEM && !fpu_check_write() )
-                dst.type = OP_NONE;
         }
         check_fpu_exn(&fic);
         break;
@@ -4521,37 +4443,13 @@ x86_emulate(
             emulate_fpu_insn_stub(0xdc, modrm);
             break;
         default:
+        fpu_memsrc64:
             ASSERT(ea.type == OP_MEM);
             if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
                                  8, ctxt)) != X86EMUL_OKAY )
                 goto done;
-            switch ( modrm_reg & 7 )
-            {
-            case 0: /* fadd m64fp */
-                emulate_fpu_insn_memsrc("faddl", src.val);
-                break;
-            case 1: /* fmul m64fp */
-                emulate_fpu_insn_memsrc("fmull", src.val);
-                break;
-            case 2: /* fcom m64fp */
-                emulate_fpu_insn_memsrc("fcoml", src.val);
-                break;
-            case 3: /* fcomp m64fp */
-                emulate_fpu_insn_memsrc("fcompl", src.val);
-                break;
-            case 4: /* fsub m64fp */
-                emulate_fpu_insn_memsrc("fsubl", src.val);
-                break;
-            case 5: /* fsubr m64fp */
-                emulate_fpu_insn_memsrc("fsubrl", src.val);
-                break;
-            case 6: /* fdiv m64fp */
-                emulate_fpu_insn_memsrc("fdivl", src.val);
-                break;
-            case 7: /* fdivr m64fp */
-                emulate_fpu_insn_memsrc("fdivrl", src.val);
-                break;
-            }
+            emulate_fpu_insn_memsrc(b, modrm_reg, src.val);
+            break;
         }
         check_fpu_exn(&fic);
         break;
@@ -4571,28 +4469,19 @@ x86_emulate(
             break;
         default:
             generate_exception_if(ea.type != OP_MEM, EXC_UD);
-            dst = ea;
             switch ( modrm_reg & 7 )
             {
             case 0: /* fld m64fp */;
-                if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
-                                     8, ctxt)) != X86EMUL_OKAY )
-                    goto done;
-                emulate_fpu_insn_memsrc("fldl", src.val);
-                dst.type = OP_NONE;
-                break;
+                goto fpu_memsrc64;
             case 1: /* fisttp m64i */
                 host_and_vcpu_must_have(sse3);
-                emulate_fpu_insn_memdst("fisttpll", dst.val);
-                dst.bytes = 8;
-                break;
+                /* fall through */
             case 2: /* fst m64fp */
-                emulate_fpu_insn_memdst("fstl", dst.val);
-                dst.bytes = 8;
-                break;
             case 3: /* fstp m64fp */
-                emulate_fpu_insn_memdst("fstpl", dst.val);
+            fpu_memdst64:
+                dst = ea;
                 dst.bytes = 8;
+                emulate_fpu_insn_memdst(b, modrm_reg, dst.val);
                 break;
             case 4: /* frstor - TODO */
             case 6: /* fnsave - TODO */
@@ -4600,18 +4489,15 @@ x86_emulate(
                 goto cannot_emulate;
             case 7: /* fnstsw m2byte */
                 state->fpu_ctrl = true;
-                emulate_fpu_insn_memdst("fnstsw", dst.val);
-                dst.bytes = 2;
-                break;
+                goto fpu_memdst16;
             default:
                 generate_exception(EXC_UD);
             }
             /*
              * Control instructions can't raise FPU exceptions, so we need
-             * to consider suppressing writes only for non-control ones. All
-             * of them in this group have data width 8.
+             * to consider suppressing writes only for non-control ones.
              */
-            if ( dst.type == OP_MEM && dst.bytes == 8 && !fpu_check_write() )
+            if ( dst.type == OP_MEM && !state->fpu_ctrl && !fpu_check_write() )
                 dst.type = OP_NONE;
         }
         check_fpu_exn(&fic);
@@ -4634,33 +4520,8 @@ x86_emulate(
             break;
         default:
             generate_exception_if(ea.type != OP_MEM, EXC_UD);
-            switch ( modrm_reg & 7 )
-            {
-            case 0: /* fiadd m16i */
-                emulate_fpu_insn_memsrc("fiadds", src.val);
-                break;
-            case 1: /* fimul m16i */
-                emulate_fpu_insn_memsrc("fimuls", src.val);
-                break;
-            case 2: /* ficom m16i */
-                emulate_fpu_insn_memsrc("ficoms", src.val);
-                break;
-            case 3: /* ficomp m16i */
-                emulate_fpu_insn_memsrc("ficomps", src.val);
-                break;
-            case 4: /* fisub m16i */
-                emulate_fpu_insn_memsrc("fisubs", src.val);
-                break;
-            case 5: /* fisubr m16i */
-                emulate_fpu_insn_memsrc("fisubrs", src.val);
-                break;
-            case 6: /* fidiv m16i */
-                emulate_fpu_insn_memsrc("fidivs", src.val);
-                break;
-            case 7: /* fidivr m16i */
-                emulate_fpu_insn_memsrc("fidivrs", src.val);
-                break;
-            }
+            emulate_fpu_insn_memsrc(b, modrm_reg, src.val);
+            break;
         }
         check_fpu_exn(&fic);
         break;
@@ -4676,7 +4537,7 @@ x86_emulate(
             dst.bytes = 2;
             dst.type = OP_REG;
             dst.reg = (void *)&_regs.ax;
-            emulate_fpu_insn_memdst("fnstsw", dst.val);
+            emulate_fpu_insn_memdst(b, modrm_reg, dst.val);
             break;
         case 0xe8 ... 0xef: /* fucomip %stN */
         case 0xf0 ... 0xf7: /* fcomip %stN */
@@ -4691,59 +4552,26 @@ x86_emulate(
             break;
         default:
             generate_exception_if(ea.type != OP_MEM, EXC_UD);
-            dst = ea;
             switch ( modrm_reg & 7 )
             {
             case 0: /* fild m16i */
-                if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
-                                     2, ctxt)) != X86EMUL_OKAY )
-                    goto done;
-                emulate_fpu_insn_memsrc("filds", src.val);
-                dst.type = OP_NONE;
-                break;
+                goto fpu_memsrc16;
             case 1: /* fisttp m16i */
                 host_and_vcpu_must_have(sse3);
-                emulate_fpu_insn_memdst("fisttps", dst.val);
-                dst.bytes = 2;
-                break;
+                /* fall through */
             case 2: /* fist m16i */
-                emulate_fpu_insn_memdst("fists", dst.val);
-                dst.bytes = 2;
-                break;
             case 3: /* fistp m16i */
-                emulate_fpu_insn_memdst("fistps", dst.val);
-                dst.bytes = 2;
-                break;
+                goto fpu_memdst16;
             case 4: /* fbld m80dec */
-                if ( (rc = ops->read(ea.mem.seg, ea.mem.off, mmvalp,
-                                     10, ctxt)) != X86EMUL_OKAY )
-                    goto done;
-                emulate_fpu_insn_memsrc("fbld", *mmvalp);
-                dst.type = OP_NONE;
-                break;
+                goto fpu_memsrc80;
             case 5: /* fild m64i */
-                if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
-                                     8, ctxt)) != X86EMUL_OKAY )
-                    goto done;
-                emulate_fpu_insn_memsrc("fildll", src.val);
                 dst.type = OP_NONE;
-                break;
+                goto fpu_memsrc64;
             case 6: /* fbstp packed bcd */
-                fail_if(!ops->write);
-                emulate_fpu_insn_memdst("fbstp", *mmvalp);
-                if ( fpu_check_write() &&
-                     (rc = ops->write(ea.mem.seg, ea.mem.off, mmvalp,
-                                      10, ctxt)) != X86EMUL_OKAY )
-                    goto done;
-                dst.type = OP_NONE;
-                break;
+                goto fpu_memdst80;
             case 7: /* fistp m64i */
-                emulate_fpu_insn_memdst("fistpll", dst.val);
-                dst.bytes = 8;
-                break;
+                goto fpu_memdst64;
             }
-            if ( dst.type == OP_MEM && !fpu_check_write() )
-                dst.type = OP_NONE;
         }
         check_fpu_exn(&fic);
         break;
diff mbox

Patch

--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -1261,28 +1261,25 @@  static inline bool fpu_check_write(void)
     return !(fsw & FSW_ES);
 }
 
-#define emulate_fpu_insn(_op)                           \
-    asm volatile (                                      \
-        "movb $2f-1f,%0 \n"                             \
-        "1: " _op "     \n"                             \
-        "2:             \n"                             \
-        : "=m" (fic.insn_bytes) : : "memory" )
-
-#define emulate_fpu_insn_memdst(_op, _arg)              \
-    asm volatile (                                      \
-        "movb $2f-1f,%0 \n"                             \
-        "1: " _op " %1  \n"                             \
-        "2:             \n"                             \
-        : "=m" (fic.insn_bytes), "=m" (_arg)            \
-        : : "memory" )
-
-#define emulate_fpu_insn_memsrc(_op, _arg)              \
-    asm volatile (                                      \
-        "movb $2f-1f,%0 \n"                             \
-        "1: " _op " %1  \n"                             \
-        "2:             \n"                             \
-        : "=m" (fic.insn_bytes)                         \
-        : "m" (_arg) : "memory" )
+#define emulate_fpu_insn_memdst(opc, ext, arg)                          \
+do {                                                                    \
+    /* ModRM: mod=0, reg=ext, rm=0, i.e. a (%rax) operand */            \
+    fic.insn_bytes = 2;                                                 \
+    memcpy(get_stub(stub),                                              \
+           ((uint8_t[]){ opc, ((ext) & 7) << 3, 0xc3 }), 3);            \
+    invoke_stub("", "", "+m" (fic), "+m" (arg) : "a" (&(arg)));         \
+    put_stub(stub);                                                     \
+} while (0)
+
+#define emulate_fpu_insn_memsrc(opc, ext, arg)                          \
+do {                                                                    \
+    /* ModRM: mod=0, reg=ext, rm=0, i.e. a (%rax) operand */            \
+    fic.insn_bytes = 2;                                                 \
+    memcpy(get_stub(stub),                                              \
+           ((uint8_t[]){ opc, ((ext) & 7) << 3, 0xc3 }), 3);            \
+    invoke_stub("", "", "+m" (fic) : "m" (arg), "a" (&(arg)));          \
+    put_stub(stub);                                                     \
+} while (0)
 
 #define emulate_fpu_insn_stub(bytes...)                                 \
 do {                                                                    \
@@ -3834,8 +3831,7 @@  x86_emulate(
     case 0x9b:  /* wait/fwait */
         host_and_vcpu_must_have(fpu);
         get_fpu(X86EMUL_FPU_wait, &fic);
-        fic.insn_bytes = 1;
-        asm volatile ( "fwait" ::: "memory" );
+        emulate_fpu_insn_stub(b);
         check_fpu_exn(&fic);
         break;
 
@@ -4253,37 +4249,13 @@  x86_emulate(
             emulate_fpu_insn_stub(0xd8, modrm);
             break;
         default:
+        fpu_memsrc32:
             ASSERT(ea.type == OP_MEM);
             if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
                                  4, ctxt)) != X86EMUL_OKAY )
                 goto done;
-            switch ( modrm_reg & 7 )
-            {
-            case 0: /* fadd */
-                emulate_fpu_insn_memsrc("fadds", src.val);
-                break;
-            case 1: /* fmul */
-                emulate_fpu_insn_memsrc("fmuls", src.val);
-                break;
-            case 2: /* fcom */
-                emulate_fpu_insn_memsrc("fcoms", src.val);
-                break;
-            case 3: /* fcomp */
-                emulate_fpu_insn_memsrc("fcomps", src.val);
-                break;
-            case 4: /* fsub */
-                emulate_fpu_insn_memsrc("fsubs", src.val);
-                break;
-            case 5: /* fsubr */
-                emulate_fpu_insn_memsrc("fsubrs", src.val);
-                break;
-            case 6: /* fdiv */
-                emulate_fpu_insn_memsrc("fdivs", src.val);
-                break;
-            case 7: /* fdivr */
-                emulate_fpu_insn_memsrc("fdivrs", src.val);
-                break;
-            }
+            emulate_fpu_insn_memsrc(b, modrm_reg, src.val);
+            break;
         }
         check_fpu_exn(&fic);
         break;
@@ -4330,52 +4302,46 @@  x86_emulate(
             break;
         default:
             generate_exception_if(ea.type != OP_MEM, EXC_UD);
-            dst = ea;
             switch ( modrm_reg & 7 )
             {
             case 0: /* fld m32fp */
-                if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
-                                     4, ctxt)) != X86EMUL_OKAY )
-                    goto done;
-                emulate_fpu_insn_memsrc("flds", src.val);
-                dst.type = OP_NONE;
-                break;
+                goto fpu_memsrc32;
             case 2: /* fst m32fp */
-                emulate_fpu_insn_memdst("fsts", dst.val);
-                dst.bytes = 4;
-                break;
             case 3: /* fstp m32fp */
-                emulate_fpu_insn_memdst("fstps", dst.val);
+            fpu_memdst32:
+                dst = ea;
                 dst.bytes = 4;
+                emulate_fpu_insn_memdst(b, modrm_reg, dst.val);
                 break;
             case 4: /* fldenv - TODO */
                 state->fpu_ctrl = true;
                 goto cannot_emulate;
             case 5: /* fldcw m2byte */
                 state->fpu_ctrl = true;
+            fpu_memsrc16:
                 if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
                                      2, ctxt)) != X86EMUL_OKAY )
                     goto done;
-                emulate_fpu_insn_memsrc("fldcw", src.val);
-                dst.type = OP_NONE;
+                emulate_fpu_insn_memsrc(b, modrm_reg, src.val);
                 break;
             case 6: /* fnstenv - TODO */
                 state->fpu_ctrl = true;
                 goto cannot_emulate;
             case 7: /* fnstcw m2byte */
                 state->fpu_ctrl = true;
-                emulate_fpu_insn_memdst("fnstcw", dst.val);
+            fpu_memdst16:
+                dst = ea;
                 dst.bytes = 2;
+                emulate_fpu_insn_memdst(b, modrm_reg, dst.val);
                 break;
             default:
                 generate_exception(EXC_UD);
             }
             /*
              * Control instructions can't raise FPU exceptions, so we need
-             * to consider suppressing writes only for non-control ones. All
-             * of them in this group have data width 4.
+             * to consider suppressing writes only for non-control ones.
              */
-            if ( dst.type == OP_MEM && dst.bytes == 4 && !fpu_check_write() )
+            if ( dst.type == OP_MEM && !state->fpu_ctrl && !fpu_check_write() )
                 dst.type = OP_NONE;
         }
         check_fpu_exn(&fic);
@@ -4398,36 +4364,7 @@  x86_emulate(
             break;
         default:
             generate_exception_if(ea.type != OP_MEM, EXC_UD);
-            if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
-                                 4, ctxt)) != X86EMUL_OKAY )
-                goto done;
-            switch ( modrm_reg & 7 )
-            {
-            case 0: /* fiadd m32i */
-                emulate_fpu_insn_memsrc("fiaddl", src.val);
-                break;
-            case 1: /* fimul m32i */
-                emulate_fpu_insn_memsrc("fimull", src.val);
-                break;
-            case 2: /* ficom m32i */
-                emulate_fpu_insn_memsrc("ficoml", src.val);
-                break;
-            case 3: /* ficomp m32i */
-                emulate_fpu_insn_memsrc("ficompl", src.val);
-                break;
-            case 4: /* fisub m32i */
-                emulate_fpu_insn_memsrc("fisubl", src.val);
-                break;
-            case 5: /* fisubr m32i */
-                emulate_fpu_insn_memsrc("fisubrl", src.val);
-                break;
-            case 6: /* fidiv m32i */
-                emulate_fpu_insn_memsrc("fidivl", src.val);
-                break;
-            case 7: /* fidivr m32i */
-                emulate_fpu_insn_memsrc("fidivrl", src.val);
-                break;
-            }
+            goto fpu_memsrc32;
         }
         check_fpu_exn(&fic);
         break;
@@ -4457,50 +4394,35 @@  x86_emulate(
             break;
         default:
             generate_exception_if(ea.type != OP_MEM, EXC_UD);
-            dst = ea;
             switch ( modrm_reg & 7 )
             {
             case 0: /* fild m32i */
-                if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
-                                     4, ctxt)) != X86EMUL_OKAY )
-                    goto done;
-                emulate_fpu_insn_memsrc("fildl", src.val);
-                dst.type = OP_NONE;
-                break;
+                goto fpu_memsrc32;
             case 1: /* fisttp m32i */
                 host_and_vcpu_must_have(sse3);
-                emulate_fpu_insn_memdst("fisttpl", dst.val);
-                dst.bytes = 4;
-                break;
+                /* fall through */
             case 2: /* fist m32i */
-                emulate_fpu_insn_memdst("fistl", dst.val);
-                dst.bytes = 4;
-                break;
             case 3: /* fistp m32i */
-                emulate_fpu_insn_memdst("fistpl", dst.val);
-                dst.bytes = 4;
-                break;
+                goto fpu_memdst32;
             case 5: /* fld m80fp */
+            fpu_memsrc80:
                 if ( (rc = ops->read(ea.mem.seg, ea.mem.off, mmvalp,
                                      10, ctxt)) != X86EMUL_OKAY )
                     goto done;
-                emulate_fpu_insn_memsrc("fldt", *mmvalp);
-                dst.type = OP_NONE;
+                emulate_fpu_insn_memsrc(b, modrm_reg, *mmvalp);
                 break;
             case 7: /* fstp m80fp */
+            fpu_memdst80:
                 fail_if(!ops->write);
-                emulate_fpu_insn_memdst("fstpt", *mmvalp);
+                emulate_fpu_insn_memdst(b, modrm_reg, *mmvalp);
                 if ( fpu_check_write() &&
                      (rc = ops->write(ea.mem.seg, ea.mem.off, mmvalp,
                                       10, ctxt)) != X86EMUL_OKAY )
                     goto done;
-                dst.type = OP_NONE;
                 break;
             default:
                 generate_exception(EXC_UD);
             }
-            if ( dst.type == OP_MEM && !fpu_check_write() )
-                dst.type = OP_NONE;
         }
         check_fpu_exn(&fic);
         break;
@@ -4521,37 +4443,13 @@  x86_emulate(
             emulate_fpu_insn_stub(0xdc, modrm);
             break;
         default:
+        fpu_memsrc64:
             ASSERT(ea.type == OP_MEM);
             if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
                                  8, ctxt)) != X86EMUL_OKAY )
                 goto done;
-            switch ( modrm_reg & 7 )
-            {
-            case 0: /* fadd m64fp */
-                emulate_fpu_insn_memsrc("faddl", src.val);
-                break;
-            case 1: /* fmul m64fp */
-                emulate_fpu_insn_memsrc("fmull", src.val);
-                break;
-            case 2: /* fcom m64fp */
-                emulate_fpu_insn_memsrc("fcoml", src.val);
-                break;
-            case 3: /* fcomp m64fp */
-                emulate_fpu_insn_memsrc("fcompl", src.val);
-                break;
-            case 4: /* fsub m64fp */
-                emulate_fpu_insn_memsrc("fsubl", src.val);
-                break;
-            case 5: /* fsubr m64fp */
-                emulate_fpu_insn_memsrc("fsubrl", src.val);
-                break;
-            case 6: /* fdiv m64fp */
-                emulate_fpu_insn_memsrc("fdivl", src.val);
-                break;
-            case 7: /* fdivr m64fp */
-                emulate_fpu_insn_memsrc("fdivrl", src.val);
-                break;
-            }
+            emulate_fpu_insn_memsrc(b, modrm_reg, src.val);
+            break;
         }
         check_fpu_exn(&fic);
         break;
@@ -4571,28 +4469,19 @@  x86_emulate(
             break;
         default:
             generate_exception_if(ea.type != OP_MEM, EXC_UD);
-            dst = ea;
             switch ( modrm_reg & 7 )
             {
             case 0: /* fld m64fp */;
-                if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
-                                     8, ctxt)) != X86EMUL_OKAY )
-                    goto done;
-                emulate_fpu_insn_memsrc("fldl", src.val);
-                dst.type = OP_NONE;
-                break;
+                goto fpu_memsrc64;
             case 1: /* fisttp m64i */
                 host_and_vcpu_must_have(sse3);
-                emulate_fpu_insn_memdst("fisttpll", dst.val);
-                dst.bytes = 8;
-                break;
+                /* fall through */
             case 2: /* fst m64fp */
-                emulate_fpu_insn_memdst("fstl", dst.val);
-                dst.bytes = 8;
-                break;
             case 3: /* fstp m64fp */
-                emulate_fpu_insn_memdst("fstpl", dst.val);
+            fpu_memdst64:
+                dst = ea;
                 dst.bytes = 8;
+                emulate_fpu_insn_memdst(b, modrm_reg, dst.val);
                 break;
             case 4: /* frstor - TODO */
             case 6: /* fnsave - TODO */
@@ -4600,18 +4489,15 @@  x86_emulate(
                 goto cannot_emulate;
             case 7: /* fnstsw m2byte */
                 state->fpu_ctrl = true;
-                emulate_fpu_insn_memdst("fnstsw", dst.val);
-                dst.bytes = 2;
-                break;
+                goto fpu_memdst16;
             default:
                 generate_exception(EXC_UD);
             }
             /*
              * Control instructions can't raise FPU exceptions, so we need
-             * to consider suppressing writes only for non-control ones. All
-             * of them in this group have data width 8.
+             * to consider suppressing writes only for non-control ones.
              */
-            if ( dst.type == OP_MEM && dst.bytes == 8 && !fpu_check_write() )
+            if ( dst.type == OP_MEM && !state->fpu_ctrl && !fpu_check_write() )
                 dst.type = OP_NONE;
         }
         check_fpu_exn(&fic);
@@ -4634,33 +4520,8 @@  x86_emulate(
             break;
         default:
             generate_exception_if(ea.type != OP_MEM, EXC_UD);
-            switch ( modrm_reg & 7 )
-            {
-            case 0: /* fiadd m16i */
-                emulate_fpu_insn_memsrc("fiadds", src.val);
-                break;
-            case 1: /* fimul m16i */
-                emulate_fpu_insn_memsrc("fimuls", src.val);
-                break;
-            case 2: /* ficom m16i */
-                emulate_fpu_insn_memsrc("ficoms", src.val);
-                break;
-            case 3: /* ficomp m16i */
-                emulate_fpu_insn_memsrc("ficomps", src.val);
-                break;
-            case 4: /* fisub m16i */
-                emulate_fpu_insn_memsrc("fisubs", src.val);
-                break;
-            case 5: /* fisubr m16i */
-                emulate_fpu_insn_memsrc("fisubrs", src.val);
-                break;
-            case 6: /* fidiv m16i */
-                emulate_fpu_insn_memsrc("fidivs", src.val);
-                break;
-            case 7: /* fidivr m16i */
-                emulate_fpu_insn_memsrc("fidivrs", src.val);
-                break;
-            }
+            emulate_fpu_insn_memsrc(b, modrm_reg, src.val);
+            break;
         }
         check_fpu_exn(&fic);
         break;
@@ -4676,7 +4537,7 @@  x86_emulate(
             dst.bytes = 2;
             dst.type = OP_REG;
             dst.reg = (void *)&_regs.ax;
-            emulate_fpu_insn_memdst("fnstsw", dst.val);
+            emulate_fpu_insn_memdst(b, modrm_reg, dst.val);
             break;
         case 0xe8 ... 0xef: /* fucomip %stN */
         case 0xf0 ... 0xf7: /* fcomip %stN */
@@ -4691,59 +4552,26 @@  x86_emulate(
             break;
         default:
             generate_exception_if(ea.type != OP_MEM, EXC_UD);
-            dst = ea;
             switch ( modrm_reg & 7 )
             {
             case 0: /* fild m16i */
-                if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
-                                     2, ctxt)) != X86EMUL_OKAY )
-                    goto done;
-                emulate_fpu_insn_memsrc("filds", src.val);
-                dst.type = OP_NONE;
-                break;
+                goto fpu_memsrc16;
             case 1: /* fisttp m16i */
                 host_and_vcpu_must_have(sse3);
-                emulate_fpu_insn_memdst("fisttps", dst.val);
-                dst.bytes = 2;
-                break;
+                /* fall through */
             case 2: /* fist m16i */
-                emulate_fpu_insn_memdst("fists", dst.val);
-                dst.bytes = 2;
-                break;
             case 3: /* fistp m16i */
-                emulate_fpu_insn_memdst("fistps", dst.val);
-                dst.bytes = 2;
-                break;
+                goto fpu_memdst16;
             case 4: /* fbld m80dec */
-                if ( (rc = ops->read(ea.mem.seg, ea.mem.off, mmvalp,
-                                     10, ctxt)) != X86EMUL_OKAY )
-                    goto done;
-                emulate_fpu_insn_memsrc("fbld", *mmvalp);
-                dst.type = OP_NONE;
-                break;
+                goto fpu_memsrc80;
             case 5: /* fild m64i */
-                if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
-                                     8, ctxt)) != X86EMUL_OKAY )
-                    goto done;
-                emulate_fpu_insn_memsrc("fildll", src.val);
                 dst.type = OP_NONE;
-                break;
+                goto fpu_memsrc64;
             case 6: /* fbstp packed bcd */
-                fail_if(!ops->write);
-                emulate_fpu_insn_memdst("fbstp", *mmvalp);
-                if ( fpu_check_write() &&
-                     (rc = ops->write(ea.mem.seg, ea.mem.off, mmvalp,
-                                      10, ctxt)) != X86EMUL_OKAY )
-                    goto done;
-                dst.type = OP_NONE;
-                break;
+                goto fpu_memdst80;
             case 7: /* fistp m64i */
-                emulate_fpu_insn_memdst("fistpll", dst.val);
-                dst.bytes = 8;
-                break;
+                goto fpu_memdst64;
             }
-            if ( dst.type == OP_MEM && !fpu_check_write() )
-                dst.type = OP_NONE;
         }
         check_fpu_exn(&fic);
         break;