From: Julian Seward Date: Mon, 10 Jan 2005 16:49:19 +0000 (+0000) Subject: x86 floating point accuracy improvements. The aim is to make x86 FP X-Git-Tag: svn/VALGRIND_3_0_1^2~629 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3feb4705a7e5d40573205444e27fd5dced3843c5;p=thirdparty%2Fvalgrind.git x86 floating point accuracy improvements. The aim is to make x86 FP behave more consistently like an 64-bit implementation. Mostly this means running the host with the FPU set to 64-bit precision, not 80-bit. Requires corresponding changes on the Valgrind side of the fence. git-svn-id: svn://svn.valgrind.org/vex/trunk@705 --- diff --git a/VEX/priv/guest-x86/toIR.c b/VEX/priv/guest-x86/toIR.c index 98cf4039f8..e86a2f7513 100644 --- a/VEX/priv/guest-x86/toIR.c +++ b/VEX/priv/guest-x86/toIR.c @@ -3905,43 +3905,50 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta ) case 0xE8: /* FLD1 */ DIP("fld1\n"); fp_push(); - put_ST(0, IRExpr_Const(IRConst_F64(1.0))); + /* put_ST(0, IRExpr_Const(IRConst_F64(1.0))); */ + put_ST(0, IRExpr_Const(IRConst_F64i(0x3ff0000000000000ULL))); break; case 0xE9: /* FLDL2T */ DIP("fldl2t\n"); fp_push(); - put_ST(0, IRExpr_Const(IRConst_F64(3.32192809488736234781))); + /* put_ST(0, IRExpr_Const(IRConst_F64(3.32192809488736234781))); */ + put_ST(0, IRExpr_Const(IRConst_F64i(0x400a934f0979a371ULL))); break; case 0xEA: /* FLDL2E */ DIP("fldl2e\n"); fp_push(); - put_ST(0, IRExpr_Const(IRConst_F64(1.44269504088896340739))); + /* put_ST(0, IRExpr_Const(IRConst_F64(1.44269504088896340739))); */ + put_ST(0, IRExpr_Const(IRConst_F64i(0x3ff71547652b82feULL))); break; case 0xEB: /* FLDPI */ DIP("fldpi\n"); fp_push(); - put_ST(0, IRExpr_Const(IRConst_F64(3.14159265358979323851))); + /* put_ST(0, IRExpr_Const(IRConst_F64(3.14159265358979323851))); */ + put_ST(0, IRExpr_Const(IRConst_F64i(0x400921fb54442d18ULL))); break; case 0xEC: /* FLDLG2 */ DIP("fldlg2\n"); fp_push(); - put_ST(0, IRExpr_Const(IRConst_F64(0.301029995663981143))); + /* put_ST(0, IRExpr_Const(IRConst_F64(0.301029995663981143))); */ + put_ST(0, IRExpr_Const(IRConst_F64i(0x3fd34413509f79ffULL))); break; case 0xED: /* FLDLN2 */ DIP("fldln2\n"); fp_push(); - put_ST(0, IRExpr_Const(IRConst_F64(0.69314718055994530942))); + /* put_ST(0, IRExpr_Const(IRConst_F64(0.69314718055994530942))); */ + put_ST(0, IRExpr_Const(IRConst_F64i(0x3fe62e42fefa39efULL))); break; case 0xEE: /* FLDZ */ DIP("fldz\n"); fp_push(); - put_ST(0, IRExpr_Const(IRConst_F64(0.0))); + /* put_ST(0, IRExpr_Const(IRConst_F64(0.0))); */ + put_ST(0, IRExpr_Const(IRConst_F64i(0x0000000000000000ULL))); break; case 0xF0: /* F2XM1 */ diff --git a/VEX/priv/host-x86/isel.c b/VEX/priv/host-x86/isel.c index 71a3fc7021..8f822fb66e 100644 --- a/VEX/priv/host-x86/isel.c +++ b/VEX/priv/host-x86/isel.c @@ -44,6 +44,27 @@ #include "host-x86/hdefs.h" +/*---------------------------------------------------------*/ +/*--- x87 control word stuff ---*/ +/*---------------------------------------------------------*/ + +/* Vex-generated code expects to run with the FPU set as follows: all + exceptions masked, round-to-nearest, precision = 53 bits. This + corresponds to a FPU control word value of 0x027F. + + Similarly the SSE control word (%mxcsr) should be 0x1F80. + + %fpucw and %mxcsr should have these values on entry to + Vex-generated code, and should those values should be + unchanged at exit. +*/ + +#define DEFAULT_FPUCW 0x027F + +/* debugging only, do not use */ +/* define DEFAULT_FPUCW 0x037F */ + + /*---------------------------------------------------------*/ /*--- misc helpers ---*/ /*---------------------------------------------------------*/ @@ -572,16 +593,16 @@ X86AMode* genGuestArrayOffset ( ISelEnv* env, IRArray* descr, /* Mess with the FPU's rounding mode: set to the default rounding mode - (0x037F). */ + (DEFAULT_FPUCW). */ static void set_FPU_rounding_default ( ISelEnv* env ) { - /* pushl $0x037F + /* pushl $DEFAULT_FPUCW fldcw 0(%esp) addl $4, %esp */ X86AMode* zero_esp = X86AMode_IR(0, hregX86_ESP()); - addInstr(env, X86Instr_Push(X86RMI_Imm(0x037F))); + addInstr(env, X86Instr_Push(X86RMI_Imm(DEFAULT_FPUCW))); addInstr(env, X86Instr_FpLdStCW(True/*load*/, zero_esp)); add_to_esp(env, 4); } @@ -602,7 +623,7 @@ void set_FPU_rounding_mode ( ISelEnv* env, IRExpr* mode ) /* movl %rrm, %rrm2 andl $3, %rrm2 -- shouldn't be needed; paranoia shll $10, %rrm2 - orl $0x037F, %rrm2 + orl $DEFAULT_FPUCW, %rrm2 pushl %rrm2 fldcw 0(%esp) addl $4, %esp @@ -610,7 +631,7 @@ void set_FPU_rounding_mode ( ISelEnv* env, IRExpr* mode ) addInstr(env, mk_iMOVsd_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_Alu32R(Xalu_OR, X86RMI_Imm(DEFAULT_FPUCW), rrm2)); addInstr(env, X86Instr_Push(X86RMI_Reg(rrm2))); addInstr(env, X86Instr_FpLdStCW(True/*load*/, zero_esp)); add_to_esp(env, 4); @@ -636,6 +657,23 @@ static HReg do_sse_Not128 ( ISelEnv* env, HReg src ) } +/* Round an x87 FPU value to 53-bit-mantissa precision, to be used + after most non-simple FPU operations (simple = +, -, *, / and + sqrt). + + This could be done a lot more efficiently if needed, by loading + zero and adding it to the value to be rounded (fldz ; faddp?). +*/ +static void roundToF64 ( ISelEnv* env, HReg reg ) +{ + X86AMode* zero_esp = X86AMode_IR(0, hregX86_ESP()); + sub_from_esp(env, 8); + addInstr(env, X86Instr_FpLdSt(False/*store*/, 8, reg, zero_esp)); + addInstr(env, X86Instr_FpLdSt(True/*load*/, 8, reg, zero_esp)); + add_to_esp(env, 8); +} + + /*---------------------------------------------------------*/ /*--- ISEL: Integer expressions (32/16/8 bit) ---*/ /*---------------------------------------------------------*/ @@ -2245,6 +2283,9 @@ static HReg iselDblExpr_wrk ( ISelEnv* env, IRExpr* e ) HReg srcL = iselDblExpr(env, e->Iex.Binop.arg1); HReg srcR = iselDblExpr(env, e->Iex.Binop.arg2); addInstr(env, X86Instr_FpBinary(fpop,srcL,srcR,res)); + if (fpop != Xfp_ADD && fpop != Xfp_SUB + && fpop != Xfp_MUL && fpop != Xfp_DIV) + roundToF64(env, res); return res; } } @@ -2305,6 +2346,9 @@ static HReg iselDblExpr_wrk ( ISelEnv* env, IRExpr* e ) HReg res = newVRegF(env); HReg src = iselDblExpr(env, e->Iex.Unop.arg); addInstr(env, X86Instr_FpUnary(fpop,src,res)); + if (fpop != Xfp_SQRT + && fpop != Xfp_NEG && fpop != Xfp_ABS) + roundToF64(env, res); return res; } } @@ -2338,9 +2382,11 @@ static HReg iselDblExpr_wrk ( ISelEnv* env, IRExpr* e ) add_to_esp(env, 8); return dst; } - case Iop_F32toF64: + case Iop_F32toF64: { /* this is a no-op */ - return iselFltExpr(env, e->Iex.Unop.arg); + HReg res = iselFltExpr(env, e->Iex.Unop.arg); + return res; + } default: break; }