]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
* x86 guest: fix bug in stmxcsr -- rounding mode field set wrongly
authorJulian Seward <jseward@acm.org>
Thu, 6 Jan 2005 12:36:38 +0000 (12:36 +0000)
committerJulian Seward <jseward@acm.org>
Thu, 6 Jan 2005 12:36:38 +0000 (12:36 +0000)
* x86 guest: fix longstanding bug in fsave/frstor -- they dumped
             FP registers in physical order, instead of ST order
* x86 guest: implement fxsave

git-svn-id: svn://svn.valgrind.org/vex/trunk@694

VEX/priv/guest-x86/gdefs.h
VEX/priv/guest-x86/ghelpers.c
VEX/priv/guest-x86/toIR.c
VEX/pub/libvex_ir.h

index ff98d5ef674cd9cb1f036344458d4a177f2b8e82..a2d4a8a1a3f5866492b27b3dd49cfd88f96e9754 100644 (file)
@@ -199,6 +199,8 @@ extern void  x86g_dirtyhelper_CPUID_sse2 ( VexGuestX86State* );
 
 extern void  x86g_dirtyhelper_FSAVE ( VexGuestX86State*, HWord );
 
+extern void  x86g_dirtyhelper_FINIT ( VexGuestX86State* );
+
 extern VexEmWarn
             x86g_dirtyhelper_FRSTOR ( VexGuestX86State*, HWord );
 
@@ -207,6 +209,8 @@ extern void x86g_dirtyhelper_FSTENV ( VexGuestX86State*, HWord );
 extern VexEmWarn 
             x86g_dirtyhelper_FLDENV ( VexGuestX86State*, HWord );
 
+extern void  x86g_dirtyhelper_FXSAVE ( VexGuestX86State*, HWord );
+
 
 /*---------------------------------------------------------*/
 /*--- Condition code stuff                              ---*/
index b8398b8a3ee3f214a442e66b93a920ce47b0a693..10880c009842f7f7347038ccf086aad4b750b73f 100644 (file)
@@ -936,9 +936,7 @@ UInt x86g_calculate_FXAM ( UInt tag, ULong dbl )
    UInt   c1;
    UChar* f64;
 
-   if (!host_is_little_endian()) {
-      vassert(0);
-   }
+   vassert(host_is_little_endian());
 
    /* vex_printf("calculate_FXAM ( %d, %llx ) .. ", tag, dbl ); */
 
@@ -1388,7 +1386,7 @@ typedef
 
 
 /* CLEAN HELPER */
-/* native_fpucw[15:0] contains a x87 native format FPU control word.
+/* fpucw[15:0] contains a x87 native format FPU control word.
    Extract from it the required FPROUND value and any resulting
    emulation warning, and return (warn << 32) | fpround value. 
 */
@@ -1425,7 +1423,7 @@ UInt x86g_create_fpucw ( UInt fpround )
 
 
 /* CLEAN HELPER */
-/* native_fpucw[15:0] contains a SSE native format MXCSR value.
+/* mxcsr[15:0] contains a SSE native format MXCSR value.
    Extract from it the required SSEROUND value and any resulting
    emulation warning, and return (warn << 32) | sseround value.
 */
@@ -1467,6 +1465,22 @@ UInt x86g_create_mxcsr ( UInt sseround )
 }
 
 
+/* CALLED FROM GENERATED CODE */
+/* DIRTY HELPER (writes guest state) */
+/* Initialise the x87 FPU state as per 'finit'. */
+void x86g_dirtyhelper_FINIT ( VexGuestX86State* gst )
+{
+   Int i;
+   gst->guest_FTOP = 0;
+   for (i = 0; i < 8; i++) {
+      gst->guest_FPTAG[i] = 0; /* empty */
+      gst->guest_FPREG[i] = 0; /* IEEE754 64-bit zero */
+   }
+   gst->guest_FPROUND = (UInt)Irrm_NEAREST;
+   gst->guest_FC3210  = 0;
+}
+
+
 /* This is used to implement both 'frstor' and 'fldenv'.  The latter
    appears to differ from the former only in that the 8 FP registers
    themselves are not transferred into the guest state. */
@@ -1475,7 +1489,7 @@ VexEmWarn put_x87 ( Bool moveRegs,
                     /*IN*/UChar* x87_state,
                     /*OUT*/VexGuestX86State* vex_state )
 {
-   Int        r;
+   Int        stno, preg;
    UInt       tag;
    Double*    vexRegs = (Double*)(&vex_state->guest_FPREG[0]);
    UChar*     vexTags = (UChar*)(&vex_state->guest_FPTAG[0]);
@@ -1489,18 +1503,25 @@ VexEmWarn put_x87 ( Bool moveRegs,
    ULong      pair;
 
    /* Copy registers and tags */
-   for (r = 0; r < 8; r++) {
-      tag = (tagw >> (2*r)) & 3;
+   tagw = 0;
+   for (stno = 0; stno < 8; stno++) {
+      preg = (stno + ftop) & 7;
+      tag = (tagw >> (2*preg)) & 3;
       if (tag == 3) {
          /* register is empty */
-         vexRegs[r] = 0.0;
-         vexTags[r] = 0;
+         /* hmm, if it's empty, does it still get written?  Probably
+            safer to say it does.  If we don't, memcheck could get out
+            of sync, in that it thinks all FP registers are defined by
+            this helper, but in reality some have not been updated. */
+         if (True || moveRegs)
+            vexRegs[preg] = 0.0;
+         vexTags[preg] = 0;
       } else {
          /* register is non-empty */
          if (moveRegs)
-            convert_f80le_to_f64le( &x87->reg[FP_REG(r)], 
-                                    (UChar*)&vexRegs[r] );
-         vexTags[r] = 1;
+            convert_f80le_to_f64le( &x87->reg[10*stno], 
+                                    (UChar*)&vexRegs[preg] );
+         vexTags[preg] = 1;
       }
    }
 
@@ -1532,10 +1553,12 @@ VexEmWarn LibVEX_GuestX86_put_x87 ( /*IN*/UChar* x87_state,
 
 
 /* VISIBLE TO LIBVEX CLIENT */
+/* Create an x87 FPU state from the guest state, as close as
+   we can approximate it. */
 void LibVEX_GuestX86_get_x87 ( /*IN*/VexGuestX86State* vex_state,
                                /*OUT*/UChar* x87_state )
 {
-   Int        i, r;
+   Int        i, stno, preg;
    UInt       tagw;
    Double*    vexRegs = (Double*)(&vex_state->guest_FPREG[0]);
    UChar*     vexTags = (UChar*)(&vex_state->guest_FPTAG[0]);
@@ -1551,16 +1574,20 @@ void LibVEX_GuestX86_get_x87 ( /*IN*/VexGuestX86State* vex_state,
    x87->env[FP_ENV_CTRL] 
       = (UShort)x86g_create_fpucw( vex_state->guest_FPROUND );
 
+   /* Dump the register stack in ST order. */
    tagw = 0;
-   for (r = 0; r < 8; r++) {
-      if (vexTags[r] == 0) {
+   for (stno = 0; stno < 8; stno++) {
+      preg = (stno + ftop) & 7;
+      if (vexTags[preg] == 0) {
          /* register is empty */
-         tagw |= (3 << (2*r));
-         convert_f64le_to_f80le( (UChar*)&vexRegs[r], &x87->reg[FP_REG(r)] );
+         tagw |= (3 << (2*preg));
+         convert_f64le_to_f80le( (UChar*)&vexRegs[preg], 
+                                 &x87->reg[10*stno] );
       } else {
          /* register is full. */
-         tagw |= (0 << (2*r));
-         convert_f64le_to_f80le( (UChar*)&vexRegs[r], &x87->reg[FP_REG(r)] );
+         tagw |= (0 << (2*preg));
+         convert_f64le_to_f80le( (UChar*)&vexRegs[preg], 
+                                 &x87->reg[10*stno] );
       }
    }
    x87->env[FP_ENV_TAG] = tagw;
@@ -1610,8 +1637,6 @@ UInt LibVEX_GuestX86_get_eflags ( /*IN*/VexGuestX86State* vex_state )
 /* VISIBLE TO LIBVEX CLIENT */
 void LibVEX_GuestX86_initialise ( /*OUT*/VexGuestX86State* vex_state )
 {
-   Int i;
-
    vex_state->guest_EAX = 0;
    vex_state->guest_ECX = 0;
    vex_state->guest_EDX = 0;
@@ -1630,14 +1655,10 @@ void LibVEX_GuestX86_initialise ( /*OUT*/VexGuestX86State* vex_state )
 
    vex_state->guest_EIP = 0;
 
-   vex_state->guest_FTOP = 0;
-   for (i = 0; i < 8; i++) {
-      vex_state->guest_FPTAG[i] = 0; /* empty */
-      vex_state->guest_FPREG[i] = 0; /* IEEE754 64-bit zero */
-   }
-   vex_state->guest_FPROUND = (UInt)Irrm_NEAREST;
-   vex_state->guest_FC3210  = 0;
+   /* Initialise the simulated FPU */
+   x86g_dirtyhelper_FINIT( vex_state );
 
+   /* Initialse the SSE state. */
 #  define SSEZERO(_xmm) _xmm[0]=_xmm[1]=_xmm[2]=_xmm[3] = 0;
 
    vex_state->guest_SSEROUND = (UInt)Irrm_NEAREST;
@@ -1814,6 +1835,97 @@ void x86g_dirtyhelper_CPUID_sse2 ( VexGuestX86State* st )
 }
 
 
+/* CALLED FROM GENERATED CODE */
+/* DIRTY HELPER (reads guest state, writes guest mem) */
+void x86g_dirtyhelper_FXSAVE ( VexGuestX86State* gst, HWord addr )
+{
+   /* Somewhat roundabout, but at least it's simple. */
+   Fpu_State tmp;
+   UShort*   addrS = (UShort*)addr;
+   UChar*    addrC = (UChar*)addr;
+   U128*     xmm   = (U128*)(addr + 160);
+   UInt      mxcsr;
+   UShort    fp_tags;
+   UChar     summary_tags;
+   Int       r, stno;
+   UShort    *srcS, *dstS;
+
+   LibVEX_GuestX86_get_x87( gst, (UChar*)&tmp );
+   mxcsr = x86g_create_mxcsr( gst->guest_SSEROUND );
+
+   /* Now build the proper fxsave image from the x87 image we just
+      made. */
+
+   addrS[0]  = tmp.env[FP_ENV_CTRL]; /* FCW: fpu control word */
+   addrS[1]  = tmp.env[FP_ENV_STAT]; /* FCW: fpu status word */
+
+   /* set addrS[2] in an endian-independent way */
+   summary_tags = 0;
+   fp_tags = tmp.env[FP_ENV_TAG];
+   for (r = 0; r < 8; r++) {
+      if ( ((fp_tags >> (2*r)) & 3) != 3 )
+         summary_tags |= (1 << r);
+   }
+   addrC[4]  = summary_tags; /* FTW: tag summary byte */
+   addrC[5]  = 0; /* pad */
+
+   addrS[3]  = 0; /* FOP: fpu opcode (bogus) */
+   addrS[4]  = 0;
+   addrS[5]  = 0; /* FPU IP (bogus) */
+   addrS[6]  = 0; /* FPU IP's segment selector (bogus) (although we
+                     could conceivably dump %CS here) */
+
+   addrS[7]  = 0; /* Intel reserved */
+
+   addrS[8]  = 0; /* FPU DP (operand pointer) (bogus) */
+   addrS[9]  = 0; /* FPU DP (operand pointer) (bogus) */
+   addrS[10] = 0; /* segment selector for above operand pointer; %DS
+                     perhaps? */
+   addrS[11] = 0; /* Intel reserved */
+
+   addrS[12] = (UShort)mxcsr;  /* MXCSR */
+   addrS[13] = (UShort)(mxcsr >> 16);
+
+   addrS[14] = 0xFFFF; /* MXCSR mask (lo16); who knows what for */
+   addrS[15] = 0xFFFF; /* MXCSR mask (hi16); who knows what for */
+
+   /* Copy in the FP registers, in ST order. */
+   for (stno = 0; stno < 8; stno++) {
+      srcS = (UShort*)(&tmp.reg[10*stno]);
+      dstS = (UShort*)(&addrS[16 + 8*stno]);
+      dstS[0] = srcS[0];
+      dstS[1] = srcS[1];
+      dstS[2] = srcS[2];
+      dstS[3] = srcS[3];
+      dstS[4] = srcS[4];
+      dstS[5] = 0;
+      dstS[6] = 0;
+      dstS[7] = 0;
+   }
+
+   /* That's the first 160 bytes of the image done.  Now only %xmm0
+      .. %xmm7 remain to be copied.  If the host is big-endian, these
+      need to be byte-swapped. */
+   vassert(host_is_little_endian());
+
+#  define COPY_U128(_dst,_src)                       \
+      do { _dst[0] = _src[0]; _dst[1] = _src[1];     \
+           _dst[2] = _src[2]; _dst[3] = _src[3]; }   \
+      while (0)
+
+   COPY_U128( xmm[0], gst->guest_XMM0 );
+   COPY_U128( xmm[1], gst->guest_XMM1 );
+   COPY_U128( xmm[2], gst->guest_XMM2 );
+   COPY_U128( xmm[3], gst->guest_XMM3 );
+   COPY_U128( xmm[4], gst->guest_XMM4 );
+   COPY_U128( xmm[5], gst->guest_XMM5 );
+   COPY_U128( xmm[6], gst->guest_XMM6 );
+   COPY_U128( xmm[7], gst->guest_XMM7 );
+
+#  undef COPY_U128
+}
+
+
 /* CALLED FROM GENERATED CODE */
 /* DIRTY HELPER (reads guest state, writes guest mem) */
 void x86g_dirtyhelper_FSAVE ( VexGuestX86State* gst, HWord addr )
index 22623200f344c042ece1892b35e73705396dfd0b..cd58b500f0c475ffe4688f09ab8846005a5c7710 100644 (file)
       to zero.
 
    x87 FP Limitations:
+
+   * all arithmetic done at 64 bits
+
    * no FP exceptions, except for handling stack over/underflow
+
    * FP rounding mode observed only for float->int conversions
-     and int->float conversions which could lose accuracy
+     and int->float conversions which could lose accuracy, and
+     for float-to-float rounding.  For all other operations, 
+     round-to-nearest is used, regardless.
+
    * FP sin/cos/tan/sincos: C2 flag is always cleared.  IOW the
      simulation claims the argument is in-range (-2^63 <= arg <= 2^63)
      even when it isn't.
+
    * some of the FCOM cases could do with testing -- not convinced
      that the args are the right way round.
 
+   * FSAVE does not re-initialise the FPU; it should do
+
+   * FINIT not only initialises the FPU environment, it also
+     zeroes all the FP registers.  It should leave the registers
+     unchanged.
+
    RDTSC returns zero, always.
 
    SAHF should cause eflags[1] == 1, and in fact it produces 0.  As
@@ -4287,6 +4301,46 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta )
                DIP("fnclex\n");
                break;
 
+            case 0xE3: {
+               /* Uses dirty helper: 
+                     void x86g_do_FINIT ( VexGuestX86State* ) */
+               IRDirty* d  = unsafeIRDirty_0_N ( 
+                                0/*regparms*/, 
+                                "x86g_dirtyhelper_FINIT", 
+                                &x86g_dirtyhelper_FINIT,
+                                mkIRExprVec_0()
+                             );
+               d->needsBBP = True;
+
+               /* declare we're writing guest state */
+               d->nFxState = 5;
+
+               d->fxState[0].fx     = Ifx_Write;
+               d->fxState[0].offset = OFFB_FTOP;
+               d->fxState[0].size   = sizeof(UInt);
+
+               d->fxState[1].fx     = Ifx_Write;
+               d->fxState[1].offset = OFFB_FPREGS;
+               d->fxState[1].size   = 8 * sizeof(ULong);
+
+               d->fxState[2].fx     = Ifx_Write;
+               d->fxState[2].offset = OFFB_FPTAGS;
+               d->fxState[2].size   = 8 * sizeof(UChar);
+
+               d->fxState[3].fx     = Ifx_Write;
+               d->fxState[3].offset = OFFB_FPROUND;
+               d->fxState[3].size   = sizeof(UInt);
+
+               d->fxState[4].fx     = Ifx_Write;
+               d->fxState[4].offset = OFFB_FC3210;
+               d->fxState[4].size   = sizeof(UInt);
+
+               stmt( IRStmt_Dirty(d) );
+
+               DIP("fninit");
+               break;
+            }
+
             case 0xE8 ... 0xEF: /* FUCOMI %st(0),%st(?) */
                fp_do_ucomi_ST0_STi( (UInt)modrm - 0xE8, False );
                break;
@@ -6863,6 +6917,84 @@ DisResult disInstr ( /*IN*/  Bool       resteerOK,
    /* Note, this doesn't handle SSE2 or SSE3.  That is handled in a
       later section, further on. */
 
+   insn = (UChar*)&guest_code[delta];
+
+   /* Treat fxsave specially.  It should be doable even on an SSE0
+      (Pentium-II class) CPU.  Hence be prepared to handle it on
+      any subarchitecture variant.
+   */
+
+   /* 0F AE /0 = FXSAVE m512 -- write x87 and SSE state to memory */
+   if (sz == 4 && insn[0] == 0x0F && insn[1] == 0xAE
+       && !epartIsReg(insn[2]) && gregOfRM(insn[2]) == 0) {
+      modrm = getIByte(delta+2);
+      vassert(sz == 4);
+      vassert(!epartIsReg(modrm));
+
+      addr = disAMode ( &alen, sorb, delta+2, dis_buf );
+      delta += 2+alen;
+
+      DIP("fxsave %s", dis_buf);
+
+      /* Uses dirty helper: 
+            void x86g_do_FXSAVE ( VexGuestX86State*, UInt ) */
+      IRDirty* d = unsafeIRDirty_0_N ( 
+                      0/*regparms*/, 
+                      "x86g_dirtyhelper_FXSAVE", 
+                      &x86g_dirtyhelper_FXSAVE,
+                      mkIRExprVec_1( mkexpr(addr) )
+                   );
+      d->needsBBP = True;
+
+      /* declare we're writing memory */
+      d->mFx   = Ifx_Write;
+      d->mAddr = mkexpr(addr);
+      d->mSize = 512;
+
+      /* declare we're reading guest state */
+      d->nFxState = 7;
+
+      d->fxState[0].fx     = Ifx_Read;
+      d->fxState[0].offset = OFFB_FTOP;
+      d->fxState[0].size   = sizeof(UInt);
+
+      d->fxState[1].fx     = Ifx_Read;
+      d->fxState[1].offset = OFFB_FPREGS;
+      d->fxState[1].size   = 8 * sizeof(ULong);
+
+      d->fxState[2].fx     = Ifx_Read;
+      d->fxState[2].offset = OFFB_FPTAGS;
+      d->fxState[2].size   = 8 * sizeof(UChar);
+
+      d->fxState[3].fx     = Ifx_Read;
+      d->fxState[3].offset = OFFB_FPROUND;
+      d->fxState[3].size   = sizeof(UInt);
+
+      d->fxState[4].fx     = Ifx_Read;
+      d->fxState[4].offset = OFFB_FC3210;
+      d->fxState[4].size   = sizeof(UInt);
+
+      d->fxState[5].fx     = Ifx_Read;
+      d->fxState[5].offset = OFFB_XMM0;
+      d->fxState[5].size   = 8 * sizeof(U128);
+
+      d->fxState[6].fx     = Ifx_Read;
+      d->fxState[6].offset = OFFB_SSEROUND;
+      d->fxState[6].size   = sizeof(UInt);
+
+      /* Be paranoid ... this assertion tries to ensure the 8 %xmm
+        images are packed back-to-back.  If not, the value of
+        d->fxState[5].size is wrong. */
+      vassert(16 == sizeof(U128));
+      vassert(OFFB_XMM7 == (OFFB_XMM0 + 7 * 16));
+
+      stmt( IRStmt_Dirty(d) );
+
+      goto decode_success;
+   }
+
+   /* ------ SSE decoder main ------ */
+
    /* Skip parts of the decoder which don't apply given the stated
       guest subarchitecture. */
    if (subarch == VexSubArchX86_sse0)
@@ -6871,8 +7003,6 @@ DisResult disInstr ( /*IN*/  Bool       resteerOK,
    /* Otherwise we must be doing sse1 or sse2, so we can at least try
       for SSE1 here. */
 
-   insn = (UChar*)&guest_code[delta];
-
    /* 0F 58 = ADDPS -- add 32Fx4 from R/M to R */
    if (sz == 4 && insn[0] == 0x0F && insn[1] == 0x58) {
       delta = dis_SSE_E_to_G_all( sorb, delta+2, "addps", Iop_Add32Fx4 );
@@ -7778,7 +7908,7 @@ DisResult disInstr ( /*IN*/  Bool       resteerOK,
       goto decode_success;
    }
 
-   /* 0F AE /3 = STMXCSR m32 -- load %mxcsr */
+   /* 0F AE /3 = STMXCSR m32 -- store %mxcsr */
    if (insn[0] == 0x0F && insn[1] == 0xAE
        && !epartIsReg(insn[2]) && gregOfRM(insn[2]) == 3) {
       modrm = getIByte(delta+2);
@@ -7797,7 +7927,7 @@ DisResult disInstr ( /*IN*/  Bool       resteerOK,
                mkIRExprCCall(
                   Ity_I32, 0/*regp*/,
                   "x86g_create_mxcsr", &x86g_create_mxcsr, 
-                  mkIRExprVec_1( get_fpround() ) 
+                  mkIRExprVec_1( get_sse_roundingmode() ) 
                ) 
              );
       goto decode_success;
index a024e56a8ad189bfb10baba5bab58bf10b7fcc53..026b5fd318a335c02cd0349b397c91a0c87e8312 100644 (file)
@@ -700,7 +700,7 @@ extern void ppIRJumpKind ( IRJumpKind );
    evaluation.
 */
 
-#define VEX_N_FXSTATE  5   /* enough for FSAVE/FRSTOR on x86 */
+#define VEX_N_FXSTATE  7   /* enough for FXSAVE/FXRSTOR on x86 */
 
 typedef
    enum {