]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Rationalisation/cleanup of float to/from int conversions and rounding
authorJulian Seward <jseward@acm.org>
Sat, 4 Dec 2004 14:36:09 +0000 (14:36 +0000)
committerJulian Seward <jseward@acm.org>
Sat, 4 Dec 2004 14:36:09 +0000 (14:36 +0000)
modes associated with them.

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

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 b2a98d8ce688a4fadd0b2dc4c98938921b8cc530..1f3192113a0021e1e336d14aabb7b02333148bb6 100644 (file)
@@ -3916,12 +3916,14 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta )
 
             case 2: /* FST single-real */
                DIP("fstS %s", dis_buf);
-               storeLE(mkexpr(addr), unop(Iop_F64toF32, get_ST(0)));
+               storeLE(mkexpr(addr),
+                       binop(Iop_F64toF32, get_roundingmode(), get_ST(0)));
                break;
 
             case 3: /* FSTP single-real */
                DIP("fstpS %s", dis_buf);
-               storeLE(mkexpr(addr), unop(Iop_F64toF32, get_ST(0)));
+               storeLE(mkexpr(addr), 
+                       binop(Iop_F64toF32, get_roundingmode(), get_ST(0)));
                fp_pop();
                break;
 
@@ -7230,16 +7232,18 @@ static DisResult disInstr ( /*IN*/  Bool    resteerOK,
       assign( rmode, get_sse_roundingmode() );
 
       putXMMRegLane32F( 
-         gregOfRM(modrm), 0, 
-         binop(Iop_I32toF32, 
+         gregOfRM(modrm), 0,
+         binop(Iop_F64toF32, 
                mkexpr(rmode),
-               unop(Iop_64to32, mkexpr(arg64)) ) );
+               unop(Iop_I32toF64, 
+                    unop(Iop_64to32, mkexpr(arg64)) )) );
 
       putXMMRegLane32F(
          gregOfRM(modrm), 1, 
-         binop(Iop_I32toF32,
+         binop(Iop_F64toF32, 
                mkexpr(rmode),
-               unop(Iop_64HIto32, mkexpr(arg64)) ) );
+               unop(Iop_I32toF64,
+                    unop(Iop_64HIto32, mkexpr(arg64)) )) );
 
       goto decode_success;
    }
@@ -7269,8 +7273,10 @@ static DisResult disInstr ( /*IN*/  Bool    resteerOK,
       assign( rmode, get_sse_roundingmode() );
 
       putXMMRegLane32F( 
-         gregOfRM(modrm), 0, 
-         binop(Iop_I32toF32, mkexpr(rmode), mkexpr(arg32)) );
+         gregOfRM(modrm), 0,
+         binop(Iop_F64toF32,
+               mkexpr(rmode),
+               unop(Iop_I32toF64, mkexpr(arg32)) ) );
 
       goto decode_success;
    }
@@ -7569,6 +7575,11 @@ static DisResult disInstr ( /*IN*/  Bool    resteerOK,
       goto decode_success;
    }
 
+   /* ***--- this is an MMX class insn introduced in SSE1 ---*** */
+   /* 0F E0 = PAVGB -- 8x8 unsigned Packed Average, with rounding */
+   if (insn[0] == 0x0F && insn[1] == 0x56) {
+   }
+
 //-- 
 //--    /* FXSAVE/FXRSTOR m32 -- load/store the FPU/MMX/SSE state. */
 //--    if (insn[0] == 0x0F && insn[1] == 0xAE 
index 2e7f854fe1dad1273fc74755f3e2a5f06230d66f..7ee3443974a03536082fd2eea5e70fc61534f017 100644 (file)
@@ -689,6 +689,13 @@ X86Instr* X86Instr_FpLdStI ( Bool isLoad, UChar sz,
    vassert(sz == 2 || sz == 4 || sz == 8);
    return i;
 }
+X86Instr* X86Instr_Fp64to32 ( HReg src, HReg dst ) {
+   X86Instr* i         = LibVEX_Alloc(sizeof(X86Instr));
+   i->tag              = Xin_Fp64to32;
+   i->Xin.Fp64to32.src = src;
+   i->Xin.Fp64to32.dst = dst;
+   return i;
+}
 X86Instr* X86Instr_FpCMov ( X86CondCode cond, HReg src, HReg dst ) {
    X86Instr* i        = LibVEX_Alloc(sizeof(X86Instr));
    i->tag             = Xin_FpCMov;
@@ -916,6 +923,12 @@ void ppX86Instr ( X86Instr* i ) {
             ppX86AMode(i->Xin.FpLdStI.addr);
          }
          return;
+      case Xin_Fp64to32:
+         vex_printf("gdtof ");
+         ppHRegX86(i->Xin.Fp64to32.src);
+         vex_printf(",");
+         ppHRegX86(i->Xin.Fp64to32.dst);
+         return;
       case Xin_FpCMov:
          vex_printf("gcmov%s ", showX86CondCode(i->Xin.FpCMov.cond));
          ppHRegX86(i->Xin.FpCMov.src);
@@ -1110,6 +1123,10 @@ void getRegUsage_X86Instr (HRegUsage* u, X86Instr* i)
          addHRegUse(u, i->Xin.FpLdStI.isLoad ? HRmWrite : HRmRead,
                        i->Xin.FpLdStI.reg);
          return;
+      case Xin_Fp64to32:
+         addHRegUse(u, HRmRead,  i->Xin.Fp64to32.src);
+         addHRegUse(u, HRmWrite, i->Xin.Fp64to32.dst);
+         return;
       case Xin_FpCMov:
          addHRegUse(u, HRmRead, i->Xin.FpCMov.src);
          addHRegUse(u, HRmModify, i->Xin.FpCMov.dst);
@@ -1236,6 +1253,10 @@ void mapRegs_X86Instr (HRegRemap* m, X86Instr* i)
          mapRegs_X86AMode(m, i->Xin.FpLdStI.addr);
          mapReg(m, &i->Xin.FpLdStI.reg);
          return;
+      case Xin_Fp64to32:
+         mapReg(m, &i->Xin.Fp64to32.src);
+         mapReg(m, &i->Xin.Fp64to32.dst);
+         return;
       case Xin_FpCMov:
          mapReg(m, &i->Xin.FpCMov.src);
          mapReg(m, &i->Xin.FpCMov.dst);
@@ -2217,11 +2238,13 @@ Int emit_X86Instr ( UChar* buf, Int nbuf, X86Instr* i )
       */
       p = do_ffree_st7(p);
       p = do_fld_st(p, 0+hregNumber(i->Xin.FpBinary.srcL));
-      p = do_fop2_st(p, i->Xin.FpBinary.op, 1+hregNumber(i->Xin.FpBinary.srcR));
+      p = do_fop2_st(p, i->Xin.FpBinary.op, 
+                        1+hregNumber(i->Xin.FpBinary.srcR));
       p = do_fstp_st(p, 1+hregNumber(i->Xin.FpBinary.dst));
       goto done;
 
    case Xin_FpLdSt:
+      vassert(i->Xin.FpLdSt.sz == 4 || i->Xin.FpLdSt.sz == 8);
       if (i->Xin.FpLdSt.isLoad) {
          /* Load from memory into %fakeN.  
             --> ffree %st(7) ; fld{s/l} amode ; fstp st(N+1) 
@@ -2277,6 +2300,22 @@ Int emit_X86Instr ( UChar* buf, Int nbuf, X86Instr* i )
       }
       break;
 
+   case Xin_Fp64to32:
+      /* ffree %st7 ; fld %st(src) */
+      p = do_ffree_st7(p);
+      p = do_fld_st(p, 0+fregNo(i->Xin.Fp64to32.src));
+      /* subl $4, %esp */
+      *p++ = 0x83; *p++ = 0xEC; *p++ = 0x04;
+      /* fstps (%esp) */
+      *p++ = 0xD9; *p++ = 0x1C; *p++ = 0x24;
+      /* flds (%esp) */
+      *p++ = 0xD9; *p++ = 0x04; *p++ = 0x24;
+      /* addl $4, %esp */
+      *p++ = 0x83; *p++ = 0xC4; *p++ = 0x04;
+      /* fstp %st(1+dst) */
+      p = do_fstp_st(p, 1+fregNo(i->Xin.Fp64to32.dst));
+      goto done;
+
    case Xin_FpCMov:
       /* jmp fwds if !condition */
       *p++ = 0x70 + (i->Xin.FpCMov.cond ^ 1);
index c1ba6c2fa438d93d1374eb9331759be9b2ce69de..3e143cc6e220a52386e50908035bf218f77317d5 100644 (file)
@@ -341,6 +341,7 @@ typedef
       Xin_FpBinary,  /* FP fake binary op */
       Xin_FpLdSt,    /* FP fake load/store */
       Xin_FpLdStI,   /* FP fake load/store, converting to/from Int */
+      Xin_Fp64to32,  /* FP round IEEE754 double to IEEE754 single */
       Xin_FpCMov,    /* FP fake floating point (un)conditional move */
       Xin_FpLdStCW,  /* fldcw / fstcw */
       Xin_FpStSW_AX, /* fstsw %ax */
@@ -478,13 +479,21 @@ typedef
             X86AMode* addr;
          } FpLdSt;
          /* Move 64-bit float to/from memory, converting to/from
-           signed int on the way. */
+            signed int on the way.  Note the conversions will observe
+            the host FPU rounding mode currently in force. */
          struct {
             Bool      isLoad;
             UChar     sz; /* only 2, 4 or 8 */
             HReg      reg;
             X86AMode* addr;
          } FpLdStI;
+         /* By observing the current FPU rounding mode, round (etc)
+            src into dst given that dst should be interpreted as an
+            IEEE754 32-bit (float) type. */
+         struct {
+            HReg src;
+            HReg dst;
+         } Fp64to32;
          /* Mov src to dst on the given condition, which may not
             be the bogus Xcc_ALWAYS. */
          struct {
@@ -562,6 +571,7 @@ extern X86Instr* X86Instr_FpUnary   ( X86FpOp op, HReg src, HReg dst );
 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_Fp64to32  ( HReg src, HReg dst );
 extern X86Instr* X86Instr_FpCMov    ( X86CondCode, HReg src, HReg dst );
 extern X86Instr* X86Instr_FpLdStCW  ( Bool isLoad, X86AMode* );
 extern X86Instr* X86Instr_FpStSW_AX ( void );
index 0347d14d17ee1c3b7f1e89fbc9d351882f11aa8f..9f04acea2810b7747c1bd50fec06d3f02b2053c2 100644 (file)
@@ -961,7 +961,7 @@ static HReg iselIntExpr_R_wrk ( ISelEnv* env, IRExpr* e )
          if (sz == 2) {
             /* movzwl 0(%esp), %dst */
             addInstr(env, X86Instr_LoadEX(2,False,zero_esp,dst));
-        } else {
+         } else {
             /* movl 0(%esp), %dst */
             vassert(sz == 4);
             addInstr(env, X86Instr_Alu32R(
@@ -1888,6 +1888,8 @@ static void iselInt64Expr_wrk ( HReg* rHi, HReg* rLo, ISelEnv* env, IRExpr* e )
       HReg tHi  = newVRegI(env);
       X86AMode* zero_esp = X86AMode_IR(0, hregX86_ESP());
       X86AMode* four_esp = X86AMode_IR(4, hregX86_ESP());
+      /* paranoia */
+      set_FPU_rounding_default(env);
       /* subl $8, %esp */
       addInstr(env, 
                X86Instr_Alu32R(Xalu_SUB, X86RMI_Imm(8), hregX86_ESP()));
@@ -2103,10 +2105,17 @@ static HReg iselFltExpr_wrk ( ISelEnv* env, IRExpr* e )
       return res;
    }
 
-   if (e->tag == Iex_Unop
-       && e->Iex.Unop.op == Iop_F64toF32) {
-      /* this is a no-op */
-      return iselDblExpr(env, e->Iex.Unop.arg);
+   if (e->tag == Iex_Binop
+       && e->Iex.Binop.op == Iop_F64toF32) {
+      /* Although the result is still held in a standard FPU register,
+         we need to round it to reflect the loss of accuracy/range
+         entailed in casting it to a 32-bit float. */
+      HReg dst = newVRegF(env);
+      HReg src = iselDblExpr(env, e->Iex.Binop.arg2);
+      set_FPU_rounding_mode( env, e->Iex.Binop.arg1 );
+      addInstr(env, X86Instr_Fp64to32(src,dst));
+      set_FPU_rounding_default( env );
+      return dst;
    }
 
    if (e->tag == Iex_Get) {
@@ -2117,37 +2126,6 @@ static HReg iselFltExpr_wrk ( ISelEnv* env, IRExpr* e )
       return res;
    }
 
-   if (e->tag == Iex_Binop && e->Iex.Binop.op == Iop_I32toF32) {
-      HReg dst = newVRegF(env);
-      X86AMode* zero_esp = X86AMode_IR(0, hregX86_ESP());
-      X86RMI* rmi = iselIntExpr_RMI(env, e->Iex.Binop.arg2);
-
-      /* int value -> stack */
-      addInstr(env, X86Instr_Push(rmi));
-
-      /* Set host rounding mode */
-      set_FPU_rounding_mode( env, e->Iex.Binop.arg1 );
-
-      /* get it back from the stack, into F80 form.  Rounding mode has
-         no effect as this can be done exactly. */
-      addInstr(env, X86Instr_FpLdStI(True/*load*/, 4, dst, zero_esp));
-
-      /* write it back to the stack, as a 32-bit float.  Rounding mode
-        effects this. */
-      addInstr(env, X86Instr_FpLdSt(False/*store*/, 4, dst, zero_esp));
-
-      /* and finally ... fetch it back again.  (sigh) */
-      addInstr(env, X86Instr_FpLdSt(True/*load*/, 4, dst, zero_esp));
-
-      /* Restore default FPU rounding. */
-      set_FPU_rounding_default( env );
-
-      addInstr(env, X86Instr_Alu32R(Xalu_ADD,
-                                       X86RMI_Imm(4),
-                                       hregX86_ESP()));
-      return dst;
-   }
-
    ppIRExpr(e);
    vpanic("iselFltExpr_wrk");
 }
@@ -2355,9 +2333,10 @@ static HReg iselDblExpr_wrk ( ISelEnv* env, IRExpr* e )
             HReg dst = newVRegF(env);
             HReg rHi, rLo;
            iselInt64Expr( &rHi, &rLo, env, e->Iex.Unop.arg);
+            /* paranoia */
+            set_FPU_rounding_default(env);
             addInstr(env, X86Instr_Push(X86RMI_Reg(rHi)));
             addInstr(env, X86Instr_Push(X86RMI_Reg(rLo)));
-            set_FPU_rounding_default(env);
             addInstr(env, X86Instr_FpLdSt(
                              True/*load*/, 8, dst, 
                              X86AMode_IR(0, hregX86_ESP())));
index 0434c2f813acbedb6edb370367816a3933985091..3d3c62288138069640e94aa8e5598af4f973c8d9 100644 (file)
@@ -205,18 +205,19 @@ void ppIROp ( IROp op )
 
       case Iop_CmpF64:    vex_printf("CmpF64"); return;
 
+      case Iop_F64toI16: vex_printf("F64toI16"); return;
+      case Iop_F64toI32: vex_printf("F64toI32"); return;
+      case Iop_F64toI64: vex_printf("F64toI64"); return;
+
+      case Iop_I16toF64: vex_printf("I16toF64"); return;
       case Iop_I32toF64: vex_printf("I32toF64"); return;
       case Iop_I64toF64: vex_printf("I64toF64"); return;
-      case Iop_I32toF32: vex_printf("I32toF32"); return;
-
-      case Iop_F64toI64: vex_printf("F64toI64"); return;
-      case Iop_F64toI32: vex_printf("F64toI32"); return;
-      case Iop_F64toI16: vex_printf("F64toI16"); return;
-      case Iop_RoundF64: vex_printf("RoundF64"); return;
 
       case Iop_F32toF64: vex_printf("F32toF64"); return;
       case Iop_F64toF32: vex_printf("F64toF32"); return;
 
+      case Iop_RoundF64: vex_printf("RoundF64"); return;
+
       case Iop_ReinterpF64asI64: vex_printf("ReinterpF64asI64"); return;
       case Iop_ReinterpI64asF64: vex_printf("ReinterpI64asF64"); return;
 
@@ -1089,20 +1090,21 @@ void typeOfPrimop ( IROp op, IRType* t_dst, IRType* t_arg1, IRType* t_arg2 )
       case Iop_SinF64: case Iop_CosF64: case Iop_TanF64: case Iop_2xm1F64:
          UNARY(Ity_F64,Ity_F64);
 
-      case Iop_I32toF64:         UNARY(Ity_F64,Ity_I32);
-      case Iop_ReinterpI64asF64: UNARY(Ity_F64,Ity_I64);
+      case Iop_ReinterpI64asF64: UNARY(Ity_F64, Ity_I64);
       case Iop_ReinterpF64asI64: UNARY(Ity_I64, Ity_F64);
 
-      case Iop_I32toF32: BINARY(Ity_F32, Ity_I32,Ity_I32);
+      case Iop_F64toI16: BINARY(Ity_I16, Ity_I32,Ity_F64);
+      case Iop_F64toI32: BINARY(Ity_I32, Ity_I32,Ity_F64);
+      case Iop_F64toI64: BINARY(Ity_I64, Ity_I32,Ity_F64);
+
+      case Iop_I16toF64: UNARY(Ity_F64, Ity_I16);
+      case Iop_I32toF64: UNARY(Ity_F64, Ity_I32);
       case Iop_I64toF64: BINARY(Ity_F64, Ity_I32,Ity_I64);
 
-      case Iop_F64toI64: BINARY(Ity_I64, Ity_I32,Ity_F64);
-      case Iop_F64toI32: BINARY(Ity_I32, Ity_I32,Ity_F64);
-      case Iop_F64toI16: BINARY(Ity_I16, Ity_I32,Ity_F64);
-      case Iop_RoundF64: BINARY(Ity_F64, Ity_I32,Ity_F64);
+      case Iop_F32toF64: UNARY(Ity_F64, Ity_F32);
+      case Iop_F64toF32: BINARY(Ity_F32, Ity_I32,Ity_F64);
 
-      case Iop_F32toF64: UNARY(Ity_F64,Ity_F32);
-      case Iop_F64toF32: UNARY(Ity_F32,Ity_F64);
+      case Iop_RoundF64: BINARY(Ity_F64, Ity_I32,Ity_F64);
 
       case Iop_64HLto128: BINARY(Ity_V128, Ity_I64,Ity_I64);
       case Iop_128to64: case Iop_128HIto64: 
index 362a06cfb2511ca89470a9a7cf9b7523eaf8a1a3..d2fd489607c9aae9eae24883ccef55d13bf41e29 100644 (file)
@@ -281,13 +281,10 @@ typedef
       */
       Iop_CmpF64,
 
-      /* int -> double */
-      Iop_I32toF64,  /* no rounding needed because result can always be
-                        represented exactly */
-
-      /* double -> int.  These take a first argument :: Ity_I32 
-         (an IRRoundingMode) which is an indication of the rounding mode,
-         as per the following encoding:
+      /* --- Int to/from FP conversions. --- */
+      /* For the most part, these take a first argument :: Ity_I32
+        (as IRRoundingMode) which is an indication of the rounding
+         mode to use, as per the following encoding:
             00b  to nearest (the default)
             01b  to -infinity
             10b  to +infinity
@@ -306,21 +303,25 @@ typedef
          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.
+
+         Rounding is required whenever the destination type cannot
+         represent exactly all values of the source type.
       */
-      Iop_F64toI64, Iop_F64toI32, Iop_F64toI16,
+      Iop_F64toI16,  /* IRRoundingMode(I32) x F64 -> I16 */
+      Iop_F64toI32,  /* IRRoundingMode(I32) x F64 -> I32 */
+      Iop_F64toI64,  /* IRRoundingMode(I32) x F64 -> I64 */
 
-      /* these also take an I32 first argument encoding the
-         rounding mode. */
-      Iop_I64toF64,
-      Iop_I32toF32,
+      Iop_I16toF64,  /*                       I16 -> F64 */
+      Iop_I32toF64,  /*                       I32 -> F64 */
+      Iop_I64toF64,  /* IRRoundingMode(I32) x I64 -> F64 */
+
+      Iop_F32toF64,  /*                       F32 -> F64 */
+      Iop_F64toF32,  /* IRRoundingMode(I32) x F64 -> F32 */
 
       /* F64 -> F64, also takes an I32 first argument encoding the
          rounding mode. */
       Iop_RoundF64,
 
-      /* double <-> float.  What does this mean -- does it round? */
-      Iop_F32toF64, Iop_F64toF32,
-
       /* Reinterpretation.  Take an F64 and produce an I64 with 
          the same bit pattern, or vice versa. */
       Iop_ReinterpF64asI64, Iop_ReinterpI64asF64,