From: Julian Seward Date: Sat, 4 Dec 2004 14:36:09 +0000 (+0000) Subject: Rationalisation/cleanup of float to/from int conversions and rounding X-Git-Tag: svn/VALGRIND_3_0_1^2~718 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=2875f2e1f93f593f031000f4cb0de82287eed927;p=thirdparty%2Fvalgrind.git Rationalisation/cleanup of float to/from int conversions and rounding modes associated with them. git-svn-id: svn://svn.valgrind.org/vex/trunk@616 --- diff --git a/VEX/priv/guest-x86/toIR.c b/VEX/priv/guest-x86/toIR.c index b2a98d8ce6..1f3192113a 100644 --- a/VEX/priv/guest-x86/toIR.c +++ b/VEX/priv/guest-x86/toIR.c @@ -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 diff --git a/VEX/priv/host-x86/hdefs.c b/VEX/priv/host-x86/hdefs.c index 2e7f854fe1..7ee3443974 100644 --- a/VEX/priv/host-x86/hdefs.c +++ b/VEX/priv/host-x86/hdefs.c @@ -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); diff --git a/VEX/priv/host-x86/hdefs.h b/VEX/priv/host-x86/hdefs.h index c1ba6c2fa4..3e143cc6e2 100644 --- a/VEX/priv/host-x86/hdefs.h +++ b/VEX/priv/host-x86/hdefs.h @@ -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 ); diff --git a/VEX/priv/host-x86/isel.c b/VEX/priv/host-x86/isel.c index 0347d14d17..9f04acea28 100644 --- a/VEX/priv/host-x86/isel.c +++ b/VEX/priv/host-x86/isel.c @@ -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()))); diff --git a/VEX/priv/ir/irdefs.c b/VEX/priv/ir/irdefs.c index 0434c2f813..3d3c622881 100644 --- a/VEX/priv/ir/irdefs.c +++ b/VEX/priv/ir/irdefs.c @@ -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: diff --git a/VEX/pub/libvex_ir.h b/VEX/pub/libvex_ir.h index 362a06cfb2..d2fd489607 100644 --- a/VEX/pub/libvex_ir.h +++ b/VEX/pub/libvex_ir.h @@ -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,