effect.
git-svn-id: svn://svn.valgrind.org/vex/trunk@241
}
+/* --------- Get/set the FPU control word. --------- */
+/* Note, IA32 has this as a 16-bit value, so fstcw/fldcw need to cast
+ to/from 16 bits. Here we represent it in 32 bits. */
+static IRExpr* /* :: Ity_I32 */ get_fpucw ( void )
+{
+ return IRExpr_Get( OFFB_FPUCW, Ity_I32 );
+}
+
+static void put_fpucw ( IRExpr* /* :: Ity_I32 */ e )
+{
+ stmt( IRStmt_Put( OFFB_FPUCW, e ) );
+}
+
+
+/* --------- Get the FPU rounding mode from the CW. --------- */
+/* Produces a value in 0 .. 3, which is encoded as per the type
+ IRRoundingMode. On IA32 the relevant value is precisely bits 11
+ and 10 of the control word.
+*/
+static IRExpr* /* :: Ity_I32 */ get_roundingmode ( void )
+{
+ return binop( Iop_And32,
+ binop(Iop_Shr32, get_fpucw(), mkU8(10)),
+ mkU32(3) );
+}
+
+
/* --------- Get/set FP register tag bytes. --------- */
/* Given i, generate an expression which is the offset in the guest
DIP("fldF %s\n", dis_buf);
fp_push();
put_ST(0, unop(Iop_F32toF64,
- IRExpr_LDle(Ity_F32, mkexpr(addr))));
+ loadLE(Ity_F32, mkexpr(addr))));
break;
case 3: /* FSTP single-real */
fp_pop();
break;
- case 5:
- vex_printf("vex x86->IR: ignoring fldcw\n");
+ case 5:
+ DIP("fldcw %s", dis_buf);
+ put_fpucw( unop(Iop_16Uto32, loadLE(Ity_I16, mkexpr(addr))) );
break;
case 7:
- vex_printf("vex x86->IR: ignoring fstcw\n");
- storeLE(mkexpr(addr), mkU16(0x037F));
+ DIP("fstcw %s", dis_buf);
+ storeLE(mkexpr(addr), unop(Iop_32to16, get_fpucw()));
break;
default:
loadLE(Ity_I32, mkexpr(addr))));
break;
+ case 2: /* FIST m32 */
+ DIP("fistl %s", dis_buf);
+ storeLE( mkexpr(addr),
+ binop(Iop_F64toI32, get_roundingmode(), get_ST(0)) );
+ break;
+
case 3: /* FISTP m32 */
DIP("fistpl %s", dis_buf);
- storeLE(mkexpr(addr), unop(Iop_F64toI32,get_ST(0)));
+ storeLE( mkexpr(addr),
+ binop(Iop_F64toI32, get_roundingmode(), get_ST(0)) );
fp_pop();
break;
case 3: /* FISTP m16 */
DIP("fistps %s", dis_buf);
- storeLE(mkexpr(addr), unop(Iop_F64toI16,get_ST(0)));
- fp_pop();
+ storeLE( mkexpr(addr),
+ binop(Iop_F64toI16, get_roundingmode(), get_ST(0)) );
+ fp_pop();
break;
#if 0
case 0xB0: /* MOV imm,AL */
case 0xB1: /* MOV imm,CL */
case 0xB2: /* MOV imm,DL */
-#if 0
case 0xB3: /* MOV imm,BL */
case 0xB4: /* MOV imm,AH */
case 0xB5: /* MOV imm,CH */
case 0xB6: /* MOV imm,DH */
case 0xB7: /* MOV imm,BH */
-#endif
d32 = getIByte(delta); delta += 1;
putIReg(1, opc-0xB0, mkU8(d32));
DIP("movb $0x%x,%s\n", d32, nameIReg(1,opc-0xB0));
vassert(cond != Xcc_ALWAYS);
return i;
}
+X86Instr* X86Instr_FpLdStCW ( Bool isLoad, X86AMode* addr )
+{
+ X86Instr* i = LibVEX_Alloc(sizeof(X86Instr));
+ i->tag = Xin_FpLdStCW;
+ i->Xin.FpLdStCW.isLoad = isLoad;
+ i->Xin.FpLdStCW.addr = addr;
+ return i;
+}
void ppX86Instr ( X86Instr* i ) {
vex_printf(",");
ppHRegX86(i->Xin.FpCMov.dst);
return;
+ case Xin_FpLdStCW:
+ vex_printf(i->Xin.FpLdStCW.isLoad ? "fldcw " : "fstcw ");
+ ppX86AMode(i->Xin.FpLdStCW.addr);
+ return;
default:
vpanic("ppX86Instr");
}
addHRegUse(u, HRmRead, i->Xin.FpCMov.src);
addHRegUse(u, HRmModify, i->Xin.FpCMov.dst);
return;
+ case Xin_FpLdStCW:
+ addRegUsage_X86AMode(u, i->Xin.FpLdStCW.addr);
+ return;
default:
ppX86Instr(i);
vpanic("getRegUsage_X86Instr");
mapReg(m, &i->Xin.FpCMov.src);
mapReg(m, &i->Xin.FpCMov.dst);
return;
+ case Xin_FpLdStCW:
+ mapRegs_X86AMode(m, i->Xin.FpLdStCW.addr);
+ return;
default:
ppX86Instr(i);
vpanic("mapRegs_X86Instr");
*(ptmp-1) = p - ptmp;
goto done;
+ case Xin_FpLdStCW:
+ if (i->Xin.FpLdStCW.isLoad) {
+ *p++ = 0xD9;
+ p = doAMode_M(p, fake(5)/*subopcode*/, i->Xin.FpLdStCW.addr);
+ } else {
+ vassert(0);
+ }
+ goto done;
+
default:
goto bad;
}
Xin_FpBinary, /* FP fake binary op */
Xin_FpLdSt, /* FP fake load/store */
Xin_FpLdStI, /* FP fake load/store, converting to/from Int */
- Xin_FpCMov /* FP fake floating point (un)conditional move */
+ Xin_FpCMov, /* FP fake floating point (un)conditional move */
+ Xin_FpLdStCW /* fldcw / fstcw */
}
X86InstrTag;
HReg src;
HReg dst;
} FpCMov;
+ /* Load/store the FPU's 16-bit control word (fldcw/fstcw) */
+ struct {
+ Bool isLoad;
+ X86AMode* addr;
+ }
+ FpLdStCW;
} Xin;
}
X86Instr;
extern X86Instr* X86Instr_FpLdSt ( Bool isLoad, UChar sz, HReg reg, X86AMode* );
extern X86Instr* X86Instr_FpLdStI ( Bool isLoad, UChar sz, HReg reg, X86AMode* );
extern X86Instr* X86Instr_FpCMov ( X86CondCode, HReg src, HReg dst );
+extern X86Instr* X86Instr_FpLdStCW ( Bool isLoad, X86AMode* );
+
extern void ppX86Instr ( X86Instr* );
return b16;
}
+ if (e->Iex.Binop.op == Iop_F64toI32 || e->Iex.Binop.op == Iop_F64toI16) {
+ Int sz = e->Iex.Binop.op == Iop_F64toI16 ? 2 : 4;
+ 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);
+
+ /* Used several times ... */
+ X86AMode* zero_esp = X86AMode_IR(0, hregX86_ESP());
+
+ /* rf now holds the value to be converted, and rrm holds the
+ rounding mode value, encoded as per the IRRoundingMode
+ enum. The first thing to do is set the FPU's rounding
+ mode accordingly. */
+
+ /* Create a space, both for the control word messing, and for
+ the actual store conversion.
+ /* subl $4, %esp */
+ addInstr(env,
+ X86Instr_Alu32R(Xalu_SUB, X86RMI_Imm(4), hregX86_ESP()));
+ /* movl %rrm, %rrm2
+ andl $3, %rrm2 -- shouldn't be needed; paranoia
+ shll $10, %rrm2
+ orl $0x037F, %rrm2
+ movl %rrm2, 0(%esp)
+ fldcw 0(%esp)
+ */
+ addInstr(env, mk_MOVsd_RR(rrm, rrm2));
+ addInstr(env, X86Instr_Alu32R(Xalu_AND, X86RMI_Imm(3), rrm2));
+ addInstr(env, X86Instr_Sh32(Xsh_SHL, 10, X86RM_Reg(rrm2)));
+ addInstr(env, X86Instr_Alu32R(Xalu_OR, X86RMI_Imm(0x037F), rrm2));
+ addInstr(env, X86Instr_Alu32M(Xalu_MOV, X86RI_Reg(rrm2), zero_esp));
+ addInstr(env, X86Instr_FpLdStCW(True/*load*/, zero_esp));
+
+ /* gistw/l %rf, 0(%esp) */
+ addInstr(env, X86Instr_FpLdStI(False/*store*/, sz, rf, zero_esp));
+
+ if (sz == 2) {
+ /* movzwl 0(%esp), %dst */
+ addInstr(env, X86Instr_LoadEX(2,False,zero_esp,dst));
+ } else {
+ /* movl 0(%esp), %dst */
+ vassert(sz == 4);
+ addInstr(env, X86Instr_Alu32R(
+ Xalu_MOV, X86RMI_Mem(zero_esp), dst));
+ }
+
+ /* Restore default FPU control.
+ movl $0x037F, 0(%esp)
+ fldcw 0(%esp)
+ */
+ addInstr(env, X86Instr_Alu32M(Xalu_MOV, X86RI_Imm(0x037F), zero_esp));
+ addInstr(env, X86Instr_FpLdStCW(True/*load*/, zero_esp));
+
+ /* addl $4, %esp */
+ addInstr(env,
+ X86Instr_Alu32R(Xalu_ADD, X86RMI_Imm(4), hregX86_ESP()));
+ return dst;
+ }
+
break;
}
return dst;
}
- case Iop_F64toI32:
- case Iop_F64toI16: {
- Int sz = e->Iex.Unop.op == Iop_F64toI16 ? 2 : 4;
- HReg dst = newVRegI(env);
- HReg rf = iselDblExpr(env, e->Iex.Unop.arg);
- /* subl $sz, %esp */
- addInstr(env, X86Instr_Alu32R(Xalu_SUB,
- X86RMI_Imm(sz),
- hregX86_ESP()));
- /* gistw/l %rf, 0(%esp) */
- addInstr(env, X86Instr_FpLdStI(
- False/*store*/, sz, rf,
- X86AMode_IR(0, hregX86_ESP())));
- if (sz == 2) {
- /* movzwl 0(%esp), %dst */
- addInstr(env, X86Instr_LoadEX(
- 2,False,
- X86AMode_IR(0, hregX86_ESP()),dst));
- } else {
- /* movl 0(%esp), %dst */
- vassert(sz == 4);
- addInstr(env, X86Instr_Alu32R(Xalu_MOV,
- X86RMI_Mem(X86AMode_IR(0, hregX86_ESP())),
- dst));
- }
- /* addl $sz, %esp */
- addInstr(env, X86Instr_Alu32R(Xalu_ADD,
- X86RMI_Imm(sz),
- hregX86_ESP()));
- return dst;
- }
-
-
case Iop_16to8:
case Iop_32to8:
case Iop_32to16:
{
MatchInfo mi;
DECLARE_PATTERN(p_32to1);
- DECLARE_PATTERN(p_eq32_literal);
- DECLARE_PATTERN(p_ne32_zero);
+ //DECLARE_PATTERN(p_eq32_literal);
+ //DECLARE_PATTERN(p_ne32_zero);
DECLARE_PATTERN(p_1Uto32_then_32to1);
vassert(e);
case Iop_AddF64: case Iop_SubF64:
case Iop_MulF64: case Iop_DivF64:
BINARY(Ity_F64,Ity_F64,Ity_F64);
+
case Iop_I32toF64: UNARY(Ity_F64,Ity_I32);
- case Iop_F64toI32: UNARY(Ity_I32,Ity_F64);
- case Iop_F64toI16: UNARY(Ity_I16,Ity_F64);
+
+ case Iop_F64toI32: BINARY(Ity_I32, Ity_I32,Ity_F64);
+ case Iop_F64toI16: BINARY(Ity_I16, Ity_I32,Ity_F64);
+
case Iop_F32toF64: UNARY(Ity_F64,Ity_F32);
case Iop_F64toF32: UNARY(Ity_F32,Ity_F64);
tcExpr(bb,stmt, expr->Iex.Binop.arg1, gWordTy );
tcExpr(bb,stmt, expr->Iex.Binop.arg2, gWordTy );
typeOfPrimop(expr->Iex.Binop.op, &t_dst, &t_arg1, &t_arg2);
- if (t_arg1 == Ity_INVALID || t_arg2 == Ity_INVALID)
- sanityCheckFail(bb,stmt,"Iex.Binop: wrong arity op");
+ if (t_arg1 == Ity_INVALID || t_arg2 == Ity_INVALID) {
+ vex_printf(" op name: " );
+ ppIROp(expr->Iex.Binop.op);
+ vex_printf("\n");
+ sanityCheckFail(bb,stmt,
+ "Iex.Binop: wrong arity op\n"
+ "... name of op precedes BB printout\n");
+ }
ttarg1 = typeOfIRExpr(tyenv, expr->Iex.Binop.arg1);
ttarg2 = typeOfIRExpr(tyenv, expr->Iex.Binop.arg2);
if (t_arg1 != ttarg1 || t_arg2 != ttarg2) {
/* ------------------ Binary and unary ops ------------------ */
+/* Encoding of rounding modes in Float -> Int conversions. This is
+ the same as the encoding used by Intel IA32 to indicate x87
+ rounding mode. */
+typedef
+ enum { Irrm_NEAREST=0, Irrm_NegINF=1, Irrm_PosINF=2, Irrm_ZERO=3 }
+ IRRoundingMode;
+
+/* Floating point comparison result values, as created by Iop_CmpF64.
+ This is also derived from what IA32 does. */
+typedef
+ enum {
+ Ircr_UN = 0x85,
+ Ircr_LT = 0x01,
+ Ircr_GT = 0x00,
+ Ircr_EQ = 0x80
+ }
+ IRCmpF64Result;
+
+
typedef
enum {
/* Do not change this ordering. The IR generators
/* Widening multiplies */
Iop_MullS8, Iop_MullS16, Iop_MullS32,
Iop_MullU8, Iop_MullU16, Iop_MullU32,
+
/* Wierdo integer stuff */
Iop_Clz32, /* count leading zeroes */
Iop_Ctz32, /* count trailing zeros */
+ /* Ctz32/Clz32 are UNDEFINED when given arguments of zero.
+ You must ensure they are never given a zero argument.
+ */
+
/* Ordering not important after here. */
Iop_CmpLT32S,
Iop_CmpLE32S,
Iop_CmpLT32U,
Iop_CmpLE32U,
/* Division */
+ /* TODO: clarify semantics wrt rounding, negative values, whatever */
Iop_DivModU64to32, // :: I64,I32 -> I64
// of which lo half is div and hi half is mod
Iop_DivModS64to32, // ditto, signed
Iop_32to1, /* :: Ity_I32 -> Ity_Bit, just select bit[0] */
Iop_1Uto8, /* :: Ity_Bit -> Ity_I8, unsigned widen */
Iop_1Uto32, /* :: Ity_Bit -> Ity_I32, unsigned widen */
- /* FP stuff */
- Iop_AddF64, Iop_SubF64, Iop_MulF64, Iop_DivF64,
- Iop_SqrtF64,
- /* double <-> int */
- Iop_I32toF64,
- Iop_F64toI32, Iop_F64toI16,
- /* double <-> float */
+
+ /* ------ Floating point. We try and be IEEE754 compliant. ------ */
+
+ /* Simple operations */
+ Iop_AddF64, Iop_SubF64, Iop_MulF64, Iop_DivF64, Iop_RemF64,
+ Iop_SqrtF64, Iop_SinF64, Iop_CosF64,
+
+ /* Comparison, yielding GT/LT/EQ/UN(ordered), as per the following:
+ 0x85 Unordered
+ 0x01 LT
+ 0x00 GT
+ 0x80 EQ
+ This just happens to be the Intel encoding. The values
+ are recorded in the type IRCmpF64Result.
+ */
+ Iop_CmpF64,
+
+ /* int -> double */
+ Iop_I32toF64, Iop_I64toF64,
+
+ /* double -> int. These take a first argument :: Ity_I32
+ (an IRRoundingMode) which is an indication of the rounding mode,
+ as per the following encoding:
+ 00b to nearest (the default)
+ 01b to -infinity
+ 10b to +infinity
+ 11b to zero
+ This just happens to be the Intel encoding. For reference only,
+ the PPC encoding is:
+ 00b to nearest (the default)
+ 01b to zero
+ 10b to +infinity
+ 11b to -infinity
+ Any PPC -> IR front end will have to translate these PPC
+ encodings to the standard encodings.
+
+ If one of these conversions gets an out-of-range condition,
+ or a NaN, as an argument, the result is host-defined. On x86
+ the "integer indefinite" value 0x80..00 is produced.
+ On PPC it is either 0x80..00 or 0x7F..FF depending on the sign
+ of the argument.
+ */
+ Iop_F64toI64, Iop_F64toI32, Iop_F64toI16,
+
+ /* double <-> float. What does this mean -- does it round? */
Iop_F32toF64, Iop_F64toF32
}
IROp;
-/* Notes.
- Ctz32/Clz32 are UNDEFINED when given arguments of zero.
- You must ensure they are never given a zero argument.
-*/
extern void ppIROp ( IROp );