]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Front end stuff to support floating point on x86 (guest).
authorJulian Seward <jseward@acm.org>
Fri, 27 Aug 2004 12:00:18 +0000 (12:00 +0000)
committerJulian Seward <jseward@acm.org>
Fri, 27 Aug 2004 12:00:18 +0000 (12:00 +0000)
git-svn-id: svn://svn.valgrind.org/vex/trunk@220

VEX/priv/guest-x86/gdefs.h
VEX/priv/guest-x86/toIR.c
VEX/priv/host-x86/hdefs.c
VEX/priv/ir/irdefs.c
VEX/pub/libvex_ir.h

index a2cb0b1bfba11384b9fb4782fa5156278d3be496..6ece7e9caa73fa308d4c113bb4c978c98084415d 100644 (file)
@@ -165,14 +165,14 @@ typedef
 /* EIP */
 #define OFFB_EIP     (12*4)
 
-/* FPU.  For now, just simulate 8 64-bit registers and the reg-stack
-   top pointer, of which only the least significant three bits are
-   relevant.
+/* FPU.  For now, just simulate 8 64-bit registers, their tags, and
+   the reg-stack top pointer, of which only the least significant
+   three bits are relevant.
 
    The model is:
-     F0 .. F7 are the 8 registers.  ftop[2:0] contains the 
+     F0 .. F7 are the 8 registers.  FTOP[2:0] contains the 
      index of the current 'stack top' -- pretty meaningless, but
-     still.  
+     still.  FTOP is a 32-bit value.
 
      When a value is pushed onto the stack, ftop is first replaced by 
      (ftop-1) & 7, and then F[ftop] is assigned the value.
@@ -183,28 +183,41 @@ typedef
      In general, a reference to a register ST(i) actually references
      F[ (ftop+i) & 7 ].
 
-   There should be an array of 8 booleans corresponding to F0 .. F7,
-   indicating whether the corresponding F reg contains a value or not.
+   FTAG0 .. FTAG0+7 are the tags.  Each is a byte, zero means empty,
+   non-zero means non-empty.
 
-   A read of an F reg marked empty, for any reason, elicits a stack
-   underflow fault.
+   The general rule appears to be that a read or modify of a register
+   gets a stack underflow fault if the register is empty.  A write of
+   a register (only a write, not a modify) gets a stack overflow fault
+   if the register is full.  Note that "over" vs "under" is pretty
+   meaningless since the FP stack pointer can move around arbitrarily,
+   so it's really just two different kinds of exceptions:
+   register-empty and register full.
+
+   Naturally Intel (in its infinite wisdom) has seen fit to throw in
+   some ad-hoc inconsistencies to the fault-generation rules of the
+   above para, just to complicate everything.  Known inconsistencies:
+
+   * fxam can read a register in any state without taking an underflow
+     fault.
+
+   * fst from st(0) to st(i) does not take an overflow fault even if the
+     destination is already full.
 
-   A load from memory into an F reg marked full elicits a stack overflow
-   fault.  This appears to be the only way a stack overflow fault can
-   happen.
 */
-#define OFFB_F0      (13*4)
-#define OFFB_F1      (15*4)
-#define OFFB_F2      (17*4)
-#define OFFB_F3      (19*4)
-#define OFFB_F4      (21*4)
-#define OFFB_F5      (23*4)
-#define OFFB_F6      (25*4)
-#define OFFB_F7      (27*4)
-#define OFFB_FTOP    (29*4)
+#define OFFB_FTOP    (13*4)
+#define OFFB_F0      (14*4)
+#define OFFB_F1      (16*4)
+#define OFFB_F2      (18*4)
+#define OFFB_F3      (20*4)
+#define OFFB_F4      (22*4)
+#define OFFB_F5      (24*4)
+#define OFFB_F6      (26*4)
+#define OFFB_F7      (28*4)
+#define OFFB_FTAG0   (30*4) // up to 30*4 + 7
 
 /* Don't forget to keep this up to date. */
-#define SIZEOF_X86H_STATE  OFFB_FTOP
+#define SIZEOF_X86H_STATE  (OFFB_FTAG0 + 8)
 
 
 
index 5846eae329dc3eeec2ac428934f24dc396e28d1d..9e57e779942bba7f58909f3d640f5006891afe33 100644 (file)
@@ -3078,82 +3078,182 @@ UInt dis_imul_I_E_G ( UChar       sorb,
 /*--- x87 floating point insns.                            ---*/
 /*------------------------------------------------------------*/
 
-/* Get/set the top-of-stack pointer. */
+/* --- Helper functions for dealing with the register stack. --- */
+
+/* --- Produce an IRExpr* denoting a 64-bit NaN. --- */
+
+static IRExpr* mkNaN64 ( void )
+{
+   return IRExpr_Const(IRConst_NaN64());
+}
+
+/* --------- Get/set the top-of-stack pointer. --------- */
 
 static IRExpr* get_ftop ( void )
 {
    return IRExpr_Get( OFFB_FTOP, Ity_I32 );
 }
 
-static IRStmt* put_ftop ( IRExpr* e )
+static void put_ftop ( IRExpr* e )
+{
+   stmt( IRStmt_Put( OFFB_FTOP, e ) );
+}
+
+
+/* --------- Get/set FP register tag bytes. --------- */
+
+/* Given i, generate an expression which is the offset in the guest
+   state of ST(i)'s tag byte, considering the current value of FTOP.  The 
+   generated expression is:
+
+      ((Get(OFFB_FTOP) + i) & 7) + OFFB_FTAGO
+*/
+static IRExpr* off_ST_TAG ( Int i )
+{
+  vassert(i >= 0 && i <= 7);
+  return 
+     binop(Iop_Add32,
+           binop(Iop_And32, 
+                 binop(Iop_Add32, get_ftop(), mkU32(i)),
+                 mkU32(7)),
+           mkU32(OFFB_FTAG0)
+     );
+}
+
+/* Given i, and some expression e, generate 'ST_TAG(i) = e'. */
+
+static void put_ST_TAG ( Int i, IRExpr* value )
+{
+   vassert(typeOfIRExpr(irbb->tyenv, value) == Ity_I8);
+   stmt(
+       IRStmt_PutI( off_ST_TAG(i), value, OFFB_FTAG0, OFFB_FTAG0 +7 )
+   );
+}
+
+/* Given i, generate an expression yielding 'ST_TAG(i)'.  This will be
+   zero to indicate "Empty" and nonzero and indicate "NonEmpty".  */
+
+static IRExpr* get_ST_TAG ( Int i )
 {
-   return IRStmt_Put( OFFB_FTOP, e );
+   return
+      IRExpr_GetI( off_ST_TAG(i), Ity_I8, OFFB_FTAG0, OFFB_FTAG0 +7 );
 }
 
+
+/* --------- Get/set FP registers. --------- */
+
 /* Given i, generate an expression which is the offset in the guest
-   state of ST(i), considering the current value of FTOP. */
+   state of ST(i), considering the current value of FTOP.  The 
+   generated expression is:
 
+      (((Get(OFFB_FTOP) + i) & 7) << 3) + OFFB_FO
+*/
 static IRExpr* off_ST ( Int i )
 {
   vassert(i >= 0 && i <= 7);
   return 
      binop(Iop_Add32,
-           binop(Iop_Mul32,
+           binop(Iop_Shl32,
                  binop(Iop_And32, 
                        binop(Iop_Add32, get_ftop(), mkU32(i)),
                        mkU32(7)),
-                 mkU32(8)),
+                 mkU8(3)),
            mkU32(OFFB_F0)
      );
 }
 
-/* Given i, and some expression e, generate 'ST(i) = e'. */
 
-static IRStmt* put_ST ( Int i, IRExpr* value )
+/* Given i, and some expression e, emit 'ST(i) = e'
+and set the register's tag to indicate the register is full.
+The previous state of the register is not checked. */
+
+static void put_ST_UNCHECKED ( Int i, IRExpr* value )
 {
-   return
-      IRStmt_PutI( off_ST(i), value, OFFB_F0, OFFB_F7+8-1 );
+   stmt( IRStmt_PutI( off_ST(i), value, OFFB_F0, OFFB_F7+8-1 ) );
+   /* Mark the register as in-use. */
+   put_ST_TAG(i, mkU8(1));
 }
 
+/* Given i, and some expression e, emit
+      ST(i) = is_full(i) ? NaN : e
+   and set the tag accordingly.
+*/
+
+static void put_ST ( Int i, IRExpr* value )
+{
+   put_ST_UNCHECKED( i,
+                     IRExpr_Mux0X(get_ST_TAG(i),
+                        /* 0 means empty */
+                        value,
+                        /* non-0 means full */
+                        mkNaN64()
+                   )
+   );
+}
+
+
 /* Given i, generate an expression yielding 'ST(i)'. */
 
-static IRExpr* get_ST ( Int i )
+static IRExpr* get_ST_UNCHECKED ( Int i )
 {
    return
       IRExpr_GetI( off_ST(i), Ity_F64, OFFB_F0, OFFB_F7+8-1 );
 }
 
-/* Adjust FTOP downwards by one register. */
+/* Given i, generate an expression yielding 
+  is_full(i) ? ST(i) : NaN
+*/
 
-static IRStmt* fp_push ( void )
+static IRExpr* get_ST ( Int i )
 {
    return
-      put_ftop(
-         binop(Iop_And32,
-               binop(Iop_Sub32, get_ftop(), mkU32(1)),
-              mkU32(7))
-      );
+      IRExpr_Mux0X( get_ST_TAG(i),
+                    /* 0 means empty */
+                    mkNaN64(),
+                    /* non-0 means full */
+                    get_ST_UNCHECKED(i));
 }
 
-/* Adjust FTOP upwards by one register. */
 
-static IRStmt* fp_pop ( void )
+/* Adjust FTOP downwards by one register. */
+
+static void fp_push ( void )
 {
-   return
-      put_ftop(
-         binop(Iop_And32,
-               binop(Iop_Add32, get_ftop(), mkU32(1)),
-              mkU32(7))
-      );
+   put_ftop(
+      binop(Iop_And32,
+            binop(Iop_Sub32, get_ftop(), mkU32(1)),
+            mkU32(7))
+   );
 }
 
+/* Adjust FTOP upwards by one register, and mark the vacated register
+   as empty.  */
+
+static void fp_pop ( void )
+{
+   put_ST_TAG(0, mkU8(1));
+   put_ftop(
+      binop(Iop_And32,
+            binop(Iop_Add32, get_ftop(), mkU32(1)),
+            mkU32(7))
+   );
+}
+
+/* ------------------------------------------------------- */
+/* Given all that stack-mangling junk, we can now go ahead
+   and describe FP instructions. 
+*/
+
+/* ST(0) = ST(0) `op` mem64(addr)       (when dbl==True) 
+   Need to check ST(0)'s tag on read, but not on write.
+*/
 static
 void fp_do_op_mem_ST_0 ( IRTemp addr, UChar* op_txt, UChar* dis_buf, 
                          IROp op, Bool dbl )
 {
    DIP("f%s%c %s", op_txt, dbl?'l':'s', dis_buf);
    if (dbl) {
-      stmt( put_ST(0, binop(op, get_ST(0), loadLE(Ity_F64,mkexpr(addr)))));
+      put_ST_UNCHECKED(0, binop(op, get_ST(0), loadLE(Ity_F64,mkexpr(addr))));
    } else {
       vassert(0);
    }
@@ -3192,14 +3292,14 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta )
                DIP("fld %%st(%d)\n", r_src);
               t1 = newTemp(Ity_F64);
               assign(t1, get_ST(r_src));
-              stmt( fp_push() );
-              stmt( put_ST(0, mkexpr(t1)) );
+              fp_push();
+              put_ST(0, mkexpr(t1));
                break;
 
             case 0xEE: /* FLDZ */
                DIP("fldz");
-               stmt( fp_push() );
-               stmt( put_ST(0, IRExpr_Const(IRConst_F64(0.0))) );
+               fp_push();
+               put_ST(0, IRExpr_Const(IRConst_F64(0.0)));
                break;
 
             default:
@@ -3220,14 +3320,14 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta )
          delta += len;
 
          switch (gregOfRM(modrm)) {
-            case 1: /* FIMUL m32int */
+            case 1: /* FIMUL m32int */ /* ST(0) *= m32int */
                DIP("fimull %s", dis_buf);
-               stmt( put_ST(0, 
-                        binop(Iop_MulF64, 
-                              get_ST(0),
-                              unop(Iop_I64toF64,
-                                   unop(Iop_32Sto64,
-                                        loadLE(Ity_I32, mkexpr(addr)))))));
+               put_ST_UNCHECKED(0, 
+                  binop(Iop_MulF64, 
+                        get_ST(0),
+                        unop(Iop_I64toF64,
+                             unop(Iop_32Sto64,
+                                  loadLE(Ity_I32, mkexpr(addr))))));
                break;
 
             default:
@@ -3288,8 +3388,8 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta )
 
             case 0: /* FLD double-real */
                DIP("fldD %s\n", dis_buf);
-               stmt( fp_push() );
-               stmt( put_ST(0, IRExpr_LDle(Ity_F64, mkexpr(addr))) );
+               fp_push();
+               put_ST(0, IRExpr_LDle(Ity_F64, mkexpr(addr)));
                break;
 
 #if 0
@@ -3307,7 +3407,7 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta )
             case 3: /* FSTP double-real */
                DIP("fstpD %s", dis_buf);
               storeLE(mkexpr(addr), get_ST(0));
-              stmt( fp_pop() );
+              fp_pop();
                break;
 
             default:
@@ -3321,8 +3421,11 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta )
             case 0xD8 ... 0xDF: /* FSTP %st(0),%st(?) */
                r_dst = (UInt)modrm - 0xD8;
                DIP("fstp %%st(0),%%st(%d)\n", r_dst);
-               stmt( put_ST(r_dst, get_ST(0)) );
-               stmt( fp_pop() );
+              /* P4 manual says: "If the destination operand is a
+                  non-empty register, the invalid-operation exception
+                  is not generated.  Hence put_ST_UNCHECKED. */
+               put_ST_UNCHECKED(r_dst, get_ST(0));
+               fp_pop();
                break;
            default:
                goto decode_fail;
index 00676ceba288951f5b363abab8eedd5929023146..ef53f788090e80c939f0d32fb30f3a392c787500 100644 (file)
@@ -958,7 +958,7 @@ Bool isMove_X86Instr ( X86Instr* i, HReg* src, HReg* dst )
 
 X86Instr* genSpill_X86 ( HReg rreg, Int offset )
 {
-   Int base = 4 * 51;
+   Int base = 4 * 53;
    vassert(offset >= 0);
    vassert(offset <= 4*(100-1));
    vassert(!hregIsVirtual(rreg));
@@ -976,7 +976,7 @@ X86Instr* genSpill_X86 ( HReg rreg, Int offset )
 
 X86Instr* genReload_X86 ( HReg rreg, Int offset )
 {
-   Int base = 4 * 51;
+   Int base = 4 * 53;
    vassert(offset >= 0);
    vassert(offset <= 4*(100-1));
    vassert(!hregIsVirtual(rreg));
index 2efb84440c946b1d82994f8cce7335b2d671b200..accf6f7a13873cc40a955a939f6ab667a5990df6 100644 (file)
@@ -36,12 +36,13 @@ void ppIRType ( IRType ty )
 void ppIRConst ( IRConst* con )
 {
   switch (con->tag) {
-    case Ico_Bit: vex_printf( "%d:Bit",     con->Ico.Bit ? 1 : 0); break;
-    case Ico_U8:  vex_printf( "0x%x:I8",    (UInt)(con->Ico.U8)); break;
-    case Ico_U16: vex_printf( "0x%x:I16",   (UInt)(con->Ico.U16)); break;
-    case Ico_U32: vex_printf( "0x%x:I32",   (UInt)(con->Ico.U32)); break;
-    case Ico_U64: vex_printf( "0x%llx:I64", (ULong)(con->Ico.U64)); break;
-    case Ico_F64: vex_printf("(f64 value)"); break;
+    case Ico_Bit:   vex_printf( "%d:Bit",     con->Ico.Bit ? 1 : 0); break;
+    case Ico_U8:    vex_printf( "0x%x:I8",    (UInt)(con->Ico.U8)); break;
+    case Ico_U16:   vex_printf( "0x%x:I16",   (UInt)(con->Ico.U16)); break;
+    case Ico_U32:   vex_printf( "0x%x:I32",   (UInt)(con->Ico.U32)); break;
+    case Ico_U64:   vex_printf( "0x%llx:I64", (ULong)(con->Ico.U64)); break;
+    case Ico_F64:   vex_printf( "(f64 value)"); break;
+    case Ico_NaN64: vex_printf( "NaN:F64"); break;
     default: vpanic("ppIRConst");
   }
 }
@@ -348,6 +349,13 @@ IRConst* IRConst_F64 ( Double f64 )
    c->Ico.F64 = f64;
    return c;
 }
+IRConst* IRConst_NaN64 ( void )
+{
+   IRConst* c = LibVEX_Alloc(sizeof(IRConst));
+   c->tag     = Ico_NaN64;
+   return c;
+}
+
 
 /* Constructors -- IRExpr */
 
@@ -684,12 +692,13 @@ IRType lookupIRTypeEnv ( IRTypeEnv* env, IRTemp tmp )
 IRType typeOfIRConst ( IRConst* con )
 {
    switch (con->tag) {
-      case Ico_Bit: return Ity_Bit;
-      case Ico_U8:  return Ity_I8;
-      case Ico_U16: return Ity_I16;
-      case Ico_U32: return Ity_I32;
-      case Ico_U64: return Ity_I64;
-      case Ico_F64: return Ity_F64;
+      case Ico_Bit:   return Ity_Bit;
+      case Ico_U8:    return Ity_I8;
+      case Ico_U16:   return Ity_I16;
+      case Ico_U32:   return Ity_I32;
+      case Ico_U64:   return Ity_I64;
+      case Ico_F64:   return Ity_F64;
+      case Ico_NaN64: return Ity_F64;
       default: vpanic("typeOfIRConst");
    }
 }
index c048144d7d1c4f894aa9fc716d1975c31a9af347..6e211d8e08f8c9a95130f40efbff98c736984158 100644 (file)
@@ -33,7 +33,9 @@ extern void ppIRType ( IRType );
 
 typedef
    enum { Ico_Bit=0x12000,
-          Ico_U8, Ico_U16, Ico_U32, Ico_U64, Ico_F64 }
+          Ico_U8, Ico_U16, Ico_U32, Ico_U64, Ico_F64,
+          Ico_NaN64 /* 64-bit IEEE NaN. */
+   }
    IRConstTag;
 
 typedef
@@ -50,12 +52,13 @@ typedef
    }
    IRConst;
 
-extern IRConst* IRConst_Bit ( Bool );
-extern IRConst* IRConst_U8  ( UChar );
-extern IRConst* IRConst_U16 ( UShort );
-extern IRConst* IRConst_U32 ( UInt );
-extern IRConst* IRConst_U64 ( ULong );
-extern IRConst* IRConst_F64 ( Double );
+extern IRConst* IRConst_Bit   ( Bool );
+extern IRConst* IRConst_U8    ( UChar );
+extern IRConst* IRConst_U16   ( UShort );
+extern IRConst* IRConst_U32   ( UInt );
+extern IRConst* IRConst_U64   ( ULong );
+extern IRConst* IRConst_F64   ( Double );
+extern IRConst* IRConst_NaN64 ( void );
 
 extern void ppIRConst ( IRConst* );