From: Dejan Jevtic Date: Wed, 18 Sep 2013 10:06:13 +0000 (+0000) Subject: mips32/mips64: additional VEX support for FCSR register. X-Git-Tag: svn/VALGRIND_3_9_0^2~29 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e9d111bc55a3f7153c2b6e816682dee29684ad2f;p=thirdparty%2Fvalgrind.git mips32/mips64: additional VEX support for FCSR register. Some mips fpu instructions are changing the value of the fcsr register so we need to update the value of the fcsr register in the guest state. git-svn-id: svn://svn.valgrind.org/vex/trunk@2766 --- diff --git a/VEX/priv/guest_mips_defs.h b/VEX/priv/guest_mips_defs.h index 2ab3868b76..b7fe1fb56c 100644 --- a/VEX/priv/guest_mips_defs.h +++ b/VEX/priv/guest_mips_defs.h @@ -78,6 +78,15 @@ extern VexGuestLayout mips64Guest_layout; /*---------------------------------------------------------*/ /*--- mips guest helpers ---*/ /*---------------------------------------------------------*/ +typedef enum { + CEILWS=0, CEILWD, CEILLS, CEILLD, + FLOORWS, FLOORWD, FLOORLS, FLOORLD, + ROUNDWS, ROUNDWD, ROUNDLS, ROUNDLD, + TRUNCWS, TRUNCWD, TRUNCLS, TRUNCLD, + CVTDS, CVTDW, CVTSD, CVTSW, + CVTWS, CVTWD, CVTDL, CVTLS, + CVTLD, CVTSL +} flt_op; extern UInt mips32_dirtyhelper_mfc0 ( UInt rd, UInt sel ); @@ -88,6 +97,9 @@ extern ULong mips64_dirtyhelper_dmfc0 ( UInt rd, UInt sel ); extern ULong mips64_dirtyhelper_rdhwr ( ULong rt, ULong rd ); #endif +extern UInt mips_dirtyhelper_calculate_FCSR ( void* guest_state, UInt fs, + flt_op op ); + /*---------------------------------------------------------*/ /*--- Condition code stuff ---*/ /*---------------------------------------------------------*/ diff --git a/VEX/priv/guest_mips_helpers.c b/VEX/priv/guest_mips_helpers.c index 1e2c20b3a8..0a2e5ee2e1 100644 --- a/VEX/priv/guest_mips_helpers.c +++ b/VEX/priv/guest_mips_helpers.c @@ -1092,6 +1092,128 @@ ULong mips64_dirtyhelper_rdhwr ( ULong rt, ULong rd ) } #endif +#define ASM_VOLATILE_ROUND32(fs, inst) \ + __asm__ volatile("ctc1 %3, $31" "\n\t" \ + "mtc1 %1, $f0" "\n\t" \ + "mtc1 %2, $f1" "\n\t" \ + ""#inst" $f0, $f0" "\n\t" \ + "cfc1 %0, $31" "\n\t" \ + : "=r" (ret) \ + : "r" (addr[fs]), "r" (addr[fs+1]), "r" (fcsr) \ + : "$f0", "$f1" \ + ); + +#define ASM_VOLATILE_ROUND64(fs, inst) \ + __asm__ volatile("ctc1 %2, $31" "\n\t" \ + "dmtc1 %1, $f0" "\n\t" \ + ""#inst" $f0, $f0" "\n\t" \ + "cfc1 %0, $31" "\n\t" \ + : "=r" (ret) \ + : "r" (addr[fs]), "r" (fcsr) \ + : "$f0" \ + ); + +/* TODO: Add cases for all fpu instructions because all fpu instructions are + change the value of FCSR register. */ +extern UInt mips_dirtyhelper_calculate_FCSR ( void* gs, UInt fs, flt_op inst ) +{ + UInt ret = 0; +#if defined(VGA_mips32) + VexGuestMIPS32State* guest_state = (VexGuestMIPS32State*)gs; + UInt *addr = (UInt *)&guest_state->guest_f0; +#define ASM_VOLATILE_ROUND(fs, inst) ASM_VOLATILE_ROUND32(fs, inst) +#else + VexGuestMIPS64State* guest_state = (VexGuestMIPS64State*)gs; + ULong *addr = (ULong *)&guest_state->guest_f0; +#define ASM_VOLATILE_ROUND(fs, inst) ASM_VOLATILE_ROUND64(fs, inst) +#endif + UInt fcsr = guest_state->guest_FCSR; + switch (inst) { + case ROUNDWD: + ASM_VOLATILE_ROUND(fs, round.w.d) + break; + case FLOORWS: + ASM_VOLATILE_ROUND(fs, floor.w.s) + break; + case FLOORWD: + ASM_VOLATILE_ROUND(fs, floor.w.d) + break; + case TRUNCWS: + ASM_VOLATILE_ROUND(fs, trunc.w.s) + break; + case TRUNCWD: + ASM_VOLATILE_ROUND(fs, trunc.w.d) + break; + case CEILWS: + ASM_VOLATILE_ROUND(fs, ceil.w.s) + break; + case CEILWD: + ASM_VOLATILE_ROUND(fs, ceil.w.d) + break; + case CEILLS: + ASM_VOLATILE_ROUND(fs, ceil.l.s) + break; + case CEILLD: + ASM_VOLATILE_ROUND(fs, ceil.l.d) + break; + case ROUNDLS: + ASM_VOLATILE_ROUND(fs, round.l.s) + break; + case ROUNDLD: + ASM_VOLATILE_ROUND(fs, round.l.d) + break; + case TRUNCLS: + ASM_VOLATILE_ROUND(fs, trunc.l.s) + break; + case TRUNCLD: + ASM_VOLATILE_ROUND(fs, trunc.l.d) + break; + case CVTDS: + ASM_VOLATILE_ROUND(fs, cvt.d.s) + break; + case CVTDW: + ASM_VOLATILE_ROUND(fs, cvt.d.w) + break; + case CVTDL: + ASM_VOLATILE_ROUND(fs, cvt.d.l) + break; + case CVTSW: + ASM_VOLATILE_ROUND(fs, cvt.s.w) + break; + case CVTSD: + ASM_VOLATILE_ROUND(fs, cvt.s.d) + break; + case CVTSL: + ASM_VOLATILE_ROUND(fs, cvt.s.l) + break; + case CVTWS: + ASM_VOLATILE_ROUND(fs, cvt.w.s) + break; + case CVTWD: + ASM_VOLATILE_ROUND(fs, cvt.w.d) + break; + case CVTLS: + ASM_VOLATILE_ROUND(fs, cvt.l.s) + break; + case CVTLD: + ASM_VOLATILE_ROUND(fs, cvt.l.d) + break; + case FLOORLS: + ASM_VOLATILE_ROUND(fs, floor.l.s) + break; + case FLOORLD: + ASM_VOLATILE_ROUND(fs, floor.l.d) + break; + case ROUNDWS: + ASM_VOLATILE_ROUND(fs, round.w.s) + break; + default: + vassert(0); + break; + } + return ret; +} + /*---------------------------------------------------------------*/ /*--- end guest_mips_helpers.c ---*/ /*---------------------------------------------------------------*/ diff --git a/VEX/priv/guest_mips_toIR.c b/VEX/priv/guest_mips_toIR.c index b3917b62c3..6f7eca9184 100644 --- a/VEX/priv/guest_mips_toIR.c +++ b/VEX/priv/guest_mips_toIR.c @@ -1088,6 +1088,41 @@ static void putFCSR(IRExpr * e) stmt(IRStmt_Put(offsetof(VexGuestMIPS32State, guest_FCSR), e)); } +static void calculateFCSR(UInt fs, UInt inst) +{ + IRDirty *d; + IRTemp fcsr = newTemp(Ity_I32); + /* IRExpr_BBPTR() => Need to pass pointer to guest + state to helper. */ + d = unsafeIRDirty_1_N(fcsr, 0, + "mips_dirtyhelper_calculate_FCSR", + &mips_dirtyhelper_calculate_FCSR, + mkIRExprVec_3(IRExpr_BBPTR(), + mkU32(fs), + mkU32(inst))); + + /* Declare we're reading guest state. */ + d->nFxState = mode64 ? 1 : 2; + vex_bzero(&d->fxState, sizeof(d->fxState)); + + d->fxState[0].fx = Ifx_Read; /* read */ + d->fxState[0].offset = floatGuestRegOffset(fs); + if (mode64) + d->fxState[0].size = sizeof(ULong); + else + d->fxState[0].size = sizeof(UInt); + + if (!mode64) { + d->fxState[1].fx = Ifx_Read; /* read */ + d->fxState[1].offset = floatGuestRegOffset(fs+1); + d->fxState[1].size = sizeof(UInt); + } + + stmt(IRStmt_Dirty(d)); + + putFCSR(mkexpr(fcsr)); +} + static IRExpr *getULR(void) { if (mode64) @@ -11658,6 +11693,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, switch (fmt) { case 0x10: /* S */ DIP("round.l.s f%d, f%d", fd, fs); + calculateFCSR(fs, ROUNDLS); t0 = newTemp(Ity_I64); assign(t0, binop(Iop_F32toI64S, mkU32(0x0), @@ -11667,6 +11703,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, break; case 0x11: /* D */ DIP("round.l.d f%d, f%d", fd, fs); + calculateFCSR(fs, ROUNDLD); putFReg(fd, binop(Iop_RoundF64toInt, mkU32(0x0), getFReg(fs))); break; @@ -11680,6 +11717,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, switch (fmt) { case 0x10: /* S */ DIP("trunc.l.s f%d, f%d", fd, fs); + calculateFCSR(fs, TRUNCLS); t0 = newTemp(Ity_I64); assign(t0, binop(Iop_F32toI64S, mkU32(0x3), getLoFromF64(Ity_F64, getFReg(fs)))); @@ -11688,6 +11726,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, break; case 0x11: /* D */ DIP("trunc.l.d f%d, f%d", fd, fs); + calculateFCSR(fs, TRUNCLD); putFReg(fd, binop(Iop_RoundF64toInt, mkU32(0x3), getFReg(fs))); break; @@ -12155,6 +12194,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, switch (fmt) { case 0x10: /* S */ DIP("cvt.d.s f%d, f%d", fd, fs); + calculateFCSR(fs, CVTDS); if (mode64) { t0 = newTemp(Ity_I64); t1 = newTemp(Ity_I32); @@ -12168,14 +12208,13 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, assign(t3, unop(Iop_ReinterpI32asF32, mkexpr(t1))); putFReg(fd, unop(Iop_F32toF64, mkexpr(t3))); - break; - } else { + } else putDReg(fd, unop(Iop_F32toF64, getFReg(fs))); - break; - } + break; case 0x14: DIP("cvt.d.w %d, %d", fd, fs); + calculateFCSR(fs, CVTDW); if (mode64) { t0 = newTemp(Ity_I64); t1 = newTemp(Ity_I32); @@ -12197,6 +12236,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, case 0x15: { /* L */ if (mode64) { DIP("cvt.d.l %d, %d", fd, fs); + calculateFCSR(fs, CVTDL); t0 = newTemp(Ity_I64); assign(t0, unop(Iop_ReinterpF64asI64, getFReg(fs))); @@ -12215,6 +12255,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, switch (fmt) { case 0x14: /* W */ DIP("cvt.s.w %d, %d", fd, fs); + calculateFCSR(fs, CVTSW); if (mode64) { t0 = newTemp(Ity_I64); t1 = newTemp(Ity_I32); @@ -12226,17 +12267,17 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, assign(t1, unop(Iop_64to32, mkexpr(t0))); putFReg(fd, mkWidenFromF32(tyF, binop(Iop_I32StoF32, get_IR_roundingmode(), mkexpr(t1)))); - break; } else { t0 = newTemp(Ity_I32); assign(t0, unop(Iop_ReinterpF32asI32, getFReg(fs))); putFReg(fd, binop(Iop_I32StoF32, get_IR_roundingmode(), mkexpr(t0))); - break; - } + } + break; case 0x11: /* D */ DIP("cvt.s.d %d, %d", fd, fs); + calculateFCSR(fs, CVTSD); if (mode64) { t0 = newTemp(Ity_F32); assign(t0, binop(Iop_F64toF32, get_IR_roundingmode(), @@ -12249,6 +12290,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, case 0x15: /* L */ DIP("cvt.s.l %d, %d", fd, fs); + calculateFCSR(fs, CVTSL); t0 = newTemp(Ity_I64); assign(t0, unop(Iop_ReinterpF64asI64, getFReg(fs))); @@ -12265,6 +12307,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, switch (fmt) { case 0x10: /* S */ DIP("cvt.w.s %d, %d", fd, fs); + calculateFCSR(fs, CVTWS); if (mode64) { putFReg(fd, mkWidenFromF32(tyF, binop(Iop_RoundF32toInt, get_IR_roundingmode(), getLoFromF64(tyF, @@ -12276,6 +12319,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, case 0x11: DIP("cvt.w.d %d, %d", fd, fs); + calculateFCSR(fs, CVTWD); if (mode64) { t0 = newTemp(Ity_I32); t1 = newTemp(Ity_F32); @@ -12303,6 +12347,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, switch (fmt) { case 0x10: /* S */ DIP("cvt.l.s %d, %d", fd, fs); + calculateFCSR(fs, CVTLS); t0 = newTemp(Ity_I64); assign(t0, binop(Iop_F32toI64S, get_IR_roundingmode(), @@ -12313,6 +12358,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, case 0x11: { /* D */ DIP("cvt.l.d %d, %d", fd, fs); + calculateFCSR(fs, CVTLD); putFReg(fd, binop(Iop_RoundF64toInt, get_IR_roundingmode(), getFReg(fs))); break; @@ -12327,6 +12373,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, switch (fmt) { case 0x10: /* S */ DIP("floor.l.s %d, %d", fd, fs); + calculateFCSR(fs, FLOORLS); t0 = newTemp(Ity_I64); assign(t0, binop(Iop_F32toI64S, mkU32(0x1), @@ -12337,6 +12384,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, case 0x11: /* D */ DIP("floor.l.d %d, %d", fd, fs); + calculateFCSR(fs, FLOORLD); putFReg(fd, binop(Iop_RoundF64toInt, mkU32(0x1), getFReg(fs))); break; @@ -12349,6 +12397,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, switch (fmt) { case 0x10: /* S */ DIP("round.w.s f%d, f%d", fd, fs); + calculateFCSR(fs, ROUNDWS); if (mode64) { t0 = newTemp(Ity_I64); t1 = newTemp(Ity_I32); @@ -12365,22 +12414,20 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, mkexpr(t3))); putFReg(fd, mkWidenFromF32(tyF, mkexpr(t4))); - break; - } else { + } else putFReg(fd, binop(Iop_RoundF32toInt, mkU32(0x0), getFReg(fs))); - break; - } + break; case 0x11: /* D */ DIP("round.w.d f%d, f%d", fd, fs); + calculateFCSR(fs, ROUNDWD); if (mode64) { t0 = newTemp(Ity_I32); assign(t0, binop(Iop_F64toI32S, mkU32(0x0), getDReg(fs))); putFReg(fd, mkWidenFromF32(tyF, unop(Iop_ReinterpI32asF32, mkexpr(t0)))); - break; } else { t0 = newTemp(Ity_I32); @@ -12388,8 +12435,8 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, getDReg(fs))); putFReg(fd, unop(Iop_ReinterpI32asF32, mkexpr(t0))); - break; } + break; default: goto decode_failure; @@ -12400,6 +12447,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, switch (fmt) { case 0x10: /* S */ DIP("floor.w.s f%d, f%d", fd, fs); + calculateFCSR(fs, FLOORWS); if (mode64) { t0 = newTemp(Ity_I64); t1 = newTemp(Ity_I32); @@ -12416,15 +12464,14 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, mkexpr(t3))); putFReg(fd, mkWidenFromF32(tyF, mkexpr(t4))); - break; - } else { + } else putFReg(fd, binop(Iop_RoundF32toInt, mkU32(0x1), getFReg(fs))); - break; - } + break; case 0x11: /* D */ DIP("floor.w.d f%d, f%d", fd, fs); + calculateFCSR(fs, FLOORWD); if (mode64) { t0 = newTemp(Ity_I32); assign(t0, binop(Iop_F64toI32S, mkU32(0x1), @@ -12451,6 +12498,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, switch (fmt) { case 0x10: /* S */ DIP("trunc.w.s %d, %d", fd, fs); + calculateFCSR(fs, TRUNCWS); if (mode64) { t0 = newTemp(Ity_I64); t1 = newTemp(Ity_I32); @@ -12467,14 +12515,13 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, mkexpr(t3))); putFReg(fd, mkWidenFromF32(tyF, mkexpr(t4))); - break; - } else { + } else putFReg(fd, binop(Iop_RoundF32toInt, mkU32(0x3), getFReg(fs))); break; - } case 0x11: /* D */ DIP("trunc.w.d %d, %d", fd, fs); + calculateFCSR(fs, TRUNCWD); if (mode64) { t0 = newTemp(Ity_I32); @@ -12483,7 +12530,6 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, putFReg(fd, mkWidenFromF32(tyF, unop(Iop_ReinterpI32asF32, mkexpr(t0)))); - break; } else { t0 = newTemp(Ity_I32); @@ -12491,8 +12537,8 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, getDReg(fs))); putFReg(fd, unop(Iop_ReinterpI32asF32, mkexpr(t0))); - break; } + break; default: goto decode_failure; @@ -12503,6 +12549,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, switch (fmt) { case 0x10: /* S */ DIP("ceil.w.s %d, %d", fd, fs); + calculateFCSR(fs, CEILWS); if (mode64) { t0 = newTemp(Ity_I64); t1 = newTemp(Ity_I32); @@ -12526,20 +12573,20 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, case 0x11: /* D */ DIP("ceil.w.d %d, %d", fd, fs); + calculateFCSR(fs, CEILWD); if (!mode64) { t0 = newTemp(Ity_I32); assign(t0, binop(Iop_F64toI32S, mkU32(0x2), getDReg(fs))); putFReg(fd, unop(Iop_ReinterpI32asF32, mkexpr(t0))); - break; } else { t0 = newTemp(Ity_I32); assign(t0, binop(Iop_F64toI32S, mkU32(0x2), getDReg(fs))); putFReg(fd, mkWidenFromF32(tyF, unop(Iop_ReinterpI32asF32, mkexpr(t0)))); - break; } + break; default: goto decode_failure; @@ -12550,6 +12597,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, switch (fmt) { case 0x10: /* S */ DIP("ceil.l.s %d, %d", fd, fs); + calculateFCSR(fs, CEILLS); t0 = newTemp(Ity_I64); assign(t0, binop(Iop_F32toI64S, mkU32(0x2), @@ -12560,6 +12608,7 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, case 0x11: /* D */ DIP("ceil.l.d %d, %d", fd, fs); + calculateFCSR(fs, CEILLD); putFReg(fd, binop(Iop_RoundF64toInt, mkU32(0x2), getFReg(fs))); break; diff --git a/VEX/priv/host_mips_isel.c b/VEX/priv/host_mips_isel.c index d59b4ab166..06c1eb5439 100644 --- a/VEX/priv/host_mips_isel.c +++ b/VEX/priv/host_mips_isel.c @@ -554,7 +554,7 @@ static void doHelperCall(/*OUT*/UInt* stackAdjustAfterCall, if (LIKELY(!is_IRExpr_VECRET_or_BBPTR(arg))) aTy = typeOfIRExpr(env->type_env, arg); - if (aTy == Ity_I32 || mode64) { + if (aTy == Ity_I32 || (mode64 && arg->tag != Iex_BBPTR)) { tmpregs[argreg] = iselWordExpr_R(env, arg); argreg++; } else if (aTy == Ity_I64) { /* Ity_I64 */ @@ -569,7 +569,6 @@ static void doHelperCall(/*OUT*/UInt* stackAdjustAfterCall, tmpregs[argreg] = raHi; argreg++; } else if (arg->tag == Iex_BBPTR) { - vassert(0); // ATC tmpregs[argreg] = GuestStatePointer(mode64); argreg++; }