From: Julian Seward Date: Mon, 13 Jun 2005 12:17:27 +0000 (+0000) Subject: Implement fldenv/fstenv on amd64. X-Git-Tag: svn/VALGRIND_3_0_1^2~128 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6c95a725df5bee8def3a1adc548af8c319aa519d;p=thirdparty%2Fvalgrind.git Implement fldenv/fstenv on amd64. git-svn-id: svn://svn.valgrind.org/vex/trunk@1206 --- diff --git a/VEX/priv/guest-amd64/gdefs.h b/VEX/priv/guest-amd64/gdefs.h index 1cf7f6f171..6279014318 100644 --- a/VEX/priv/guest-amd64/gdefs.h +++ b/VEX/priv/guest-amd64/gdefs.h @@ -105,6 +105,10 @@ extern ULong amd64g_check_ldmxcsr ( ULong mxcsr ); extern ULong amd64g_create_mxcsr ( ULong sseround ); +extern VexEmWarn amd64g_dirtyhelper_FLDENV ( VexGuestAMD64State*, HWord ); + +extern void amd64g_dirtyhelper_FSTENV ( VexGuestAMD64State*, HWord ); + /* Translate a guest virtual_addr into a guest linear address by consulting the supplied LDT/GDT structures. Their representation must be as specified in pub/libvex_guest_amd64.h. To indicate a diff --git a/VEX/priv/guest-amd64/ghelpers.c b/VEX/priv/guest-amd64/ghelpers.c index 885d267d65..1157e85cbe 100644 --- a/VEX/priv/guest-amd64/ghelpers.c +++ b/VEX/priv/guest-amd64/ghelpers.c @@ -1308,6 +1308,98 @@ ULong amd64g_create_fpucw ( ULong fpround ) } +/* This is used to implement 'fldenv'. + Reads 28 bytes at x87_state[0 .. 27]. */ +/* CALLED FROM GENERATED CODE */ +/* DIRTY HELPER */ +VexEmWarn amd64g_dirtyhelper_FLDENV ( /*OUT*/VexGuestAMD64State* vex_state, + /*IN*/HWord x87_state) +{ + Int stno, preg; + UInt tag; + UChar* vexTags = (UChar*)(&vex_state->guest_FPTAG[0]); + Fpu_State* x87 = (Fpu_State*)x87_state; + UInt ftop = (x87->env[FP_ENV_STAT] >> 11) & 7; + UInt tagw = x87->env[FP_ENV_TAG]; + UInt fpucw = x87->env[FP_ENV_CTRL]; + ULong c3210 = x87->env[FP_ENV_STAT] & 0x4700; + VexEmWarn ew; + ULong fpround; + ULong pair; + + /* Copy tags */ + for (stno = 0; stno < 8; stno++) { + preg = (stno + ftop) & 7; + tag = (tagw >> (2*preg)) & 3; + if (tag == 3) { + /* register is empty */ + vexTags[preg] = 0; + } else { + /* register is non-empty */ + vexTags[preg] = 1; + } + } + + /* stack pointer */ + vex_state->guest_FTOP = ftop; + + /* status word */ + vex_state->guest_FC3210 = c3210; + + /* handle the control word, setting FPROUND and detecting any + emulation warnings. */ + pair = amd64g_check_fldcw ( (ULong)fpucw ); + fpround = pair & 0xFFFFFFFFULL; + ew = (VexEmWarn)(pair >> 32); + + vex_state->guest_FPROUND = fpround & 3; + + /* emulation warnings --> caller */ + return ew; +} + + +/* CALLED FROM GENERATED CODE */ +/* DIRTY HELPER */ +/* Create an x87 FPU env from the guest state, as close as we can + approximate it. Writes 28 bytes at x87_state[0..27]. */ +void amd64g_dirtyhelper_FSTENV ( /*IN*/VexGuestAMD64State* vex_state, + /*OUT*/HWord x87_state ) +{ + Int i, stno, preg; + UInt tagw; + UChar* vexTags = (UChar*)(&vex_state->guest_FPTAG[0]); + Fpu_State* x87 = (Fpu_State*)x87_state; + UInt ftop = vex_state->guest_FTOP; + ULong c3210 = vex_state->guest_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_STAT] + = toUShort(((ftop & 7) << 11) | (c3210 & 0x4700)); + x87->env[FP_ENV_CTRL] + = toUShort(amd64g_create_fpucw( vex_state->guest_FPROUND )); + + /* Compute the x87 tag word. */ + tagw = 0; + for (stno = 0; stno < 8; stno++) { + preg = (stno + ftop) & 7; + if (vexTags[preg] == 0) { + /* register is empty */ + tagw |= (3 << (2*preg)); + } else { + /* register is full. */ + tagw |= (0 << (2*preg)); + } + } + x87->env[FP_ENV_TAG] = toUShort(tagw); + + /* We don't dump the x87 registers, tho. */ +} + + /*---------------------------------------------------------------*/ /*--- Misc integer helpers, including rotates and CPUID. ---*/ /*---------------------------------------------------------------*/ diff --git a/VEX/priv/guest-amd64/toIR.c b/VEX/priv/guest-amd64/toIR.c index ca4d5aa03a..c4d1815cca 100644 --- a/VEX/priv/guest-amd64/toIR.c +++ b/VEX/priv/guest-amd64/toIR.c @@ -4596,64 +4596,62 @@ ULong dis_FPU ( /*OUT*/Bool* decode_ok, fp_pop(); break; -//.. case 4: { /* FLDENV m108 */ -//.. /* Uses dirty helper: -//.. VexEmWarn x86g_do_FLDENV ( VexGuestX86State*, Addr32 ) */ -//.. IRTemp ew = newTemp(Ity_I32); -//.. IRDirty* d = unsafeIRDirty_0_N ( -//.. 0/*regparms*/, -//.. "x86g_dirtyhelper_FLDENV", -//.. &x86g_dirtyhelper_FLDENV, -//.. mkIRExprVec_1( mkexpr(addr) ) -//.. ); -//.. d->needsBBP = True; -//.. d->tmp = ew; -//.. /* declare we're reading memory */ -//.. d->mFx = Ifx_Read; -//.. d->mAddr = mkexpr(addr); -//.. d->mSize = 28; -//.. -//.. /* 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) ); -//.. -//.. /* ew contains any emulation warning we may need to -//.. issue. If needed, side-exit to the next insn, -//.. reporting the warning, so that Valgrind's dispatcher -//.. sees the warning. */ -//.. put_emwarn( mkexpr(ew) ); -//.. stmt( -//.. IRStmt_Exit( -//.. binop(Iop_CmpNE32, mkexpr(ew), mkU32(0)), -//.. Ijk_EmWarn, -//.. IRConst_U32( ((Addr32)guest_eip_bbstart)+delta) -//.. ) -//.. ); -//.. -//.. DIP("fldenv %s\n", dis_buf); -//.. break; -//.. } + case 4: { /* FLDENV m28 */ + /* Uses dirty helper: + VexEmWarn amd64g_do_FLDENV ( VexGuestX86State*, HWord ) */ + IRTemp ew = newTemp(Ity_I32); + IRTemp w64 = newTemp(Ity_I64); + IRDirty* d = unsafeIRDirty_0_N ( + 0/*regparms*/, + "amd64g_dirtyhelper_FLDENV", + &amd64g_dirtyhelper_FLDENV, + mkIRExprVec_1( mkexpr(addr) ) + ); + d->needsBBP = True; + d->tmp = w64; + /* declare we're reading memory */ + d->mFx = Ifx_Read; + d->mAddr = mkexpr(addr); + d->mSize = 28; + + /* declare we're writing guest state */ + d->nFxState = 4; + + 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_FPTAGS; + d->fxState[1].size = 8 * sizeof(UChar); + + d->fxState[2].fx = Ifx_Write; + d->fxState[2].offset = OFFB_FPROUND; + d->fxState[2].size = sizeof(ULong); + + d->fxState[3].fx = Ifx_Write; + d->fxState[3].offset = OFFB_FC3210; + d->fxState[3].size = sizeof(ULong); + + stmt( IRStmt_Dirty(d) ); + + /* ew contains any emulation warning we may need to + issue. If needed, side-exit to the next insn, + reporting the warning, so that Valgrind's dispatcher + sees the warning. */ + assign(ew, unop(Iop_64to32,mkexpr(w64)) ); + put_emwarn( mkexpr(ew) ); + stmt( + IRStmt_Exit( + binop(Iop_CmpNE32, mkexpr(ew), mkU32(0)), + Ijk_EmWarn, + IRConst_U64( guest_rip_bbstart+delta ) + ) + ); + + DIP("fldenv %s\n", dis_buf); + break; + } case 5: {/* FLDCW */ /* The only thing we observe in the control word is the @@ -4695,51 +4693,51 @@ ULong dis_FPU ( /*OUT*/Bool* decode_ok, break; } -//.. case 6: { /* FNSTENV m28 */ -//.. /* Uses dirty helper: -//.. void x86g_do_FSTENV ( VexGuestX86State*, UInt ) */ -//.. IRDirty* d = unsafeIRDirty_0_N ( -//.. 0/*regparms*/, -//.. "x86g_dirtyhelper_FSTENV", -//.. &x86g_dirtyhelper_FSTENV, -//.. mkIRExprVec_1( mkexpr(addr) ) -//.. ); -//.. d->needsBBP = True; -//.. /* declare we're writing memory */ -//.. d->mFx = Ifx_Write; -//.. d->mAddr = mkexpr(addr); -//.. d->mSize = 28; -//.. -//.. /* declare we're reading guest state */ -//.. d->nFxState = 4; -//.. -//.. 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_FPTAGS; -//.. d->fxState[1].size = 8 * sizeof(UChar); -//.. -//.. d->fxState[2].fx = Ifx_Read; -//.. d->fxState[2].offset = OFFB_FPROUND; -//.. d->fxState[2].size = sizeof(UInt); -//.. -//.. d->fxState[3].fx = Ifx_Read; -//.. d->fxState[3].offset = OFFB_FC3210; -//.. d->fxState[3].size = sizeof(UInt); -//.. -//.. stmt( IRStmt_Dirty(d) ); -//.. -//.. DIP("fnstenv %s\n", dis_buf); -//.. break; -//.. } + case 6: { /* FNSTENV m28 */ + /* Uses dirty helper: + void amd64g_do_FSTENV ( VexGuestAMD64State*, HWord ) */ + IRDirty* d = unsafeIRDirty_0_N ( + 0/*regparms*/, + "amd64g_dirtyhelper_FSTENV", + &amd64g_dirtyhelper_FSTENV, + mkIRExprVec_1( mkexpr(addr) ) + ); + d->needsBBP = True; + /* declare we're writing memory */ + d->mFx = Ifx_Write; + d->mAddr = mkexpr(addr); + d->mSize = 28; + + /* declare we're reading guest state */ + d->nFxState = 4; + + 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_FPTAGS; + d->fxState[1].size = 8 * sizeof(UChar); + + d->fxState[2].fx = Ifx_Read; + d->fxState[2].offset = OFFB_FPROUND; + d->fxState[2].size = sizeof(ULong); + + d->fxState[3].fx = Ifx_Read; + d->fxState[3].offset = OFFB_FC3210; + d->fxState[3].size = sizeof(ULong); + + stmt( IRStmt_Dirty(d) ); + + 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. */ - /* ULong x86h_create_fpucw ( ULong fpround ) */ + /* ULong amd64g_create_fpucw ( ULong fpround ) */ DIP("fnstcw %s\n", dis_buf); storeLE( mkexpr(addr), diff --git a/VEX/priv/guest-generic/g_generic_x87.h b/VEX/priv/guest-generic/g_generic_x87.h index 2901612955..50f0d69adf 100644 --- a/VEX/priv/guest-generic/g_generic_x87.h +++ b/VEX/priv/guest-generic/g_generic_x87.h @@ -75,6 +75,26 @@ extern void convert_f80le_to_f64le ( /*IN*/UChar* f80, /*OUT*/UChar* f64 ); +/* Layout of the real x87 state. */ +typedef + struct { + UShort env[14]; + UChar reg[80]; + } + Fpu_State; + +/* Offsets, in 16-bit ints, into the FPU environment (env) area. */ +#define FP_ENV_CTRL 0 +#define FP_ENV_STAT 2 +#define FP_ENV_TAG 4 +#define FP_ENV_IP 6 /* and 7 */ +#define FP_ENV_CS 8 +#define FP_ENV_OPOFF 10 /* and 11 */ +#define FP_ENV_OPSEL 12 +#define FP_REG(ii) (10*(7-(ii))) + + + #endif /* ndef __G_GENERIC_X87_H */ /*---------------------------------------------------------------*/ diff --git a/VEX/priv/guest-x86/ghelpers.c b/VEX/priv/guest-x86/ghelpers.c index cd221c0469..d3954590af 100644 --- a/VEX/priv/guest-x86/ghelpers.c +++ b/VEX/priv/guest-x86/ghelpers.c @@ -1234,23 +1234,8 @@ void x86g_storeF80le ( UInt addrU, ULong f64 ) /*----------------------------------------------*/ /* Layout of the real x87 state. */ - -typedef - struct { - UShort env[14]; - UChar reg[80]; - } - Fpu_State; - -/* Offsets, in 16-bit ints, into the FPU environment (env) area. */ -#define FP_ENV_CTRL 0 -#define FP_ENV_STAT 2 -#define FP_ENV_TAG 4 -#define FP_ENV_IP 6 /* and 7 */ -#define FP_ENV_CS 8 -#define FP_ENV_OPOFF 10 /* and 11 */ -#define FP_ENV_OPSEL 12 -#define FP_REG(ii) (10*(7-(ii))) +/* 13 June 05: Fpu_State and auxiliary constants was moved to + g_generic_x87.h */ /* CLEAN HELPER */ diff --git a/VEX/priv/guest-x86/toIR.c b/VEX/priv/guest-x86/toIR.c index cb0cfbe0e7..9f2924ff18 100644 --- a/VEX/priv/guest-x86/toIR.c +++ b/VEX/priv/guest-x86/toIR.c @@ -3733,7 +3733,7 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta ) case 4: { /* FLDENV m28 */ /* Uses dirty helper: - VexEmWarn x86g_do_FLDENV ( VexGuestX86State*, Addr32 ) */ + VexEmWarn x86g_do_FLDENV ( VexGuestX86State*, HWord ) */ IRTemp ew = newTemp(Ity_I32); IRDirty* d = unsafeIRDirty_0_N ( 0/*regparms*/, @@ -3832,7 +3832,7 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta ) case 6: { /* FNSTENV m28 */ /* Uses dirty helper: - void x86g_do_FSTENV ( VexGuestX86State*, UInt ) */ + void x86g_do_FSTENV ( VexGuestX86State*, HWord ) */ IRDirty* d = unsafeIRDirty_0_N ( 0/*regparms*/, "x86g_dirtyhelper_FSTENV",