From: Julian Seward Date: Fri, 27 Aug 2004 12:00:18 +0000 (+0000) Subject: Front end stuff to support floating point on x86 (guest). X-Git-Tag: svn/VALGRIND_3_0_1^2~1114 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0f90c85468ed4beee07bf416ee1d214bb90d077c;p=thirdparty%2Fvalgrind.git Front end stuff to support floating point on x86 (guest). git-svn-id: svn://svn.valgrind.org/vex/trunk@220 --- diff --git a/VEX/priv/guest-x86/gdefs.h b/VEX/priv/guest-x86/gdefs.h index a2cb0b1bfb..6ece7e9caa 100644 --- a/VEX/priv/guest-x86/gdefs.h +++ b/VEX/priv/guest-x86/gdefs.h @@ -165,14 +165,14 @@ typedef /* EIP */ #define OFFB_EIP (12*4) -/* FPU. For now, just simulate 8 64-bit registers and the reg-stack - top pointer, of which only the least significant three bits are - relevant. +/* FPU. For now, just simulate 8 64-bit registers, their tags, and + the reg-stack top pointer, of which only the least significant + three bits are relevant. The model is: - F0 .. F7 are the 8 registers. ftop[2:0] contains the + F0 .. F7 are the 8 registers. FTOP[2:0] contains the index of the current 'stack top' -- pretty meaningless, but - still. + still. FTOP is a 32-bit value. When a value is pushed onto the stack, ftop is first replaced by (ftop-1) & 7, and then F[ftop] is assigned the value. @@ -183,28 +183,41 @@ typedef In general, a reference to a register ST(i) actually references F[ (ftop+i) & 7 ]. - There should be an array of 8 booleans corresponding to F0 .. F7, - indicating whether the corresponding F reg contains a value or not. + FTAG0 .. FTAG0+7 are the tags. Each is a byte, zero means empty, + non-zero means non-empty. - A read of an F reg marked empty, for any reason, elicits a stack - underflow fault. + The general rule appears to be that a read or modify of a register + gets a stack underflow fault if the register is empty. A write of + a register (only a write, not a modify) gets a stack overflow fault + if the register is full. Note that "over" vs "under" is pretty + meaningless since the FP stack pointer can move around arbitrarily, + so it's really just two different kinds of exceptions: + register-empty and register full. + + Naturally Intel (in its infinite wisdom) has seen fit to throw in + some ad-hoc inconsistencies to the fault-generation rules of the + above para, just to complicate everything. Known inconsistencies: + + * fxam can read a register in any state without taking an underflow + fault. + + * fst from st(0) to st(i) does not take an overflow fault even if the + destination is already full. - A load from memory into an F reg marked full elicits a stack overflow - fault. This appears to be the only way a stack overflow fault can - happen. */ -#define OFFB_F0 (13*4) -#define OFFB_F1 (15*4) -#define OFFB_F2 (17*4) -#define OFFB_F3 (19*4) -#define OFFB_F4 (21*4) -#define OFFB_F5 (23*4) -#define OFFB_F6 (25*4) -#define OFFB_F7 (27*4) -#define OFFB_FTOP (29*4) +#define OFFB_FTOP (13*4) +#define OFFB_F0 (14*4) +#define OFFB_F1 (16*4) +#define OFFB_F2 (18*4) +#define OFFB_F3 (20*4) +#define OFFB_F4 (22*4) +#define OFFB_F5 (24*4) +#define OFFB_F6 (26*4) +#define OFFB_F7 (28*4) +#define OFFB_FTAG0 (30*4) // up to 30*4 + 7 /* Don't forget to keep this up to date. */ -#define SIZEOF_X86H_STATE OFFB_FTOP +#define SIZEOF_X86H_STATE (OFFB_FTAG0 + 8) diff --git a/VEX/priv/guest-x86/toIR.c b/VEX/priv/guest-x86/toIR.c index 5846eae329..9e57e77994 100644 --- a/VEX/priv/guest-x86/toIR.c +++ b/VEX/priv/guest-x86/toIR.c @@ -3078,82 +3078,182 @@ UInt dis_imul_I_E_G ( UChar sorb, /*--- x87 floating point insns. ---*/ /*------------------------------------------------------------*/ -/* Get/set the top-of-stack pointer. */ +/* --- Helper functions for dealing with the register stack. --- */ + +/* --- Produce an IRExpr* denoting a 64-bit NaN. --- */ + +static IRExpr* mkNaN64 ( void ) +{ + return IRExpr_Const(IRConst_NaN64()); +} + +/* --------- Get/set the top-of-stack pointer. --------- */ static IRExpr* get_ftop ( void ) { return IRExpr_Get( OFFB_FTOP, Ity_I32 ); } -static IRStmt* put_ftop ( IRExpr* e ) +static void put_ftop ( IRExpr* e ) +{ + stmt( IRStmt_Put( OFFB_FTOP, e ) ); +} + + +/* --------- Get/set FP register tag bytes. --------- */ + +/* Given i, generate an expression which is the offset in the guest + state of ST(i)'s tag byte, considering the current value of FTOP. The + generated expression is: + + ((Get(OFFB_FTOP) + i) & 7) + OFFB_FTAGO +*/ +static IRExpr* off_ST_TAG ( Int i ) +{ + vassert(i >= 0 && i <= 7); + return + binop(Iop_Add32, + binop(Iop_And32, + binop(Iop_Add32, get_ftop(), mkU32(i)), + mkU32(7)), + mkU32(OFFB_FTAG0) + ); +} + +/* Given i, and some expression e, generate 'ST_TAG(i) = e'. */ + +static void put_ST_TAG ( Int i, IRExpr* value ) +{ + vassert(typeOfIRExpr(irbb->tyenv, value) == Ity_I8); + stmt( + IRStmt_PutI( off_ST_TAG(i), value, OFFB_FTAG0, OFFB_FTAG0 +7 ) + ); +} + +/* Given i, generate an expression yielding 'ST_TAG(i)'. This will be + zero to indicate "Empty" and nonzero and indicate "NonEmpty". */ + +static IRExpr* get_ST_TAG ( Int i ) { - return IRStmt_Put( OFFB_FTOP, e ); + return + IRExpr_GetI( off_ST_TAG(i), Ity_I8, OFFB_FTAG0, OFFB_FTAG0 +7 ); } + +/* --------- Get/set FP registers. --------- */ + /* Given i, generate an expression which is the offset in the guest - state of ST(i), considering the current value of FTOP. */ + state of ST(i), considering the current value of FTOP. The + generated expression is: + (((Get(OFFB_FTOP) + i) & 7) << 3) + OFFB_FO +*/ static IRExpr* off_ST ( Int i ) { vassert(i >= 0 && i <= 7); return binop(Iop_Add32, - binop(Iop_Mul32, + binop(Iop_Shl32, binop(Iop_And32, binop(Iop_Add32, get_ftop(), mkU32(i)), mkU32(7)), - mkU32(8)), + mkU8(3)), mkU32(OFFB_F0) ); } -/* Given i, and some expression e, generate 'ST(i) = e'. */ -static IRStmt* put_ST ( Int i, IRExpr* value ) +/* Given i, and some expression e, emit 'ST(i) = e' +and set the register's tag to indicate the register is full. +The previous state of the register is not checked. */ + +static void put_ST_UNCHECKED ( Int i, IRExpr* value ) { - return - IRStmt_PutI( off_ST(i), value, OFFB_F0, OFFB_F7+8-1 ); + stmt( IRStmt_PutI( off_ST(i), value, OFFB_F0, OFFB_F7+8-1 ) ); + /* Mark the register as in-use. */ + put_ST_TAG(i, mkU8(1)); } +/* Given i, and some expression e, emit + ST(i) = is_full(i) ? NaN : e + and set the tag accordingly. +*/ + +static void put_ST ( Int i, IRExpr* value ) +{ + put_ST_UNCHECKED( i, + IRExpr_Mux0X(get_ST_TAG(i), + /* 0 means empty */ + value, + /* non-0 means full */ + mkNaN64() + ) + ); +} + + /* Given i, generate an expression yielding 'ST(i)'. */ -static IRExpr* get_ST ( Int i ) +static IRExpr* get_ST_UNCHECKED ( Int i ) { return IRExpr_GetI( off_ST(i), Ity_F64, OFFB_F0, OFFB_F7+8-1 ); } -/* Adjust FTOP downwards by one register. */ +/* Given i, generate an expression yielding + is_full(i) ? ST(i) : NaN +*/ -static IRStmt* fp_push ( void ) +static IRExpr* get_ST ( Int i ) { return - put_ftop( - binop(Iop_And32, - binop(Iop_Sub32, get_ftop(), mkU32(1)), - mkU32(7)) - ); + IRExpr_Mux0X( get_ST_TAG(i), + /* 0 means empty */ + mkNaN64(), + /* non-0 means full */ + get_ST_UNCHECKED(i)); } -/* Adjust FTOP upwards by one register. */ -static IRStmt* fp_pop ( void ) +/* Adjust FTOP downwards by one register. */ + +static void fp_push ( void ) { - return - put_ftop( - binop(Iop_And32, - binop(Iop_Add32, get_ftop(), mkU32(1)), - mkU32(7)) - ); + put_ftop( + binop(Iop_And32, + binop(Iop_Sub32, get_ftop(), mkU32(1)), + mkU32(7)) + ); } +/* Adjust FTOP upwards by one register, and mark the vacated register + as empty. */ + +static void fp_pop ( void ) +{ + put_ST_TAG(0, mkU8(1)); + put_ftop( + binop(Iop_And32, + binop(Iop_Add32, get_ftop(), mkU32(1)), + mkU32(7)) + ); +} + +/* ------------------------------------------------------- */ +/* Given all that stack-mangling junk, we can now go ahead + and describe FP instructions. +*/ + +/* ST(0) = ST(0) `op` mem64(addr) (when dbl==True) + Need to check ST(0)'s tag on read, but not on write. +*/ static void fp_do_op_mem_ST_0 ( IRTemp addr, UChar* op_txt, UChar* dis_buf, IROp op, Bool dbl ) { DIP("f%s%c %s", op_txt, dbl?'l':'s', dis_buf); if (dbl) { - stmt( put_ST(0, binop(op, get_ST(0), loadLE(Ity_F64,mkexpr(addr))))); + put_ST_UNCHECKED(0, binop(op, get_ST(0), loadLE(Ity_F64,mkexpr(addr)))); } else { vassert(0); } @@ -3192,14 +3292,14 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta ) DIP("fld %%st(%d)\n", r_src); t1 = newTemp(Ity_F64); assign(t1, get_ST(r_src)); - stmt( fp_push() ); - stmt( put_ST(0, mkexpr(t1)) ); + fp_push(); + put_ST(0, mkexpr(t1)); break; case 0xEE: /* FLDZ */ DIP("fldz"); - stmt( fp_push() ); - stmt( put_ST(0, IRExpr_Const(IRConst_F64(0.0))) ); + fp_push(); + put_ST(0, IRExpr_Const(IRConst_F64(0.0))); break; default: @@ -3220,14 +3320,14 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta ) delta += len; switch (gregOfRM(modrm)) { - case 1: /* FIMUL m32int */ + case 1: /* FIMUL m32int */ /* ST(0) *= m32int */ DIP("fimull %s", dis_buf); - stmt( put_ST(0, - binop(Iop_MulF64, - get_ST(0), - unop(Iop_I64toF64, - unop(Iop_32Sto64, - loadLE(Ity_I32, mkexpr(addr))))))); + put_ST_UNCHECKED(0, + binop(Iop_MulF64, + get_ST(0), + unop(Iop_I64toF64, + unop(Iop_32Sto64, + loadLE(Ity_I32, mkexpr(addr)))))); break; default: @@ -3288,8 +3388,8 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta ) case 0: /* FLD double-real */ DIP("fldD %s\n", dis_buf); - stmt( fp_push() ); - stmt( put_ST(0, IRExpr_LDle(Ity_F64, mkexpr(addr))) ); + fp_push(); + put_ST(0, IRExpr_LDle(Ity_F64, mkexpr(addr))); break; #if 0 @@ -3307,7 +3407,7 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta ) case 3: /* FSTP double-real */ DIP("fstpD %s", dis_buf); storeLE(mkexpr(addr), get_ST(0)); - stmt( fp_pop() ); + fp_pop(); break; default: @@ -3321,8 +3421,11 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta ) case 0xD8 ... 0xDF: /* FSTP %st(0),%st(?) */ r_dst = (UInt)modrm - 0xD8; DIP("fstp %%st(0),%%st(%d)\n", r_dst); - stmt( put_ST(r_dst, get_ST(0)) ); - stmt( fp_pop() ); + /* P4 manual says: "If the destination operand is a + non-empty register, the invalid-operation exception + is not generated. Hence put_ST_UNCHECKED. */ + put_ST_UNCHECKED(r_dst, get_ST(0)); + fp_pop(); break; default: goto decode_fail; diff --git a/VEX/priv/host-x86/hdefs.c b/VEX/priv/host-x86/hdefs.c index 00676ceba2..ef53f78809 100644 --- a/VEX/priv/host-x86/hdefs.c +++ b/VEX/priv/host-x86/hdefs.c @@ -958,7 +958,7 @@ Bool isMove_X86Instr ( X86Instr* i, HReg* src, HReg* dst ) X86Instr* genSpill_X86 ( HReg rreg, Int offset ) { - Int base = 4 * 51; + Int base = 4 * 53; vassert(offset >= 0); vassert(offset <= 4*(100-1)); vassert(!hregIsVirtual(rreg)); @@ -976,7 +976,7 @@ X86Instr* genSpill_X86 ( HReg rreg, Int offset ) X86Instr* genReload_X86 ( HReg rreg, Int offset ) { - Int base = 4 * 51; + Int base = 4 * 53; vassert(offset >= 0); vassert(offset <= 4*(100-1)); vassert(!hregIsVirtual(rreg)); diff --git a/VEX/priv/ir/irdefs.c b/VEX/priv/ir/irdefs.c index 2efb84440c..accf6f7a13 100644 --- a/VEX/priv/ir/irdefs.c +++ b/VEX/priv/ir/irdefs.c @@ -36,12 +36,13 @@ void ppIRType ( IRType ty ) void ppIRConst ( IRConst* con ) { switch (con->tag) { - case Ico_Bit: vex_printf( "%d:Bit", con->Ico.Bit ? 1 : 0); break; - case Ico_U8: vex_printf( "0x%x:I8", (UInt)(con->Ico.U8)); break; - case Ico_U16: vex_printf( "0x%x:I16", (UInt)(con->Ico.U16)); break; - case Ico_U32: vex_printf( "0x%x:I32", (UInt)(con->Ico.U32)); break; - case Ico_U64: vex_printf( "0x%llx:I64", (ULong)(con->Ico.U64)); break; - case Ico_F64: vex_printf("(f64 value)"); break; + case Ico_Bit: vex_printf( "%d:Bit", con->Ico.Bit ? 1 : 0); break; + case Ico_U8: vex_printf( "0x%x:I8", (UInt)(con->Ico.U8)); break; + case Ico_U16: vex_printf( "0x%x:I16", (UInt)(con->Ico.U16)); break; + case Ico_U32: vex_printf( "0x%x:I32", (UInt)(con->Ico.U32)); break; + case Ico_U64: vex_printf( "0x%llx:I64", (ULong)(con->Ico.U64)); break; + case Ico_F64: vex_printf( "(f64 value)"); break; + case Ico_NaN64: vex_printf( "NaN:F64"); break; default: vpanic("ppIRConst"); } } @@ -348,6 +349,13 @@ IRConst* IRConst_F64 ( Double f64 ) c->Ico.F64 = f64; return c; } +IRConst* IRConst_NaN64 ( void ) +{ + IRConst* c = LibVEX_Alloc(sizeof(IRConst)); + c->tag = Ico_NaN64; + return c; +} + /* Constructors -- IRExpr */ @@ -684,12 +692,13 @@ IRType lookupIRTypeEnv ( IRTypeEnv* env, IRTemp tmp ) IRType typeOfIRConst ( IRConst* con ) { switch (con->tag) { - case Ico_Bit: return Ity_Bit; - case Ico_U8: return Ity_I8; - case Ico_U16: return Ity_I16; - case Ico_U32: return Ity_I32; - case Ico_U64: return Ity_I64; - case Ico_F64: return Ity_F64; + case Ico_Bit: return Ity_Bit; + case Ico_U8: return Ity_I8; + case Ico_U16: return Ity_I16; + case Ico_U32: return Ity_I32; + case Ico_U64: return Ity_I64; + case Ico_F64: return Ity_F64; + case Ico_NaN64: return Ity_F64; default: vpanic("typeOfIRConst"); } } diff --git a/VEX/pub/libvex_ir.h b/VEX/pub/libvex_ir.h index c048144d7d..6e211d8e08 100644 --- a/VEX/pub/libvex_ir.h +++ b/VEX/pub/libvex_ir.h @@ -33,7 +33,9 @@ extern void ppIRType ( IRType ); typedef enum { Ico_Bit=0x12000, - Ico_U8, Ico_U16, Ico_U32, Ico_U64, Ico_F64 } + Ico_U8, Ico_U16, Ico_U32, Ico_U64, Ico_F64, + Ico_NaN64 /* 64-bit IEEE NaN. */ + } IRConstTag; typedef @@ -50,12 +52,13 @@ typedef } IRConst; -extern IRConst* IRConst_Bit ( Bool ); -extern IRConst* IRConst_U8 ( UChar ); -extern IRConst* IRConst_U16 ( UShort ); -extern IRConst* IRConst_U32 ( UInt ); -extern IRConst* IRConst_U64 ( ULong ); -extern IRConst* IRConst_F64 ( Double ); +extern IRConst* IRConst_Bit ( Bool ); +extern IRConst* IRConst_U8 ( UChar ); +extern IRConst* IRConst_U16 ( UShort ); +extern IRConst* IRConst_U32 ( UInt ); +extern IRConst* IRConst_U64 ( ULong ); +extern IRConst* IRConst_F64 ( Double ); +extern IRConst* IRConst_NaN64 ( void ); extern void ppIRConst ( IRConst* );