UInt c1;
UChar* f64;
- if (!host_is_little_endian()) {
- vassert(0);
- }
+ vassert(host_is_little_endian());
/* vex_printf("calculate_FXAM ( %d, %llx ) .. ", tag, dbl ); */
/* CLEAN HELPER */
-/* native_fpucw[15:0] contains a x87 native format FPU control word.
+/* 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.
*/
/* CLEAN HELPER */
-/* native_fpucw[15:0] contains a SSE native format MXCSR value.
+/* mxcsr[15:0] contains a SSE native format MXCSR value.
Extract from it the required SSEROUND value and any resulting
emulation warning, and return (warn << 32) | sseround value.
*/
}
+/* CALLED FROM GENERATED CODE */
+/* DIRTY HELPER (writes guest state) */
+/* Initialise the x87 FPU state as per 'finit'. */
+void x86g_dirtyhelper_FINIT ( VexGuestX86State* gst )
+{
+ Int i;
+ gst->guest_FTOP = 0;
+ for (i = 0; i < 8; i++) {
+ gst->guest_FPTAG[i] = 0; /* empty */
+ gst->guest_FPREG[i] = 0; /* IEEE754 64-bit zero */
+ }
+ gst->guest_FPROUND = (UInt)Irrm_NEAREST;
+ gst->guest_FC3210 = 0;
+}
+
+
/* This is used to implement both 'frstor' and 'fldenv'. The latter
appears to differ from the former only in that the 8 FP registers
themselves are not transferred into the guest state. */
/*IN*/UChar* x87_state,
/*OUT*/VexGuestX86State* vex_state )
{
- Int r;
+ Int stno, preg;
UInt tag;
Double* vexRegs = (Double*)(&vex_state->guest_FPREG[0]);
UChar* vexTags = (UChar*)(&vex_state->guest_FPTAG[0]);
ULong pair;
/* Copy registers and tags */
- for (r = 0; r < 8; r++) {
- tag = (tagw >> (2*r)) & 3;
+ tagw = 0;
+ for (stno = 0; stno < 8; stno++) {
+ preg = (stno + ftop) & 7;
+ tag = (tagw >> (2*preg)) & 3;
if (tag == 3) {
/* register is empty */
- vexRegs[r] = 0.0;
- vexTags[r] = 0;
+ /* hmm, if it's empty, does it still get written? Probably
+ safer to say it does. If we don't, memcheck could get out
+ of sync, in that it thinks all FP registers are defined by
+ this helper, but in reality some have not been updated. */
+ if (True || moveRegs)
+ vexRegs[preg] = 0.0;
+ vexTags[preg] = 0;
} else {
/* register is non-empty */
if (moveRegs)
- convert_f80le_to_f64le( &x87->reg[FP_REG(r)],
- (UChar*)&vexRegs[r] );
- vexTags[r] = 1;
+ convert_f80le_to_f64le( &x87->reg[10*stno],
+ (UChar*)&vexRegs[preg] );
+ vexTags[preg] = 1;
}
}
/* VISIBLE TO LIBVEX CLIENT */
+/* Create an x87 FPU state from the guest state, as close as
+ we can approximate it. */
void LibVEX_GuestX86_get_x87 ( /*IN*/VexGuestX86State* vex_state,
/*OUT*/UChar* x87_state )
{
- Int i, r;
+ Int i, stno, preg;
UInt tagw;
Double* vexRegs = (Double*)(&vex_state->guest_FPREG[0]);
UChar* vexTags = (UChar*)(&vex_state->guest_FPTAG[0]);
x87->env[FP_ENV_CTRL]
= (UShort)x86g_create_fpucw( vex_state->guest_FPROUND );
+ /* Dump the register stack in ST order. */
tagw = 0;
- for (r = 0; r < 8; r++) {
- if (vexTags[r] == 0) {
+ for (stno = 0; stno < 8; stno++) {
+ preg = (stno + ftop) & 7;
+ if (vexTags[preg] == 0) {
/* register is empty */
- tagw |= (3 << (2*r));
- convert_f64le_to_f80le( (UChar*)&vexRegs[r], &x87->reg[FP_REG(r)] );
+ tagw |= (3 << (2*preg));
+ convert_f64le_to_f80le( (UChar*)&vexRegs[preg],
+ &x87->reg[10*stno] );
} else {
/* register is full. */
- tagw |= (0 << (2*r));
- convert_f64le_to_f80le( (UChar*)&vexRegs[r], &x87->reg[FP_REG(r)] );
+ tagw |= (0 << (2*preg));
+ convert_f64le_to_f80le( (UChar*)&vexRegs[preg],
+ &x87->reg[10*stno] );
}
}
x87->env[FP_ENV_TAG] = tagw;
/* VISIBLE TO LIBVEX CLIENT */
void LibVEX_GuestX86_initialise ( /*OUT*/VexGuestX86State* vex_state )
{
- Int i;
-
vex_state->guest_EAX = 0;
vex_state->guest_ECX = 0;
vex_state->guest_EDX = 0;
vex_state->guest_EIP = 0;
- vex_state->guest_FTOP = 0;
- for (i = 0; i < 8; i++) {
- vex_state->guest_FPTAG[i] = 0; /* empty */
- vex_state->guest_FPREG[i] = 0; /* IEEE754 64-bit zero */
- }
- vex_state->guest_FPROUND = (UInt)Irrm_NEAREST;
- vex_state->guest_FC3210 = 0;
+ /* Initialise the simulated FPU */
+ x86g_dirtyhelper_FINIT( vex_state );
+ /* Initialse the SSE state. */
# define SSEZERO(_xmm) _xmm[0]=_xmm[1]=_xmm[2]=_xmm[3] = 0;
vex_state->guest_SSEROUND = (UInt)Irrm_NEAREST;
}
+/* CALLED FROM GENERATED CODE */
+/* DIRTY HELPER (reads guest state, writes guest mem) */
+void x86g_dirtyhelper_FXSAVE ( VexGuestX86State* gst, HWord addr )
+{
+ /* Somewhat roundabout, but at least it's simple. */
+ Fpu_State tmp;
+ UShort* addrS = (UShort*)addr;
+ UChar* addrC = (UChar*)addr;
+ U128* xmm = (U128*)(addr + 160);
+ UInt mxcsr;
+ UShort fp_tags;
+ UChar summary_tags;
+ Int r, stno;
+ UShort *srcS, *dstS;
+
+ LibVEX_GuestX86_get_x87( gst, (UChar*)&tmp );
+ mxcsr = x86g_create_mxcsr( gst->guest_SSEROUND );
+
+ /* Now build the proper fxsave image from the x87 image we just
+ made. */
+
+ addrS[0] = tmp.env[FP_ENV_CTRL]; /* FCW: fpu control word */
+ addrS[1] = tmp.env[FP_ENV_STAT]; /* FCW: fpu status word */
+
+ /* set addrS[2] in an endian-independent way */
+ summary_tags = 0;
+ fp_tags = tmp.env[FP_ENV_TAG];
+ for (r = 0; r < 8; r++) {
+ if ( ((fp_tags >> (2*r)) & 3) != 3 )
+ summary_tags |= (1 << r);
+ }
+ addrC[4] = summary_tags; /* FTW: tag summary byte */
+ addrC[5] = 0; /* pad */
+
+ addrS[3] = 0; /* FOP: fpu opcode (bogus) */
+ addrS[4] = 0;
+ addrS[5] = 0; /* FPU IP (bogus) */
+ addrS[6] = 0; /* FPU IP's segment selector (bogus) (although we
+ could conceivably dump %CS here) */
+
+ addrS[7] = 0; /* Intel reserved */
+
+ addrS[8] = 0; /* FPU DP (operand pointer) (bogus) */
+ addrS[9] = 0; /* FPU DP (operand pointer) (bogus) */
+ addrS[10] = 0; /* segment selector for above operand pointer; %DS
+ perhaps? */
+ addrS[11] = 0; /* Intel reserved */
+
+ addrS[12] = (UShort)mxcsr; /* MXCSR */
+ addrS[13] = (UShort)(mxcsr >> 16);
+
+ addrS[14] = 0xFFFF; /* MXCSR mask (lo16); who knows what for */
+ addrS[15] = 0xFFFF; /* MXCSR mask (hi16); who knows what for */
+
+ /* Copy in the FP registers, in ST order. */
+ for (stno = 0; stno < 8; stno++) {
+ srcS = (UShort*)(&tmp.reg[10*stno]);
+ dstS = (UShort*)(&addrS[16 + 8*stno]);
+ dstS[0] = srcS[0];
+ dstS[1] = srcS[1];
+ dstS[2] = srcS[2];
+ dstS[3] = srcS[3];
+ dstS[4] = srcS[4];
+ dstS[5] = 0;
+ dstS[6] = 0;
+ dstS[7] = 0;
+ }
+
+ /* That's the first 160 bytes of the image done. Now only %xmm0
+ .. %xmm7 remain to be copied. If the host is big-endian, these
+ need to be byte-swapped. */
+ vassert(host_is_little_endian());
+
+# define COPY_U128(_dst,_src) \
+ do { _dst[0] = _src[0]; _dst[1] = _src[1]; \
+ _dst[2] = _src[2]; _dst[3] = _src[3]; } \
+ while (0)
+
+ COPY_U128( xmm[0], gst->guest_XMM0 );
+ COPY_U128( xmm[1], gst->guest_XMM1 );
+ COPY_U128( xmm[2], gst->guest_XMM2 );
+ COPY_U128( xmm[3], gst->guest_XMM3 );
+ COPY_U128( xmm[4], gst->guest_XMM4 );
+ COPY_U128( xmm[5], gst->guest_XMM5 );
+ COPY_U128( xmm[6], gst->guest_XMM6 );
+ COPY_U128( xmm[7], gst->guest_XMM7 );
+
+# undef COPY_U128
+}
+
+
/* CALLED FROM GENERATED CODE */
/* DIRTY HELPER (reads guest state, writes guest mem) */
void x86g_dirtyhelper_FSAVE ( VexGuestX86State* gst, HWord addr )
to zero.
x87 FP Limitations:
+
+ * all arithmetic done at 64 bits
+
* no FP exceptions, except for handling stack over/underflow
+
* FP rounding mode observed only for float->int conversions
- and int->float conversions which could lose accuracy
+ and int->float conversions which could lose accuracy, and
+ for float-to-float rounding. For all other operations,
+ round-to-nearest is used, regardless.
+
* FP sin/cos/tan/sincos: C2 flag is always cleared. IOW the
simulation claims the argument is in-range (-2^63 <= arg <= 2^63)
even when it isn't.
+
* some of the FCOM cases could do with testing -- not convinced
that the args are the right way round.
+ * FSAVE does not re-initialise the FPU; it should do
+
+ * FINIT not only initialises the FPU environment, it also
+ zeroes all the FP registers. It should leave the registers
+ unchanged.
+
RDTSC returns zero, always.
SAHF should cause eflags[1] == 1, and in fact it produces 0. As
DIP("fnclex\n");
break;
+ case 0xE3: {
+ /* Uses dirty helper:
+ void x86g_do_FINIT ( VexGuestX86State* ) */
+ IRDirty* d = unsafeIRDirty_0_N (
+ 0/*regparms*/,
+ "x86g_dirtyhelper_FINIT",
+ &x86g_dirtyhelper_FINIT,
+ mkIRExprVec_0()
+ );
+ d->needsBBP = True;
+
+ /* declare we're writing guest state */
+ d->nFxState = 5;
+
+ d->fxState[0].fx = Ifx_Write;
+ d->fxState[0].offset = OFFB_FTOP;
+ d->fxState[0].size = sizeof(UInt);
+
+ d->fxState[1].fx = Ifx_Write;
+ d->fxState[1].offset = OFFB_FPREGS;
+ d->fxState[1].size = 8 * sizeof(ULong);
+
+ d->fxState[2].fx = Ifx_Write;
+ d->fxState[2].offset = OFFB_FPTAGS;
+ d->fxState[2].size = 8 * sizeof(UChar);
+
+ d->fxState[3].fx = Ifx_Write;
+ d->fxState[3].offset = OFFB_FPROUND;
+ d->fxState[3].size = sizeof(UInt);
+
+ d->fxState[4].fx = Ifx_Write;
+ d->fxState[4].offset = OFFB_FC3210;
+ d->fxState[4].size = sizeof(UInt);
+
+ stmt( IRStmt_Dirty(d) );
+
+ DIP("fninit");
+ break;
+ }
+
case 0xE8 ... 0xEF: /* FUCOMI %st(0),%st(?) */
fp_do_ucomi_ST0_STi( (UInt)modrm - 0xE8, False );
break;
/* Note, this doesn't handle SSE2 or SSE3. That is handled in a
later section, further on. */
+ insn = (UChar*)&guest_code[delta];
+
+ /* Treat fxsave specially. It should be doable even on an SSE0
+ (Pentium-II class) CPU. Hence be prepared to handle it on
+ any subarchitecture variant.
+ */
+
+ /* 0F AE /0 = FXSAVE m512 -- write x87 and SSE state to memory */
+ if (sz == 4 && insn[0] == 0x0F && insn[1] == 0xAE
+ && !epartIsReg(insn[2]) && gregOfRM(insn[2]) == 0) {
+ modrm = getIByte(delta+2);
+ vassert(sz == 4);
+ vassert(!epartIsReg(modrm));
+
+ addr = disAMode ( &alen, sorb, delta+2, dis_buf );
+ delta += 2+alen;
+
+ DIP("fxsave %s", dis_buf);
+
+ /* Uses dirty helper:
+ void x86g_do_FXSAVE ( VexGuestX86State*, UInt ) */
+ IRDirty* d = unsafeIRDirty_0_N (
+ 0/*regparms*/,
+ "x86g_dirtyhelper_FXSAVE",
+ &x86g_dirtyhelper_FXSAVE,
+ mkIRExprVec_1( mkexpr(addr) )
+ );
+ d->needsBBP = True;
+
+ /* declare we're writing memory */
+ d->mFx = Ifx_Write;
+ d->mAddr = mkexpr(addr);
+ d->mSize = 512;
+
+ /* declare we're reading guest state */
+ d->nFxState = 7;
+
+ d->fxState[0].fx = Ifx_Read;
+ d->fxState[0].offset = OFFB_FTOP;
+ d->fxState[0].size = sizeof(UInt);
+
+ d->fxState[1].fx = Ifx_Read;
+ d->fxState[1].offset = OFFB_FPREGS;
+ d->fxState[1].size = 8 * sizeof(ULong);
+
+ d->fxState[2].fx = Ifx_Read;
+ d->fxState[2].offset = OFFB_FPTAGS;
+ d->fxState[2].size = 8 * sizeof(UChar);
+
+ d->fxState[3].fx = Ifx_Read;
+ d->fxState[3].offset = OFFB_FPROUND;
+ d->fxState[3].size = sizeof(UInt);
+
+ d->fxState[4].fx = Ifx_Read;
+ d->fxState[4].offset = OFFB_FC3210;
+ d->fxState[4].size = sizeof(UInt);
+
+ d->fxState[5].fx = Ifx_Read;
+ d->fxState[5].offset = OFFB_XMM0;
+ d->fxState[5].size = 8 * sizeof(U128);
+
+ d->fxState[6].fx = Ifx_Read;
+ d->fxState[6].offset = OFFB_SSEROUND;
+ d->fxState[6].size = sizeof(UInt);
+
+ /* Be paranoid ... this assertion tries to ensure the 8 %xmm
+ images are packed back-to-back. If not, the value of
+ d->fxState[5].size is wrong. */
+ vassert(16 == sizeof(U128));
+ vassert(OFFB_XMM7 == (OFFB_XMM0 + 7 * 16));
+
+ stmt( IRStmt_Dirty(d) );
+
+ goto decode_success;
+ }
+
+ /* ------ SSE decoder main ------ */
+
/* Skip parts of the decoder which don't apply given the stated
guest subarchitecture. */
if (subarch == VexSubArchX86_sse0)
/* Otherwise we must be doing sse1 or sse2, so we can at least try
for SSE1 here. */
- insn = (UChar*)&guest_code[delta];
-
/* 0F 58 = ADDPS -- add 32Fx4 from R/M to R */
if (sz == 4 && insn[0] == 0x0F && insn[1] == 0x58) {
delta = dis_SSE_E_to_G_all( sorb, delta+2, "addps", Iop_Add32Fx4 );
goto decode_success;
}
- /* 0F AE /3 = STMXCSR m32 -- load %mxcsr */
+ /* 0F AE /3 = STMXCSR m32 -- store %mxcsr */
if (insn[0] == 0x0F && insn[1] == 0xAE
&& !epartIsReg(insn[2]) && gregOfRM(insn[2]) == 3) {
modrm = getIByte(delta+2);
mkIRExprCCall(
Ity_I32, 0/*regp*/,
"x86g_create_mxcsr", &x86g_create_mxcsr,
- mkIRExprVec_1( get_fpround() )
+ mkIRExprVec_1( get_sse_roundingmode() )
)
);
goto decode_success;