// UInt arg, UInt rot_amt, UInt eflags_in, UInt sz
// );
-//extern ULong amd64g_check_fldcw ( ULong fpucw );
+extern ULong amd64g_check_fldcw ( ULong fpucw );
-//extern ULong amd64g_create_fpucw ( ULong fpround );
+extern ULong amd64g_create_fpucw ( ULong fpround );
extern ULong amd64g_check_ldmxcsr ( ULong mxcsr );
}
+/* CLEAN HELPER */
+/* fpucw[15:0] contains a x87 native format FPU control word.
+ Extract from it the required FPROUND value and any resulting
+ emulation warning, and return (warn << 32) | fpround value.
+*/
+ULong amd64g_check_fldcw ( ULong fpucw )
+{
+ /* Decide on a rounding mode. fpucw[11:10] holds it. */
+ /* NOTE, encoded exactly as per enum IRRoundingMode. */
+ ULong rmode = (fpucw >> 10) & 3;
+
+ /* Detect any required emulation warnings. */
+ VexEmWarn ew = EmWarn_NONE;
+
+ if ((fpucw & 0x3F) != 0x3F) {
+ /* unmasked exceptions! */
+ ew = EmWarn_X86_x87exns;
+ }
+ else
+ if (((fpucw >> 8) & 3) != 3) {
+ /* unsupported precision */
+ ew = EmWarn_X86_x87precision;
+ }
+
+ return (((ULong)ew) << 32) | ((ULong)rmode);
+}
+
+
+/* CLEAN HELPER */
+/* Given fpround as an IRRoundingMode value, create a suitable x87
+ native format FPU control word. */
+ULong amd64g_create_fpucw ( ULong fpround )
+{
+ fpround &= 3;
+ return 0x037F | (fpround << 10);
+}
+
/*---------------------------------------------------------------*/
/*--- Misc integer helpers, including rotates and CPUID. ---*/
return unop(Iop_64to32, IRExpr_Get( OFFB_FPROUND, Ity_I64 ));
}
-//.. static void put_fpround ( IRExpr* /* :: Ity_I32 */ e )
-//.. {
-//.. vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I32);
-//.. stmt( IRStmt_Put( OFFB_FPROUND, unop(Iop_32Uto64,e) ) );
-//.. }
+static void put_fpround ( IRExpr* /* :: Ity_I32 */ e )
+{
+ vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I32);
+ stmt( IRStmt_Put( OFFB_FPROUND, unop(Iop_32Uto64,e) ) );
+}
/* --------- Synthesise a 2-bit FPU rounding mode. --------- */
//.. DIP("fldenv %s\n", dis_buf);
//.. break;
//.. }
-//..
-//.. case 5: {/* FLDCW */
-//.. /* The only thing we observe in the control word is the
-//.. rounding mode. Therefore, pass the 16-bit value
-//.. (x87 native-format control word) to a clean helper,
-//.. getting back a 64-bit value, the lower half of which
-//.. is the FPROUND value to store, and the upper half of
-//.. which is the emulation-warning token which may be
-//.. generated.
-//.. */
-//.. /* ULong x86h_check_fldcw ( UInt ); */
-//.. IRTemp t64 = newTemp(Ity_I64);
-//.. IRTemp ew = newTemp(Ity_I32);
-//.. DIP("fldcw %s\n", dis_buf);
-//.. assign( t64, mkIRExprCCall(
-//.. Ity_I64, 0/*regparms*/,
-//.. "x86g_check_fldcw",
-//.. &x86g_check_fldcw,
-//.. mkIRExprVec_1(
-//.. unop( Iop_16Uto32,
-//.. loadLE(Ity_I16, mkexpr(addr)))
-//.. )
-//.. )
-//.. );
-//..
-//.. put_fpround( unop(Iop_64to32, mkexpr(t64)) );
-//.. assign( ew, unop(Iop_64HIto32, mkexpr(t64) ) );
-//.. put_emwarn( mkexpr(ew) );
-//.. /* Finally, if an emulation warning was reported,
-//.. side-exit to the next insn, reporting the warning,
-//.. so that Valgrind's dispatcher sees the warning. */
-//.. stmt(
-//.. IRStmt_Exit(
-//.. binop(Iop_CmpNE32, mkexpr(ew), mkU32(0)),
-//.. Ijk_EmWarn,
-//.. IRConst_U32( ((Addr32)guest_eip_bbstart)+delta)
-//.. )
-//.. );
-//.. break;
-//.. }
-//..
+
+ case 5: {/* FLDCW */
+ /* The only thing we observe in the control word is the
+ rounding mode. Therefore, pass the 16-bit value
+ (x87 native-format control word) to a clean helper,
+ getting back a 64-bit value, the lower half of which
+ is the FPROUND value to store, and the upper half of
+ which is the emulation-warning token which may be
+ generated.
+ */
+ /* ULong amd64h_check_fldcw ( ULong ); */
+ IRTemp t64 = newTemp(Ity_I64);
+ IRTemp ew = newTemp(Ity_I32);
+ DIP("fldcw %s\n", dis_buf);
+ assign( t64, mkIRExprCCall(
+ Ity_I64, 0/*regparms*/,
+ "amd64g_check_fldcw",
+ &amd64g_check_fldcw,
+ mkIRExprVec_1(
+ unop( Iop_16Uto64,
+ loadLE(Ity_I16, mkexpr(addr)))
+ )
+ )
+ );
+
+ put_fpround( unop(Iop_64to32, mkexpr(t64)) );
+ assign( ew, unop(Iop_64HIto32, mkexpr(t64) ) );
+ put_emwarn( mkexpr(ew) );
+ /* Finally, if an emulation warning was reported,
+ side-exit to the next insn, reporting the warning,
+ so that Valgrind's dispatcher sees the warning. */
+ stmt(
+ IRStmt_Exit(
+ binop(Iop_CmpNE32, mkexpr(ew), mkU32(0)),
+ Ijk_EmWarn,
+ IRConst_U64( guest_rip_bbstart+delta )
+ )
+ );
+ break;
+ }
+
//.. case 6: { /* FNSTENV m28 */
//.. /* Uses dirty helper:
//.. void x86g_do_FSTENV ( VexGuestX86State*, UInt ) */
//.. DIP("fnstenv %s\n", dis_buf);
//.. break;
//.. }
-//..
-//.. case 7: /* FNSTCW */
-//.. /* Fake up a native x87 FPU control word. The only
-//.. thing it depends on is FPROUND[1:0], so call a clean
-//.. helper to cook it up. */
-//.. /* UInt x86h_create_fpucw ( UInt fpround ) */
-//.. DIP("fnstcw %s\n", dis_buf);
-//.. storeLE(
-//.. mkexpr(addr),
-//.. unop( Iop_32to16,
-//.. mkIRExprCCall(
-//.. Ity_I32, 0/*regp*/,
-//.. "x86g_create_fpucw", &x86g_create_fpucw,
-//.. mkIRExprVec_1( get_fpround() )
-//.. )
-//.. )
-//.. );
-//.. break;
+
+ case 7: /* FNSTCW */
+ /* Fake up a native x87 FPU control word. The only
+ thing it depends on is FPROUND[1:0], so call a clean
+ helper to cook it up. */
+ /* ULong x86h_create_fpucw ( ULong fpround ) */
+ DIP("fnstcw %s\n", dis_buf);
+ storeLE(
+ mkexpr(addr),
+ unop( Iop_64to16,
+ mkIRExprCCall(
+ Ity_I64, 0/*regp*/,
+ "amd64g_create_fpucw", &amd64g_create_fpucw,
+ mkIRExprVec_1( unop(Iop_32Uto64, get_fpround()) )
+ )
+ )
+ );
+ break;
default:
vex_printf("unhandled opc_aux = 0x%2x\n", gregLO3ofRM(modrm));
fp_pop();
break;
-//.. case 0xF2: /* FPTAN */
-//.. DIP("ftan\n");
-//.. put_ST_UNCHECKED(0, unop(Iop_TanF64, get_ST(0)));
-//.. fp_push();
-//.. put_ST(0, IRExpr_Const(IRConst_F64(1.0)));
-//.. clear_C2(); /* HACK */
-//.. break;
+ case 0xF2: /* FPTAN */
+ DIP("ftan\n");
+ put_ST_UNCHECKED(0, unop(Iop_TanF64, get_ST(0)));
+ fp_push();
+ put_ST(0, IRExpr_Const(IRConst_F64(1.0)));
+ clear_C2(); /* HACK */
+ break;
case 0xF3: /* FPATAN */
DIP("fpatan\n");
//.. break;
//.. }
//..
-//.. case 0xF9: /* FYL2XP1 */
-//.. DIP("fyl2xp1\n");
-//.. put_ST_UNCHECKED(1, binop(Iop_Yl2xp1F64,
-//.. get_ST(1), get_ST(0)));
-//.. fp_pop();
-//.. break;
+ case 0xF9: /* FYL2XP1 */
+ DIP("fyl2xp1\n");
+ put_ST_UNCHECKED(1, binop(Iop_Yl2xp1F64,
+ get_ST(1), get_ST(0)));
+ fp_pop();
+ break;
case 0xFA: /* FSQRT */
DIP("fsqrt\n");
case Afp_SCALE: return "scale";
case Afp_ATAN: return "atan";
case Afp_YL2X: return "yl2x";
-//.. case Xfp_YL2XP1: return "yl2xp1";
+ case Afp_YL2XP1: return "yl2xp1";
//.. case Xfp_PREM: return "prem";
//.. case Xfp_PREM1: return "prem1";
case Afp_SQRT: return "sqrt";
//.. case Xfp_MOV: return "mov";
case Afp_SIN: return "sin";
case Afp_COS: return "cos";
-//.. case Xfp_TAN: return "tan";
+ case Afp_TAN: return "tan";
case Afp_ROUND: return "round";
case Afp_2XM1: return "2xm1";
default: vpanic("showA87FpOp");
case Ain_A87FpOp:
switch (i->Ain.A87FpOp.op) {
- case Afp_SQRT: *p++ = 0xD9; *p++ = 0xFA; break;
- case Afp_SIN: *p++ = 0xD9; *p++ = 0xFE; break;
- case Afp_COS: *p++ = 0xD9; *p++ = 0xFF; break;
- case Afp_ROUND: *p++ = 0xD9; *p++ = 0xFC; break;
- case Afp_2XM1: *p++ = 0xD9; *p++ = 0xF0; break;
- case Afp_SCALE: *p++ = 0xD9; *p++ = 0xFD; break;
- case Afp_ATAN: *p++ = 0xD9; *p++ = 0xF3; break;
- case Afp_YL2X: *p++ = 0xD9; *p++ = 0xF1; break;
+ case Afp_SQRT: *p++ = 0xD9; *p++ = 0xFA; break;
+ case Afp_SIN: *p++ = 0xD9; *p++ = 0xFE; break;
+ case Afp_COS: *p++ = 0xD9; *p++ = 0xFF; break;
+ case Afp_TAN: *p++ = 0xD9; *p++ = 0xF2; break;
+ case Afp_ROUND: *p++ = 0xD9; *p++ = 0xFC; break;
+ case Afp_2XM1: *p++ = 0xD9; *p++ = 0xF0; break;
+ case Afp_SCALE: *p++ = 0xD9; *p++ = 0xFD; break;
+ case Afp_ATAN: *p++ = 0xD9; *p++ = 0xF3; break;
+ case Afp_YL2X: *p++ = 0xD9; *p++ = 0xF1; break;
+ case Afp_YL2XP1: *p++ = 0xD9; *p++ = 0xF9; break;
default: goto bad;
}
goto done;
Afp_INVALID,
/* Binary */
//.. Xfp_ADD, Xfp_SUB, Xfp_MUL, Xfp_DIV,
- Afp_SCALE, Afp_ATAN, Afp_YL2X, //Xfp_YL2XP1, Xfp_PREM, Xfp_PREM1,
+ Afp_SCALE, Afp_ATAN, Afp_YL2X, Afp_YL2XP1, //Xfp_PREM, Xfp_PREM1,
/* Unary */
Afp_SQRT, //Xfp_ABS, Xfp_NEG, Xfp_MOV,
- Afp_SIN, Afp_COS, //Xfp_TAN,
+ Afp_SIN, Afp_COS, Afp_TAN,
Afp_ROUND, Afp_2XM1
}
A87FpOp;
if (e->tag == Iex_Binop
&& (e->Iex.Binop.op == Iop_ScaleF64
|| e->Iex.Binop.op == Iop_AtanF64
- || e->Iex.Binop.op == Iop_Yl2xF64)
+ || e->Iex.Binop.op == Iop_Yl2xF64
+ || e->Iex.Binop.op == Iop_Yl2xp1F64)
) {
AMD64AMode* m8_rsp = AMD64AMode_IR(-8, hregAMD64_RSP());
HReg arg1 = iselDblExpr(env, e->Iex.Binop.arg1);
case Iop_Yl2xF64:
addInstr(env, AMD64Instr_A87FpOp(Afp_YL2X));
break;
+ case Iop_Yl2xp1F64:
+ addInstr(env, AMD64Instr_A87FpOp(Afp_YL2XP1));
+ break;
default:
vassert(0);
}
//.. case Iop_NegF64: fpop = Xfp_NEG; break;
//.. case Iop_AbsF64: fpop = Xfp_ABS; break;
case Iop_SqrtF64: fpop = Afp_SQRT; break;
- case Iop_SinF64: fpop = Afp_SIN; break;
- case Iop_CosF64: fpop = Afp_COS; break;
-//.. case Iop_TanF64: fpop = Xfp_TAN; break;
+ case Iop_SinF64: fpop = Afp_SIN; break;
+ case Iop_CosF64: fpop = Afp_COS; break;
+ case Iop_TanF64: fpop = Afp_TAN; break;
case Iop_2xm1F64: fpop = Afp_2XM1; break;
default: break;
}
AMD64AMode* m8_rsp = AMD64AMode_IR(-8, hregAMD64_RSP());
HReg arg = iselDblExpr(env, e->Iex.Unop.arg);
HReg dst = newVRegV(env);
+ Int nNeeded = e->Iex.Unop.op==Iop_TanF64 ? 2 : 1;
addInstr(env, AMD64Instr_SseLdSt(False/*store*/, 8, arg, m8_rsp));
- addInstr(env, AMD64Instr_A87Free(1));
+ addInstr(env, AMD64Instr_A87Free(nNeeded));
addInstr(env, AMD64Instr_A87PushPop(m8_rsp, True/*push*/));
addInstr(env, AMD64Instr_A87FpOp(fpop));
+ if (e->Iex.Unop.op==Iop_TanF64) {
+ /* get rid of the extra 1.0 that fptan pushes */
+ addInstr(env, AMD64Instr_A87PushPop(m8_rsp, False/*pop*/));
+ }
addInstr(env, AMD64Instr_A87PushPop(m8_rsp, False/*pop*/));
addInstr(env, AMD64Instr_SseLdSt(True/*load*/, 8, dst, m8_rsp));
return dst;