also track C1.
* x86 guest: handle FXAM instruction
git-svn-id: svn://svn.valgrind.org/vex/trunk@249
UInt m_fpucw;
UInt m_ftag74;
UInt m_ftag30;
- UInt m_fc320;
+ UInt m_fc3210;
UInt sh_eax;
UInt sh_ebx;
extern Int VGOFF_(m_f7);
extern Int VGOFF_(m_ftag0);
extern Int VGOFF_(m_fpucw);
-extern Int VGOFF_(m_fc320);
+extern Int VGOFF_(m_fc3210);
/* Reg-alloc spill area (VG_MAX_SPILLSLOTS words long). */
extern Int VGOFF_(spillslots);
Int VGOFF_(m_f7) = INVALID_OFFSET;
Int VGOFF_(m_ftag0) = INVALID_OFFSET;
Int VGOFF_(m_fpucw) = INVALID_OFFSET;
-Int VGOFF_(m_fc320) = INVALID_OFFSET;
+Int VGOFF_(m_fc3210) = INVALID_OFFSET;
Int VGOFF_(spillslots) = INVALID_OFFSET;
Int VGOFF_(sh_eax) = INVALID_OFFSET;
/* 12 */ VGOFF_(m_eip) = alloc_BaB(1);
- /* 13 */ VGOFF_(m_ftop) = alloc_BaB(1);
- /* 14 */ VGOFF_(m_f0) = alloc_BaB(2);
- /* 16 */ VGOFF_(m_f1) = alloc_BaB(2);
- /* 18 */ VGOFF_(m_f2) = alloc_BaB(2);
- /* 20 */ VGOFF_(m_f3) = alloc_BaB(2);
- /* 22 */ VGOFF_(m_f4) = alloc_BaB(2);
- /* 24 */ VGOFF_(m_f5) = alloc_BaB(2);
- /* 26 */ VGOFF_(m_f6) = alloc_BaB(2);
- /* 28 */ VGOFF_(m_f7) = alloc_BaB(2);
- /* 30 */ VGOFF_(m_ftag0) = alloc_BaB(2);
- /* 32 */ VGOFF_(m_fpucw) = alloc_BaB(1);
- /* 33 */ VGOFF_(m_fc320) = alloc_BaB(1);
+ /* 13 */ VGOFF_(m_ftop) = alloc_BaB(1);
+ /* 14 */ VGOFF_(m_f0) = alloc_BaB(2);
+ /* 16 */ VGOFF_(m_f1) = alloc_BaB(2);
+ /* 18 */ VGOFF_(m_f2) = alloc_BaB(2);
+ /* 20 */ VGOFF_(m_f3) = alloc_BaB(2);
+ /* 22 */ VGOFF_(m_f4) = alloc_BaB(2);
+ /* 24 */ VGOFF_(m_f5) = alloc_BaB(2);
+ /* 26 */ VGOFF_(m_f6) = alloc_BaB(2);
+ /* 28 */ VGOFF_(m_f7) = alloc_BaB(2);
+ /* 30 */ VGOFF_(m_ftag0) = alloc_BaB(2);
+ /* 32 */ VGOFF_(m_fpucw) = alloc_BaB(1);
+ /* 33 */ VGOFF_(m_fc3210) = alloc_BaB(1);
/* 34 */ VGOFF_(sh_eax) = alloc_BaB(1);
/* 35 */ VGOFF_(sh_ecx) = alloc_BaB(1);
*(ULong*)(&VG_(baseBlock)[VGOFF_(m_f7)]) = VG_(threads)[tid].m_f7;
VG_(baseBlock)[VGOFF_(m_ftop)] = VG_(threads)[tid].m_ftop;
VG_(baseBlock)[VGOFF_(m_fpucw)] = VG_(threads)[tid].m_fpucw;
- VG_(baseBlock)[VGOFF_(m_fc320)] = VG_(threads)[tid].m_fc320;
+ VG_(baseBlock)[VGOFF_(m_fc3210)] = VG_(threads)[tid].m_fc3210;
VG_(baseBlock)[VGOFF_(m_ftag0)+0] = VG_(threads)[tid].m_ftag30;
VG_(baseBlock)[VGOFF_(m_ftag0)+1] = VG_(threads)[tid].m_ftag74;
VG_(threads)[tid].m_f7 = *(ULong*)(&VG_(baseBlock)[VGOFF_(m_f7)]);
VG_(threads)[tid].m_ftop = VG_(baseBlock)[VGOFF_(m_ftop)];
VG_(threads)[tid].m_fpucw = VG_(baseBlock)[VGOFF_(m_fpucw)];
- VG_(threads)[tid].m_fc320 = VG_(baseBlock)[VGOFF_(m_fc320)];
+ VG_(threads)[tid].m_fc3210 = VG_(baseBlock)[VGOFF_(m_fc3210)];
VG_(threads)[tid].m_ftag30 = VG_(baseBlock)[VGOFF_(m_ftag0)+0];
VG_(threads)[tid].m_ftag74 = VG_(baseBlock)[VGOFF_(m_ftag0)+1];
*(ULong*)(&VG_(baseBlock)[VGOFF_(m_f7)]) = junk64;
VG_(baseBlock)[VGOFF_(m_ftop)] = junk;
VG_(baseBlock)[VGOFF_(m_fpucw)] = junk;
- VG_(baseBlock)[VGOFF_(m_fc320)] = junk;
+ VG_(baseBlock)[VGOFF_(m_fc3210)] = junk;
VG_(baseBlock)[VGOFF_(m_ftag0)+0] = junk;
VG_(baseBlock)[VGOFF_(m_ftag0)+1] = junk;
#define CC_MASK_S 0x0080
#define CC_MASK_O 0x0800
+/* FPU flag masks */
+#define FC_MASK_C3 (1 << 14)
+#define FC_MASK_C2 (1 << 10)
+#define FC_MASK_C1 (1 << 9)
+#define FC_MASK_C0 (1 << 8)
+
/* eflags thunk descriptors. */
enum {
CC_OP_COPY, /* nothing to do -- ccs are in CC_SRC and up to date */
FPUCW[15:0] is the FPU's control word. FPUCW[31:16] is unused.
- FC320 contains the C3, C2 and C0 bits in the same place they are in
- the FPU's status word. (bits 14, 10 and 8 respectively). All other
- bits should be zero. The relevant mask to select just those bits
- is 0x4500.
+ FC3210 contains the C3, C2, C1 and C0 bits in the same place they
+ are in the FPU's status word. (bits 14, 10, 9, 8 respectively).
+ All other bits should be zero. The relevant mask to select just
+ those bits is 0x4700. To select C3, C2 and C0 only, the mask is
+ 0x4500.
*/
#define OFFB_FTOP (13*4)
#define OFFB_F0 (14*4)
#define OFFB_F7 (28*4)
#define OFFB_FTAG0 (30*4) // up to 30*4 + 7
#define OFFB_FPUCW (32*4)
-#define OFFB_FC320 (33*4)
+#define OFFB_FC3210 (33*4)
/* Don't forget to keep this up to date. */
-#define SIZEOF_X86H_STATE (OFFB_FC320 + 4)
+#define SIZEOF_X86H_STATE (OFFB_FC3210 + 4)
#include "main/vex_util.h"
#include "guest-x86/gdefs.h"
+/* --- Forwardses --- */
+UInt calculate_eflags_all ( UInt cc_op, UInt cc_src, UInt cc_dst );
+UInt calculate_eflags_c ( UInt cc_op, UInt cc_src, UInt cc_dst );
+UInt calculate_condition ( UInt/*Condcode*/ cond,
+ UInt cc_op, UInt cc_src, UInt cc_dst );
+UInt calculate_FXAM ( UInt tag, ULong dbl );
+
+
/* This file contains helper functions for x86 guest code.
Calls to these functions are generated by the back end.
These calls are of course in the host machine code and
return (Addr64)(& calculate_eflags_c);
if (vex_streq(function_name, "calculate_condition"))
return (Addr64)(& calculate_condition);
+ if (vex_streq(function_name, "calculate_FXAM"))
+ return (Addr64)(& calculate_FXAM);
vex_printf("\nx86 guest: can't find helper: %s\n", function_name);
vpanic("x86guest_findhelper");
}
sign bit, giving 64 in total.
*/
+static inline Bool host_is_little_endian ( void )
+{
+ UInt x = 0x76543210;
+ UChar* p = (UChar*)(&x);
+ return (*p == 0x10);
+}
+
+UInt calculate_FXAM ( UInt tag, ULong dbl )
+{
+ Bool mantissaIsZero;
+ Int bexp;
+ UChar sign;
+ UInt c1;
+ UChar* f64;
+
+ if (!host_is_little_endian) {
+ vassert(0);
+ }
+
+ /* vex_printf("calculate_FXAM ( %d, %llx ) .. ", tag, dbl ); */
+
+ f64 = (UChar*)(&dbl);
+ sign = (f64[7] >> 7) & 1;
+
+ /* First off, if the tag indicates the register was empty,
+ return 1,0,sign,1 */
+ if (tag == 0) {
+ /* vex_printf("Empty\n"); */
+ return FC_MASK_C3 | 0 | sign | FC_MASK_C0;
+ }
+
+ bexp = (f64[7] << 4) | ((f64[6] >> 4) & 0x0F);
+ bexp &= 0x7FF;
+
+ c1 = ((UInt)sign) << 9;
+
+ mantissaIsZero
+ = (f64[6] & 0x0F) == 0
+ && (f64[5] | f64[4] | f64[3] | f64[2] | f64[1] | f64[0]) == 0;
+
+ /* If both exponent and mantissa are zero, the value is zero.
+ Return 1,0,sign,0. */
+ if (bexp == 0 && mantissaIsZero) {
+ /* vex_printf("Zero\n"); */
+ return FC_MASK_C3 | 0 | sign | 0;
+ }
+
+ /* If exponent is zero but mantissa isn't, it's a denormal.
+ Return 1,1,sign,0. */
+ if (bexp == 0 && !mantissaIsZero) {
+ /* vex_printf("Denormal\n"); */
+ return FC_MASK_C3 | FC_MASK_C2 | sign | 0;
+ }
+
+ /* If the exponent is 7FF and the mantissa is zero, this is an infinity.
+ Return 0,1,sign,1. */
+ if (bexp == 0x7FF && mantissaIsZero) {
+ /* vex_printf("Inf\n"); */
+ return 0 | FC_MASK_C2 | sign | FC_MASK_C0;
+ }
+
+ /* If the exponent is 7FF and the mantissa isn't zero, this is a NaN.
+ Return 0,0,sign,1. */
+ if (bexp == 0x7FF && !mantissaIsZero) {
+ /* vex_printf("NaN\n"); */
+ return 0 | 0 | sign | FC_MASK_C0;
+ }
+
+ /* Uh, ok, we give up. It must be a normal finite number.
+ Return 0,1,sign,0.
+ */
+ /* vex_printf("normal\n"); */
+ return 0 | FC_MASK_C2 | sign | 0;
+}
+
/* Convert a IEEE754 double (64-bit) into an x87 extended double
(80-bit), mimicing the hardware fairly closely. Both numbers are
UInt ftop = (x87->env[FP_ENV_STAT] >> 11) & 7;
UInt tagw = x87->env[FP_ENV_TAG];
UInt fpucw = x87->env[FP_ENV_CTRL];
- UInt c320 = x87->env[FP_ENV_STAT] & 0x4500;
+ UInt c3210 = x87->env[FP_ENV_STAT] & 0x4700;
/* Copy registers and tags */
for (r = 0; r < 8; r++) {
*(UInt*)(vex_state + OFFB_FPUCW) = fpucw;
/* status word */
- *(UInt*)(vex_state + OFFB_FC320) = c320;
+ *(UInt*)(vex_state + OFFB_FC3210) = c3210;
}
UChar* vexTags = (UChar*)(vex_state + OFFB_FTAG0);
Fpu_State* x87 = (Fpu_State*)x87_state;
UInt ftop = *(UInt*)(vex_state + OFFB_FTOP);
- UInt c320 = *(UInt*)(vex_state + OFFB_FC320);
+ UInt c3210 = *(UInt*)(vex_state + OFFB_FC3210);
for (i = 0; i < 14; i++)
x87->env[i] = 0;
x87->env[1] = x87->env[3] = x87->env[5] = x87->env[13] = 0xFFFF;
x87->env[FP_ENV_CTRL] = (UShort)( *(UInt*)(vex_state + OFFB_FPUCW) );
- x87->env[FP_ENV_STAT] = ((ftop & 7) << 11) | (c320 & 0x4500);
+ x87->env[FP_ENV_STAT] = ((ftop & 7) << 11) | (c3210 & 0x4700);
tagw = 0;
for (r = 0; r < 8; r++) {
stmt( IRStmt_Put( OFFB_FTOP, e ) );
}
-/* --------- Get/set the C320 bits of the control word. --------- */
+/* --------- Get/set the C3210 bits of the control word. --------- */
-static IRExpr* get_C320 ( void )
+static IRExpr* get_C3210 ( void )
{
- return IRExpr_Get( OFFB_FC320, Ity_I32 );
+ return IRExpr_Get( OFFB_FC3210, Ity_I32 );
}
-static void put_C320 ( IRExpr* e )
+static void put_C3210 ( IRExpr* e )
{
- stmt( IRStmt_Put( OFFB_FC320, e ) );
+ stmt( IRStmt_Put( OFFB_FC3210, e ) );
}
/* --------- Get/set the FPU control word. --------- */
IRExpr_GetI( off_ST(i), Ity_F64, OFFB_F0, OFFB_F7+8-1 );
}
+static IRExpr* get_ST_UNCHECKED_as_ULong ( Int i )
+{
+ return
+ IRExpr_GetI( off_ST(i), Ity_I64, OFFB_F0, OFFB_F7+8-1 );
+}
+
/* Given i, generate an expression yielding
is_full(i) ? ST(i) : NaN
*/
put_ST_UNCHECKED(0, unop(Iop_NegF64, get_ST(0)));
break;
+ case 0xE5: { /* FXAM */
+ /* This is an interesting one. It examines %st(0),
+ regardless of whether the tag says it's empty or not.
+ Here, just pass both the tag (in our format) and the
+ value (as a double, actually a ULong) to a helper
+ function. */
+ IRExpr** args;
+ DIP("fxam");
+ args = LibVEX_Alloc(3 * sizeof(IRExpr*));
+ args[0] = unop(Iop_8Uto32, get_ST_TAG(0));
+ args[1] = get_ST_UNCHECKED_as_ULong(0);
+ args[2] = NULL;
+ put_C3210(IRExpr_CCall("calculate_FXAM", Ity_I32, args));
+ break;
+ }
+
case 0xE8: /* FLD1 */
DIP("fld1");
fp_push();
fp_pop();
break;
+ case 0xFA: /* FSQRT */
+ DIP("fsqrt\n");
+ put_ST_UNCHECKED(0, unop(Iop_SqrtF64, get_ST(0)));
+ break;
+
case 0xFE: /* FSIN */
DIP("fsin\n");
put_ST_UNCHECKED(0, unop(Iop_SinF64, get_ST(0)));
case 0xE9: /* FUCOMPP %st(0),%st(1) */
DIP("fucompp %%st(0),%%st(1)\n");
- put_C320( binop(Iop_Shl32,
- binop(Iop_CmpF64, get_ST(0), get_ST(1)),
- mkU8(8)) );
+ /* This forces C1 to zero, which isn't right. */
+ put_C3210(
+ binop( Iop_And32,
+ binop(Iop_Shl32,
+ binop(Iop_CmpF64, get_ST(0), get_ST(1)),
+ mkU8(8)),
+ mkU32(0x4500)
+ ));
fp_pop();
fp_pop();
break;
case 0xE0 ... 0xE7: /* FUCOM %st(0),%st(?) */
r_dst = (UInt)modrm - 0xE0;
DIP("fucom %%st(0),%%st(%d)\n", r_dst);
- put_C320( binop(Iop_Shl32,
- binop(Iop_CmpF64, get_ST(0), get_ST(r_dst)),
- mkU8(8)) );
+ /* This forces C1 to zero, which isn't right. */
+ put_C3210(
+ binop( Iop_And32,
+ binop(Iop_Shl32,
+ binop(Iop_CmpF64, get_ST(0), get_ST(r_dst)),
+ mkU8(8)),
+ mkU32(0x4500)
+ ));
break;
case 0xE8 ... 0xEF: /* FUCOMP %st(0),%st(?) */
r_dst = (UInt)modrm - 0xE8;
DIP("fucomp %%st(0),%%st(%d)\n", r_dst);
- put_C320( binop(Iop_Shl32,
- binop(Iop_CmpF64, get_ST(0), get_ST(r_dst)),
- mkU8(8)) );
+ /* This forces C1 to zero, which isn't right. */
+ put_C3210(
+ binop( Iop_And32,
+ binop(Iop_Shl32,
+ binop(Iop_CmpF64, get_ST(0), get_ST(r_dst)),
+ mkU8(8)),
+ mkU32(0x4500)
+ ));
fp_pop();
break;
DIP("fnstsw %%ax\n");
/* Invent a plausible-looking FPU status word value and
dump it in %AX:
- ((ftop & 7) << 11) | (c320 & 0x4500)
+ ((ftop & 7) << 11) | (c3210 & 0x4700)
*/
putIReg(2, R_EAX,
unop(Iop_32to16,
binop(Iop_Shl32,
binop(Iop_And32, get_ftop(), mkU32(7)),
mkU8(11)),
- binop(Iop_And32, get_C320(), mkU32(0x4500))
+ binop(Iop_And32, get_C3210(), mkU32(0x4700))
)));
break;
/* According to the Intel manual, "repne movs" should never occur, but
* in practice it has happened, so allow for it here... */
case 0xA4: sz = 1; /* REPNE MOVS<sz> */
+ vassert(0);
//-- case 0xA5:
// dis_REP_op ( CondNZ, dis_MOVS, sz, eip_orig,
// guest_eip+delta, "repne movs" );
{
switch (op) {
case Xfp_NEGATE: *p++ = 0xD9; *p++ = 0xE0; break;
+ case Xfp_SQRT: *p++ = 0xD9; *p++ = 0xFA; break;
case Xfp_SIN: *p++ = 0xD9; *p++ = 0xFE; break;
case Xfp_COS: *p++ = 0xD9; *p++ = 0xFF; break;
default: vpanic("do_fop1_st: unknown op");
/* --------- CCALL --------- */
case Iex_CCall: {
Addr64 helper;
- Int i, nargs;
- UInt target;
+ Int i, n_args, n_arg_ws;
+ UInt target;
IRExpr* arg;
+ IRType arg_ty;
HReg dst = newVRegI(env);
vassert(ty == Ity_I32);
- /* be very restrictive for now. Only 32-bit ints allowed
- for args and return type. */
+
+ /* be very restrictive for now. Only 32/64-bit ints allowed
+ for args, and 32 bits for return type. */
if (e->Iex.CCall.retty != Ity_I32)
goto irreducible;
+
/* push args on the stack, right to left. */
- nargs = 0;
- while (e->Iex.CCall.args[nargs]) nargs++;
- for (i = nargs-1; i >= 0; i--) {
- arg = e->Iex.CCall.args[i];
- if (typeOfIRExpr(env->type_env,arg) != Ity_I32)
- goto irreducible;
- addInstr(env, X86Instr_Push(iselIntExpr_RMI(env, arg)));
+ n_arg_ws = n_args = 0;
+ while (e->Iex.CCall.args[n_args]) n_args++;
+
+ for (i = n_args-1; i >= 0; i--) {
+ arg = e->Iex.CCall.args[i];
+ arg_ty = typeOfIRExpr(env->type_env, arg);
+ if (arg_ty == Ity_I32) {
+ addInstr(env, X86Instr_Push(iselIntExpr_RMI(env, arg)));
+ n_arg_ws ++;
+ } else
+ if (arg_ty == Ity_I64) {
+ HReg rHi, rLo;
+ iselIntExpr64(&rHi, &rLo, env, arg);
+ addInstr(env, X86Instr_Push(X86RMI_Reg(rHi)));
+ addInstr(env, X86Instr_Push(X86RMI_Reg(rLo)));
+ n_arg_ws += 2;
+ }
+ else goto irreducible;
}
+
/* Find the function to call. Since the host -- for which we
are generating code -- is a 32-bit machine (x86) -- the upper
32 bit of the helper address should be zero. */
X86RMI_Imm(target),
hregX86_EAX()));
addInstr(env, X86Instr_Call(hregX86_EAX()));
- if (nargs > 0)
+ if (n_arg_ws > 0)
addInstr(env, X86Instr_Alu32R(Xalu_ADD,
- X86RMI_Imm(4*nargs),
+ X86RMI_Imm(4*n_arg_ws),
hregX86_ESP()));
addInstr(env, mk_MOVsd_RR(hregX86_EAX(), dst));
return dst;
HReg rf = iselDblExpr(env, e->Iex.Binop.arg2);
HReg rrm = iselIntExpr_R(env, e->Iex.Binop.arg1);
HReg rrm2 = newVRegI(env);
- HReg dst = newVRegI(env);
HReg tLo = newVRegI(env);
HReg tHi = newVRegI(env);
*rHi = tHi;
*rLo = tLo;
- return dst;
+ return;
+ }
+
+ if (e->tag == Iex_GetI) {
+ /* First off, compute the index expression into an integer reg.
+ The referenced address will then be 0 + ebp + reg*1, that is,
+ an X86AMode_IRRS. */
+ vassert(e->Iex.GetI.ty == Ity_I64);
+ HReg tLo = newVRegI(env);
+ HReg tHi = newVRegI(env);
+ HReg idx = iselIntExpr_R(env, e->Iex.GetI.offset);
+
+ /* This (x86) is a little-endian target. The front end will
+ have laid out the baseblock in accordance with the back-end's
+ endianness, so this hardwired assumption here that the 64-bit
+ value is stored little-endian is OK. */
+ addInstr(env, X86Instr_Alu32R(
+ Xalu_MOV,
+ X86RMI_Mem(X86AMode_IRRS(0, hregX86_EBP(), idx, 0)),
+ tLo));
+ addInstr(env, X86Instr_Alu32R(
+ Xalu_MOV,
+ X86RMI_Mem(X86AMode_IRRS(4, hregX86_EBP(), idx, 0)),
+ tHi));
+ *rHi = tHi;
+ *rLo = tLo;
+ return;
}
ppIRExpr(e);
if (e->tag == Iex_Unop) {
X86FpOp fpop = Xfp_INVALID;
switch (e->Iex.Unop.op) {
- case Iop_NegF64: fpop = Xfp_NEGATE; break;
- case Iop_SinF64: fpop = Xfp_SIN; break;
- case Iop_CosF64: fpop = Xfp_COS; break;
+ case Iop_NegF64: fpop = Xfp_NEGATE; break;
+ case Iop_SqrtF64: fpop = Xfp_SQRT; break;
+ case Iop_SinF64: fpop = Xfp_SIN; break;
+ case Iop_CosF64: fpop = Xfp_COS; break;
default: break;
}
if (fpop != Xfp_INVALID) {
case Iop_DivF64: vex_printf("DivF64"); return;
case Iop_CmpF64: vex_printf("CmpF64"); return;
case Iop_NegF64: vex_printf("NegF64"); return;
+ case Iop_SqrtF64: vex_printf("SqrtF64"); return;
case Iop_SinF64: vex_printf("SinF64"); return;
case Iop_CosF64: vex_printf("CosF64"); return;
BINARY(Ity_F64,Ity_F64,Ity_F64);
case Iop_CmpF64:
BINARY(Ity_I32,Ity_F64,Ity_F64);
- case Iop_NegF64: case Iop_SinF64: case Iop_CosF64:
+ case Iop_NegF64: case Iop_SqrtF64: case Iop_SinF64: case Iop_CosF64:
UNARY(Ity_F64,Ity_F64);
case Iop_I32toF64: UNARY(Ity_F64,Ity_I32);