]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
x86: make float to integer conversions observe the rounding mode in
authorJulian Seward <jseward@acm.org>
Wed, 8 Sep 2004 23:42:23 +0000 (23:42 +0000)
committerJulian Seward <jseward@acm.org>
Wed, 8 Sep 2004 23:42:23 +0000 (23:42 +0000)
effect.

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

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

index aa0e8584a52095a3a15ad26a5d75fbd6797dbb18..19c285cf1f9f145fd4f27e971df44d1f70741c88 100644 (file)
@@ -3100,6 +3100,33 @@ static void put_ftop ( IRExpr* e )
 }
 
 
+/* --------- Get/set the FPU control word. --------- */
+/* Note, IA32 has this as a 16-bit value, so fstcw/fldcw need to cast
+   to/from 16 bits.  Here we represent it in 32 bits. */
+static IRExpr* /* :: Ity_I32 */ get_fpucw ( void )
+{
+   return IRExpr_Get( OFFB_FPUCW, Ity_I32 );
+}
+
+static void put_fpucw ( IRExpr* /* :: Ity_I32 */ e )
+{
+   stmt( IRStmt_Put( OFFB_FPUCW, e ) );
+}
+
+
+/* --------- Get the FPU rounding mode from the CW. --------- */
+/* Produces a value in 0 .. 3, which is encoded as per the type
+   IRRoundingMode.  On IA32 the relevant value is precisely bits 11
+   and 10 of the control word.
+*/
+static IRExpr* /* :: Ity_I32 */ get_roundingmode ( void )
+{
+   return binop( Iop_And32, 
+                 binop(Iop_Shr32, get_fpucw(), mkU8(10)),
+                 mkU32(3) );
+}
+
+
 /* --------- Get/set FP register tag bytes. --------- */
 
 /* Given i, generate an expression which is the offset in the guest
@@ -3344,7 +3371,7 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta )
                DIP("fldF %s\n", dis_buf);
                fp_push();
                put_ST(0, unop(Iop_F32toF64,
-                              IRExpr_LDle(Ity_F32, mkexpr(addr))));
+                              loadLE(Ity_F32, mkexpr(addr))));
                break;
 
             case 3: /* FSTP single-real */
@@ -3353,13 +3380,14 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta )
               fp_pop();
                break;
 
-            case 5:
-               vex_printf("vex x86->IR: ignoring fldcw\n");
+            case 5: 
+               DIP("fldcw %s", dis_buf);
+               put_fpucw( unop(Iop_16Uto32, loadLE(Ity_I16, mkexpr(addr))) );
                break;
 
             case 7:
-               vex_printf("vex x86->IR: ignoring fstcw\n");
-               storeLE(mkexpr(addr), mkU16(0x037F));
+               DIP("fstcw %s", dis_buf);
+               storeLE(mkexpr(addr), unop(Iop_32to16, get_fpucw()));
                break;
 
             default:
@@ -3492,9 +3520,16 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta )
                               loadLE(Ity_I32, mkexpr(addr))));
                break;
 
+            case 2: /* FIST m32 */
+               DIP("fistl %s", dis_buf);
+               storeLE( mkexpr(addr), 
+                        binop(Iop_F64toI32, get_roundingmode(), get_ST(0)) );
+               break;
+
             case 3: /* FISTP m32 */
                DIP("fistpl %s", dis_buf);
-               storeLE(mkexpr(addr), unop(Iop_F64toI32,get_ST(0)));
+               storeLE( mkexpr(addr), 
+                        binop(Iop_F64toI32, get_roundingmode(), get_ST(0)) );
               fp_pop();
                break;
 
@@ -3610,8 +3645,9 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta )
 
             case 3: /* FISTP m16 */
                DIP("fistps %s", dis_buf);
-               storeLE(mkexpr(addr), unop(Iop_F64toI16,get_ST(0)));
-              fp_pop();
+               storeLE( mkexpr(addr), 
+                        binop(Iop_F64toI16, get_roundingmode(), get_ST(0)) );
+               fp_pop();
                break;
 
 #if 0
@@ -7089,13 +7125,11 @@ static UInt disInstr ( UInt delta, Bool* isEnd )
    case 0xB0: /* MOV imm,AL */
    case 0xB1: /* MOV imm,CL */
    case 0xB2: /* MOV imm,DL */
-#if 0
    case 0xB3: /* MOV imm,BL */
    case 0xB4: /* MOV imm,AH */
    case 0xB5: /* MOV imm,CH */
    case 0xB6: /* MOV imm,DH */
    case 0xB7: /* MOV imm,BH */
-#endif
       d32 = getIByte(delta); delta += 1;
       putIReg(1, opc-0xB0, mkU8(d32));
       DIP("movb $0x%x,%s\n", d32, nameIReg(1,opc-0xB0));
index e25cad21eee5cbc1866a44532fa1f805aba5a35a..b638b9e407197eee716792842473fa62a69142fa 100644 (file)
@@ -599,6 +599,14 @@ X86Instr* X86Instr_FpCMov ( X86CondCode cond, HReg src, HReg dst ) {
    vassert(cond != Xcc_ALWAYS);
    return i;
 }
+X86Instr* X86Instr_FpLdStCW ( Bool isLoad, X86AMode* addr )
+{
+   X86Instr* i            = LibVEX_Alloc(sizeof(X86Instr));
+   i->tag                 = Xin_FpLdStCW;
+   i->Xin.FpLdStCW.isLoad = isLoad;
+   i->Xin.FpLdStCW.addr   = addr;
+   return i;
+}
 
 
 void ppX86Instr ( X86Instr* i ) {
@@ -759,6 +767,10 @@ void ppX86Instr ( X86Instr* i ) {
          vex_printf(",");
          ppHRegX86(i->Xin.FpCMov.dst);
          return;
+      case Xin_FpLdStCW:
+         vex_printf(i->Xin.FpLdStCW.isLoad ? "fldcw " : "fstcw ");
+         ppX86AMode(i->Xin.FpLdStCW.addr);
+         return;
       default:
          vpanic("ppX86Instr");
    }
@@ -875,6 +887,9 @@ void getRegUsage_X86Instr (HRegUsage* u, X86Instr* i)
          addHRegUse(u, HRmRead, i->Xin.FpCMov.src);
          addHRegUse(u, HRmModify, i->Xin.FpCMov.dst);
          return;
+      case Xin_FpLdStCW:
+         addRegUsage_X86AMode(u, i->Xin.FpLdStCW.addr);
+         return;
       default:
          ppX86Instr(i);
          vpanic("getRegUsage_X86Instr");
@@ -963,6 +978,9 @@ void mapRegs_X86Instr (HRegRemap* m, X86Instr* i)
          mapReg(m, &i->Xin.FpCMov.src);
          mapReg(m, &i->Xin.FpCMov.dst);
          return;
+      case Xin_FpLdStCW:
+         mapRegs_X86AMode(m, i->Xin.FpLdStCW.addr);
+         return;
       default:
          ppX86Instr(i);
          vpanic("mapRegs_X86Instr");
@@ -1845,6 +1863,15 @@ Int emit_X86Instr ( UChar* buf, Int nbuf, X86Instr* i )
       *(ptmp-1) = p - ptmp;
       goto done;
 
+   case Xin_FpLdStCW:
+      if (i->Xin.FpLdStCW.isLoad) {
+         *p++ = 0xD9;
+         p = doAMode_M(p, fake(5)/*subopcode*/, i->Xin.FpLdStCW.addr);
+      } else {
+         vassert(0);
+      }
+      goto done;
+
    default: 
       goto bad;
    }
index ca4de1952f04904f00cb068b979ef18605786479..a657ec239575726c3e7899a2c21d69cacdacbdfc 100644 (file)
@@ -279,7 +279,8 @@ typedef
       Xin_FpBinary,  /* FP fake binary op */
       Xin_FpLdSt,    /* FP fake load/store */
       Xin_FpLdStI,   /* FP fake load/store, converting to/from Int */
-      Xin_FpCMov     /* FP fake floating point (un)conditional move */
+      Xin_FpCMov,    /* FP fake floating point (un)conditional move */
+      Xin_FpLdStCW   /* fldcw / fstcw */
    }
    X86InstrTag;
 
@@ -413,6 +414,12 @@ typedef
             HReg        src;
             HReg        dst;
          } FpCMov;
+         /* Load/store the FPU's 16-bit control word (fldcw/fstcw) */
+         struct {
+            Bool      isLoad;
+            X86AMode* addr;
+         }
+         FpLdStCW;
       } Xin;
    }
    X86Instr;
@@ -439,6 +446,8 @@ extern X86Instr* X86Instr_FpBinary ( X86FpOp op, HReg srcL, HReg srcR, HReg dst
 extern X86Instr* X86Instr_FpLdSt   ( Bool isLoad, UChar sz, HReg reg, X86AMode* );
 extern X86Instr* X86Instr_FpLdStI  ( Bool isLoad, UChar sz, HReg reg, X86AMode* );
 extern X86Instr* X86Instr_FpCMov   ( X86CondCode, HReg src, HReg dst );
+extern X86Instr* X86Instr_FpLdStCW ( Bool isLoad, X86AMode* );
+
 
 extern void ppX86Instr ( X86Instr* );
 
index 6d17bec07e719ad67adf7b639988476eccd94c41..65984edb4de068e44c6a3d9ec37a6a0104e9715b 100644 (file)
@@ -502,6 +502,66 @@ static HReg iselIntExpr_R_wrk ( ISelEnv* env, IRExpr* e )
          return b16;
       }
 
+      if (e->Iex.Binop.op == Iop_F64toI32 || e->Iex.Binop.op == Iop_F64toI16) {
+         Int  sz   = e->Iex.Binop.op == Iop_F64toI16 ? 2 : 4;
+         HReg rf   = iselDblExpr(env, e->Iex.Binop.arg2);
+         HReg rrm  = iselIntExpr_R(env, e->Iex.Binop.arg1);
+         HReg rrm2 = newVRegI(env);
+         HReg dst  = newVRegI(env);
+
+         /* Used several times ... */
+         X86AMode* zero_esp = X86AMode_IR(0, hregX86_ESP());
+
+        /* rf now holds the value to be converted, and rrm holds the
+           rounding mode value, encoded as per the IRRoundingMode
+           enum.  The first thing to do is set the FPU's rounding
+           mode accordingly. */
+
+         /* Create a space, both for the control word messing, and for
+           the actual store conversion.
+         /* subl $4, %esp */
+         addInstr(env, 
+                  X86Instr_Alu32R(Xalu_SUB, X86RMI_Imm(4), hregX86_ESP()));
+        /* movl %rrm, %rrm2
+            andl $3, %rrm2   -- shouldn't be needed; paranoia
+            shll $10, %rrm2
+            orl  $0x037F, %rrm2
+            movl %rrm2, 0(%esp)
+            fldcw 0(%esp)
+        */
+         addInstr(env, mk_MOVsd_RR(rrm, rrm2));
+        addInstr(env, X86Instr_Alu32R(Xalu_AND, X86RMI_Imm(3), rrm2));
+        addInstr(env, X86Instr_Sh32(Xsh_SHL, 10, X86RM_Reg(rrm2)));
+        addInstr(env, X86Instr_Alu32R(Xalu_OR, X86RMI_Imm(0x037F), rrm2));
+        addInstr(env, X86Instr_Alu32M(Xalu_MOV, X86RI_Reg(rrm2), zero_esp));
+        addInstr(env, X86Instr_FpLdStCW(True/*load*/, zero_esp));
+
+         /* gistw/l %rf, 0(%esp) */
+         addInstr(env, X86Instr_FpLdStI(False/*store*/, sz, rf, zero_esp));
+
+         if (sz == 2) {
+            /* movzwl 0(%esp), %dst */
+            addInstr(env, X86Instr_LoadEX(2,False,zero_esp,dst));
+        } else {
+            /* movl 0(%esp), %dst */
+            vassert(sz == 4);
+            addInstr(env, X86Instr_Alu32R(
+                             Xalu_MOV, X86RMI_Mem(zero_esp), dst));
+         }
+
+        /* Restore default FPU control.
+            movl $0x037F, 0(%esp)
+            fldcw 0(%esp)
+        */
+         addInstr(env, X86Instr_Alu32M(Xalu_MOV, X86RI_Imm(0x037F), zero_esp));
+        addInstr(env, X86Instr_FpLdStCW(True/*load*/, zero_esp));
+
+         /* addl $4, %esp */
+         addInstr(env, 
+                  X86Instr_Alu32R(Xalu_ADD, X86RMI_Imm(4), hregX86_ESP()));
+         return dst;
+      }
+
       break;
    }
 
@@ -611,39 +671,6 @@ static HReg iselIntExpr_R_wrk ( ISelEnv* env, IRExpr* e )
             return dst;
          }
 
-         case Iop_F64toI32:
-         case Iop_F64toI16: {
-            Int  sz  = e->Iex.Unop.op == Iop_F64toI16 ? 2 : 4;
-            HReg dst = newVRegI(env);
-            HReg rf  = iselDblExpr(env, e->Iex.Unop.arg);
-            /* subl $sz, %esp */
-            addInstr(env, X86Instr_Alu32R(Xalu_SUB,
-                                          X86RMI_Imm(sz),
-                                          hregX86_ESP()));
-            /* gistw/l %rf, 0(%esp) */
-            addInstr(env, X86Instr_FpLdStI(
-                             False/*store*/, sz, rf, 
-                             X86AMode_IR(0, hregX86_ESP())));
-            if (sz == 2) {
-               /* movzwl 0(%esp), %dst */
-               addInstr(env, X86Instr_LoadEX(
-                                2,False,
-                                X86AMode_IR(0, hregX86_ESP()),dst));
-           } else {
-               /* movl 0(%esp), %dst */
-               vassert(sz == 4);
-               addInstr(env, X86Instr_Alu32R(Xalu_MOV, 
-                                X86RMI_Mem(X86AMode_IR(0, hregX86_ESP())),
-                                dst));
-            }
-            /* addl $sz, %esp */
-            addInstr(env, X86Instr_Alu32R(Xalu_ADD,
-                                          X86RMI_Imm(sz),
-                                          hregX86_ESP()));
-            return dst;
-         }
-
-
          case Iop_16to8:
          case Iop_32to8:
          case Iop_32to16:
@@ -1012,8 +1039,8 @@ static X86CondCode iselCondCode_wrk ( ISelEnv* env, IRExpr* e )
 {
    MatchInfo mi;
    DECLARE_PATTERN(p_32to1);
-   DECLARE_PATTERN(p_eq32_literal);
-   DECLARE_PATTERN(p_ne32_zero);
+   //DECLARE_PATTERN(p_eq32_literal);
+   //DECLARE_PATTERN(p_ne32_zero);
    DECLARE_PATTERN(p_1Uto32_then_32to1);
 
    vassert(e);
index 4dfeeafb2afa5c408f44ceefb8de9d5c23e761ff..acda76ceadf9721cc77166ec90bde50b3d94f3da 100644 (file)
@@ -622,9 +622,12 @@ void typeOfPrimop ( IROp op, IRType* t_dst, IRType* t_arg1, IRType* t_arg2 )
       case Iop_AddF64: case Iop_SubF64: 
       case Iop_MulF64: case Iop_DivF64:
          BINARY(Ity_F64,Ity_F64,Ity_F64);
+
       case Iop_I32toF64: UNARY(Ity_F64,Ity_I32);
-      case Iop_F64toI32: UNARY(Ity_I32,Ity_F64);
-      case Iop_F64toI16: UNARY(Ity_I16,Ity_F64);
+
+      case Iop_F64toI32: BINARY(Ity_I32, Ity_I32,Ity_F64);
+      case Iop_F64toI16: BINARY(Ity_I16, Ity_I32,Ity_F64);
+
       case Iop_F32toF64: UNARY(Ity_F64,Ity_F32);
       case Iop_F64toF32: UNARY(Ity_F32,Ity_F64);
 
@@ -887,8 +890,14 @@ void tcExpr ( IRBB* bb, IRStmt* stmt, IRExpr* expr, IRType gWordTy )
          tcExpr(bb,stmt, expr->Iex.Binop.arg1, gWordTy );
          tcExpr(bb,stmt, expr->Iex.Binop.arg2, gWordTy );
          typeOfPrimop(expr->Iex.Binop.op, &t_dst, &t_arg1, &t_arg2);
-         if (t_arg1 == Ity_INVALID || t_arg2 == Ity_INVALID)
-            sanityCheckFail(bb,stmt,"Iex.Binop: wrong arity op");
+         if (t_arg1 == Ity_INVALID || t_arg2 == Ity_INVALID) {
+            vex_printf(" op name: " );
+            ppIROp(expr->Iex.Binop.op);
+            vex_printf("\n");
+            sanityCheckFail(bb,stmt,
+               "Iex.Binop: wrong arity op\n"
+               "... name of op precedes BB printout\n");
+         }
          ttarg1 = typeOfIRExpr(tyenv, expr->Iex.Binop.arg1);
          ttarg2 = typeOfIRExpr(tyenv, expr->Iex.Binop.arg2);
          if (t_arg1 != ttarg1 || t_arg2 != ttarg2) {
index 47750774290a00867690b0fe167537f0bdf13ed5..15d8f293747d7cdd70253c93cff7f9664eb73973 100644 (file)
@@ -74,6 +74,25 @@ extern void ppIRTemp ( IRTemp );
 
 /* ------------------ Binary and unary ops ------------------ */
 
+/* Encoding of rounding modes in Float -> Int conversions.  This is
+   the same as the encoding used by Intel IA32 to indicate x87
+   rounding mode. */
+typedef
+   enum { Irrm_NEAREST=0, Irrm_NegINF=1, Irrm_PosINF=2, Irrm_ZERO=3 }
+   IRRoundingMode;
+
+/* Floating point comparison result values, as created by Iop_CmpF64.
+   This is also derived from what IA32 does. */
+typedef
+   enum {
+      Ircr_UN = 0x85,
+      Ircr_LT = 0x01,
+      Ircr_GT = 0x00,
+      Ircr_EQ = 0x80
+   }
+   IRCmpF64Result;
+
+
 typedef
    enum { 
       /* Do not change this ordering.  The IR generators
@@ -98,15 +117,21 @@ typedef
       /* Widening multiplies */
       Iop_MullS8, Iop_MullS16, Iop_MullS32,
       Iop_MullU8, Iop_MullU16, Iop_MullU32,
+
       /* Wierdo integer stuff */
       Iop_Clz32,   /* count leading zeroes */
       Iop_Ctz32,   /* count trailing zeros */
+      /* Ctz32/Clz32 are UNDEFINED when given arguments of zero.
+         You must ensure they are never given a zero argument. 
+      */
+
       /* Ordering not important after here. */
       Iop_CmpLT32S,
       Iop_CmpLE32S,
       Iop_CmpLT32U,
       Iop_CmpLE32U,
       /* Division */
+      /* TODO: clarify semantics wrt rounding, negative values, whatever */
       Iop_DivModU64to32, // :: I64,I32 -> I64
                          // of which lo half is div and hi half is mod
       Iop_DivModS64to32, // ditto, signed
@@ -131,21 +156,55 @@ typedef
       Iop_32to1,  /* :: Ity_I32 -> Ity_Bit, just select bit[0] */
       Iop_1Uto8,  /* :: Ity_Bit -> Ity_I8, unsigned widen */
       Iop_1Uto32, /* :: Ity_Bit -> Ity_I32, unsigned widen */
-      /* FP stuff */
-      Iop_AddF64, Iop_SubF64, Iop_MulF64, Iop_DivF64,
-      Iop_SqrtF64,
-      /* double <-> int */
-      Iop_I32toF64, 
-      Iop_F64toI32, Iop_F64toI16,
-      /* double <-> float */
+
+      /* ------ Floating point.  We try and be IEEE754 compliant. ------ */
+
+      /* Simple operations */
+      Iop_AddF64, Iop_SubF64, Iop_MulF64, Iop_DivF64, Iop_RemF64,
+      Iop_SqrtF64, Iop_SinF64, Iop_CosF64,
+
+      /* Comparison, yielding GT/LT/EQ/UN(ordered), as per the following:
+            0x85 Unordered
+            0x01 LT
+            0x00 GT
+            0x80 EQ
+         This just happens to be the Intel encoding.  The values
+         are recorded in the type IRCmpF64Result.
+      */
+      Iop_CmpF64,
+
+      /* int -> double */
+      Iop_I32toF64, Iop_I64toF64,
+
+      /* double -> int.  These take a first argument :: Ity_I32 
+         (an IRRoundingMode) which is an indication of the rounding mode,
+         as per the following encoding:
+            00b  to nearest (the default)
+            01b  to -infinity
+            10b  to +infinity
+            11b  to zero
+         This just happens to be the Intel encoding.  For reference only,
+         the PPC encoding is:
+            00b  to nearest (the default)
+            01b  to zero
+            10b  to +infinity
+            11b  to -infinity
+         Any PPC -> IR front end will have to translate these PPC
+         encodings to the standard encodings.
+
+         If one of these conversions gets an out-of-range condition,
+         or a NaN, as an argument, the result is host-defined.  On x86
+         the "integer indefinite" value 0x80..00 is produced.
+         On PPC it is either 0x80..00 or 0x7F..FF depending on the sign
+         of the argument.
+      */
+      Iop_F64toI64, Iop_F64toI32, Iop_F64toI16,
+
+      /* double <-> float.  What does this mean -- does it round? */
       Iop_F32toF64, Iop_F64toF32
    }
    IROp;
 
-/* Notes.
-    Ctz32/Clz32 are UNDEFINED when given arguments of zero.
-    You must ensure they are never given a zero argument. 
-*/
 
 extern void ppIROp ( IROp );