fp_pop();
break;
+ case 0xF5: { /* FPREM1 -- IEEE compliant */
+ IRTemp a1 = newTemp(Ity_F64);
+ IRTemp a2 = newTemp(Ity_F64);
+ DIP("fprem1\n");
+ /* Do FPREM1 twice, once to get the remainder, and once
+ to get the C3210 flag values. */
+ assign( a1, get_ST(0) );
+ assign( a2, get_ST(1) );
+ put_ST_UNCHECKED(0, binop(Iop_PRem1F64,
+ mkexpr(a1), mkexpr(a2)));
+ put_C3210( binop(Iop_PRem1C3210F64, mkexpr(a1), mkexpr(a2)) );
+ break;
+ }
+
case 0xF8: { /* FPREM -- not IEEE compliant */
IRTemp a1 = newTemp(Ity_F64);
IRTemp a2 = newTemp(Ity_F64);
case Xfp_TAN: return "tan";
case Xfp_2XM1: return "2xm1";
case Xfp_ATAN: return "atan";
+ case Xfp_PREM: return "prem";
+ case Xfp_PREM1: return "prem1";
default: vpanic("showX86FpOp");
}
}
goto done;
}
if (i->Xin.FpBinary.op == Xfp_PREM
+ || i->Xin.FpBinary.op == Xfp_PREM1
|| i->Xin.FpBinary.op == Xfp_SCALE) {
/* Have to do this specially. */
/* ffree %st7 ; fld %st(srcR) ;
- ffree %st7 ; fld %st(srcL+1) ; fprem/fscale ; fstp(2+dst) ;
+ ffree %st7 ; fld %st(srcL+1) ; fprem/fprem1/fscale ; fstp(2+dst) ;
fincstp ; ffree %st7 */
p = do_ffree_st7(p);
p = do_fld_st(p, 0+hregNumber(i->Xin.FpBinary.srcR));
p = do_ffree_st7(p);
p = do_fld_st(p, 1+hregNumber(i->Xin.FpBinary.srcL));
- *p++ = 0xD9;
- *p++ = i->Xin.FpBinary.op==Xfp_PREM ? 0xF8 : 0xFD;
+ *p++ = 0xD9;
+ switch (i->Xin.FpBinary.op) {
+ case Xfp_PREM: *p++ = 0xF8; break;
+ case Xfp_PREM1: *p++ = 0xF5; break;
+ case Xfp_SCALE: *p++ = 0xFD; break;
+ default: vpanic("emitX86Instr(FpBinary,PREM/PREM1/SCALE)");
+ }
p = do_fstp_st(p, 2+hregNumber(i->Xin.FpBinary.dst));
*p++ = 0xD9; *p++ = 0xF7;
p = do_ffree_st7(p);
Xfp_INVALID,
/* Binary */
Xfp_ADD, Xfp_SUB, Xfp_MUL, Xfp_DIV,
- Xfp_SCALE, Xfp_ATAN, Xfp_YL2X, Xfp_YL2XP1, Xfp_PREM,
+ Xfp_SCALE, Xfp_ATAN, Xfp_YL2X, Xfp_YL2XP1, Xfp_PREM, Xfp_PREM1,
/* Unary */
Xfp_SQRT, Xfp_ABS, Xfp_NEG, Xfp_MOV, Xfp_SIN, Xfp_COS, Xfp_TAN,
Xfp_ROUND, Xfp_2XM1
return dst;
}
- /* C3210 flags following FPU partial remainder (fprem). */
- if (e->Iex.Binop.op == Iop_PRemC3210F64) {
+ /* C3210 flags following FPU partial remainder (fprem), both
+ IEEE compliant (PREM1) and non-IEEE compliant (PREM). */
+ if (e->Iex.Binop.op == Iop_PRemC3210F64
+ || e->Iex.Binop.op == Iop_PRem1C3210F64) {
HReg junk = newVRegF(env);
HReg dst = newVRegI(env);
HReg srcL = iselDblExpr(env, e->Iex.Binop.arg1);
HReg srcR = iselDblExpr(env, e->Iex.Binop.arg2);
- addInstr(env, X86Instr_FpBinary(Xfp_PREM,srcL,srcR,junk));
+ addInstr(env, X86Instr_FpBinary(
+ e->Iex.Binop.op==Iop_PRemC3210F64 ? Xfp_PREM : Xfp_PREM1,
+ srcL,srcR,junk
+ ));
/* The previous pseudo-insn will have left the FPU's C3210
flags set correctly. So bag them. */
addInstr(env, X86Instr_FpStSW_AX());
case Iop_Yl2xF64: fpop = Xfp_YL2X; break;
case Iop_Yl2xp1F64: fpop = Xfp_YL2XP1; break;
case Iop_PRemF64: fpop = Xfp_PREM; break;
+ case Iop_PRem1F64: fpop = Xfp_PREM1; break;
default: break;
}
if (fpop != Xfp_INVALID) {
case Iop_MulF64: vex_printf("MulF64"); return;
case Iop_DivF64: vex_printf("DivF64"); return;
- case Iop_ScaleF64: vex_printf("ScaleF64"); return;
- case Iop_AtanF64: vex_printf("AtanF64"); return;
- case Iop_Yl2xF64: vex_printf("Yl2xF64"); return;
- case Iop_Yl2xp1F64: vex_printf("Yl2xp1F64"); return;
- case Iop_PRemF64: vex_printf("PRemF64"); return;
- case Iop_PRemC3210F64: vex_printf("PRemC3210F64"); return;
- case Iop_NegF64: vex_printf("NegF64"); return;
- case Iop_SqrtF64: vex_printf("SqrtF64"); return;
+ case Iop_ScaleF64: vex_printf("ScaleF64"); return;
+ case Iop_AtanF64: vex_printf("AtanF64"); return;
+ case Iop_Yl2xF64: vex_printf("Yl2xF64"); return;
+ case Iop_Yl2xp1F64: vex_printf("Yl2xp1F64"); return;
+ case Iop_PRemF64: vex_printf("PRemF64"); return;
+ case Iop_PRemC3210F64: vex_printf("PRemC3210F64"); return;
+ case Iop_PRem1F64: vex_printf("PRem1F64"); return;
+ case Iop_PRem1C3210F64: vex_printf("PRem1C3210F64"); return;
+ case Iop_NegF64: vex_printf("NegF64"); return;
+ case Iop_SqrtF64: vex_printf("SqrtF64"); return;
case Iop_AbsF64: vex_printf("AbsF64"); return;
case Iop_SinF64: vex_printf("SinF64"); return;
case Iop_32to8: UNARY(Ity_I8,Ity_I32);
- case Iop_ScaleF64: case Iop_PRemF64:
+ case Iop_ScaleF64: case Iop_PRemF64: case Iop_PRem1F64:
case Iop_AtanF64: case Iop_Yl2xF64: case Iop_Yl2xp1F64:
case Iop_AddF64: case Iop_SubF64: case Iop_MulF64: case Iop_DivF64:
BINARY(Ity_F64,Ity_F64,Ity_F64);
- case Iop_PRemC3210F64:
+ case Iop_PRemC3210F64: case Iop_PRem1C3210F64:
case Iop_CmpF64:
BINARY(Ity_I32,Ity_F64,Ity_F64);
case Iop_NegF64: case Iop_AbsF64: case Iop_SqrtF64:
Iop_AddF64, Iop_SubF64, Iop_MulF64, Iop_DivF64, /* Iop_RemF64, */
/* Binary ops supported by IA32 but not mandated by 754. */
- Iop_AtanF64, /* FPATAN, arctan(arg1/arg2) */
- Iop_Yl2xF64, /* FYL2X, arg1 * log2(arg2) */
- Iop_Yl2xp1F64, /* FYL2XP1, arg1 * log2(arg2+1.0) */
- Iop_PRemF64, /* FPREM, remainder(arg1/arg2) */
- Iop_PRemC3210F64, /* C3210 flags resulting from FPREM, :: I32 */
- Iop_ScaleF64, /* FSCALE, arg1 * (2^RoundTowardsZero(arg2)) */
+ Iop_AtanF64, /* FPATAN, arctan(arg1/arg2) */
+ Iop_Yl2xF64, /* FYL2X, arg1 * log2(arg2) */
+ Iop_Yl2xp1F64, /* FYL2XP1, arg1 * log2(arg2+1.0) */
+ Iop_PRemF64, /* FPREM, non-IEEE remainder(arg1/arg2) */
+ Iop_PRemC3210F64, /* C3210 flags resulting from FPREM, :: I32 */
+ Iop_PRem1F64, /* FPREM1, IEEE remainder(arg1/arg2) */
+ Iop_PRem1C3210F64, /* C3210 flags resulting from FPREM1, :: I32 */
+ Iop_ScaleF64, /* FSCALE, arg1 * (2^RoundTowardsZero(arg2)) */
+ /* Note that on x86 guest, PRem1{C3210} has the same behaviour
+ as the IEEE mandated RemF64, except it is limited in the
+ range of its operand. Hence the partialness. */
/* Unary operations mandated by IEEE754. */
Iop_NegF64, Iop_SqrtF64,