From: Julian Seward Date: Tue, 31 Aug 2004 23:55:54 +0000 (+0000) Subject: Do a bit more x86 floating point. As a result, had to do 'bsf' and 'bsr' X-Git-Tag: svn/VALGRIND_3_0_1^2~1109 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0c7a4bcaf0600e455c5db1f067459f0502083fb9;p=thirdparty%2Fvalgrind.git Do a bit more x86 floating point. As a result, had to do 'bsf' and 'bsr' which in turn meant two new IR primops, Iop_Clz32 and Iop_Ctz32, to count leading and trailing zeroes on a 32-bit word. git-svn-id: svn://svn.valgrind.org/vex/trunk@225 --- diff --git a/VEX/priv/guest-x86/toIR.c b/VEX/priv/guest-x86/toIR.c index 9e57e77994..32d858f731 100644 --- a/VEX/priv/guest-x86/toIR.c +++ b/VEX/priv/guest-x86/toIR.c @@ -3296,6 +3296,12 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta ) put_ST(0, mkexpr(t1)); break; + case 0xE8: /* FLD1 */ + DIP("fldz"); + fp_push(); + put_ST(0, IRExpr_Const(IRConst_F64(1.0))); + break; + case 0xEE: /* FLDZ */ DIP("fldz"); fp_push(); @@ -3316,14 +3322,29 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta ) /* bits 5,4,3 are an opcode extension, and the modRM also specifies an address. */ + IROp fop; IRTemp addr = disAMode( &len, sorb, delta, dis_buf ); delta += len; - switch (gregOfRM(modrm)) { + case 1: /* FIMUL m32int */ /* ST(0) *= m32int */ DIP("fimull %s", dis_buf); + fop = Iop_MulF64; + goto do_fop_m32; + + case 4: /* FISUB m32int */ /* ST(0) -= m32int */ + DIP("fisubl %s", dis_buf); + fop = Iop_SubF64; + goto do_fop_m32; + + case 6: /* FIDIV m32int */ /* ST(0) /= m32int */ + DIP("fisubl %s", dis_buf); + fop = Iop_DivF64; + goto do_fop_m32; + + do_fop_m32: put_ST_UNCHECKED(0, - binop(Iop_MulF64, + binop(fop, get_ST(0), unop(Iop_I64toF64, unop(Iop_32Sto64, @@ -4015,61 +4036,110 @@ UInt dis_SHLRD_Gv_Ev ( UChar sorb, //-- //-- return eip; //-- } -//-- -//-- -//-- -//-- /* Handle BSF/BSR. Only v-size seems necessary. */ -//-- static -//-- Addr dis_bs_E_G ( UCodeBlock* cb, -//-- UChar sorb, -//-- Int sz, Addr eip, Bool fwds ) -//-- { -//-- Int t, t1, ta, helper; -//-- UInt pair; -//-- Char dis_buf[50]; -//-- UChar modrm; -//-- Bool isReg; -//-- -//-- vg_assert(sz == 2 || sz == 4); -//-- -//-- if (fwds) -//-- helper = sz == 2 ? VGOFF_(helper_bsfw) : VGOFF_(helper_bsfl); -//-- else -//-- helper = sz == 2 ? VGOFF_(helper_bsrw) : VGOFF_(helper_bsrl); -//-- -//-- modrm = getUChar(eip); -//-- t1 = newTemp(cb); -//-- t = newTemp(cb); -//-- -//-- uInstr0(cb, CALLM_S, 0); -//-- uInstr2(cb, GET, sz, ArchReg, gregOfRM(modrm), TempReg, t1); -//-- uInstr1(cb, PUSH, sz, TempReg, t1); -//-- -//-- isReg = epartIsReg(modrm); -//-- if (isReg) { -//-- eip++; -//-- uInstr2(cb, GET, sz, ArchReg, eregOfRM(modrm), TempReg, t); -//-- } else { -//-- pair = disAMode ( cb, sorb, eip, dis_buf ); -//-- ta = LOW24(pair); -//-- eip += HI8(pair); -//-- uInstr2(cb, LOAD, sz, TempReg, ta, TempReg, t); -//-- } -//-- DIP("bs%c%c %s, %s\n", -//-- fwds ? 'f' : 'r', nameISize(sz), -//-- ( isReg ? nameIReg(sz, eregOfRM(modrm)) : dis_buf ), -//-- nameIReg(sz, gregOfRM(modrm))); -//-- -//-- uInstr1(cb, PUSH, sz, TempReg, t); -//-- uInstr1(cb, CALLM, 0, Lit16, helper); -//-- uFlagsRWU(cb, FlagsEmpty, FlagZ, FlagsOSACP); -//-- uInstr1(cb, POP, sz, TempReg, t); -//-- uInstr1(cb, POP, sz, TempReg, t); -//-- uInstr2(cb, PUT, sz, TempReg, t, ArchReg, gregOfRM(modrm)); -//-- uInstr0(cb, CALLM_E, 0); -//-- -//-- return eip; -//-- } + + + +/* Handle BSF/BSR. Only v-size seems necessary. */ +static +UInt dis_bs_E_G ( UChar sorb, Int sz, UInt delta, Bool fwds ) +{ + Bool isReg; + UChar modrm; + Char dis_buf[50]; + + IRType ty = szToITy(sz); + IRTemp src = newTemp(ty); + IRTemp dst = newTemp(ty); + + IRTemp src32 = newTemp(Ity_I32); + IRTemp dst32 = newTemp(Ity_I32); + IRTemp src8 = newTemp(Ity_I8); + + vassert(sz == 4 || sz == 2); + vassert(sz == 4); + + modrm = getIByte(delta); + + isReg = epartIsReg(modrm); + if (isReg) { + delta++; + assign( src, getIReg(sz, eregOfRM(modrm)) ); + } else { + Int len; + IRTemp addr = disAMode( &len, sorb, delta, dis_buf ); + delta += len; + assign( src, loadLE(ty, mkexpr(addr)) ); + } + + DIP("bs%c%c %s, %s\n", + fwds ? 'f' : 'r', nameISize(sz), + ( isReg ? nameIReg(sz, eregOfRM(modrm)) : dis_buf ), + nameIReg(sz, gregOfRM(modrm))); + + /* Generate an 8-bit expression which is zero iff the + original is zero, and nonzero otherwise */ + assign( src8, + unop(Iop_1Uto8, binop(mkSizedOp(ty,Iop_CmpNE8), + mkexpr(src), mkU(ty,0))) ); + + /* Flags: Z is 1 iff source value is zero. All others + are undefined -- we force them to zero. */ + stmt( IRStmt_Put( OFFB_CC_OP, mkU32(CC_OP_COPY) )); + stmt( IRStmt_Put( OFFB_CC_DST, mkU32(0) )); + stmt( IRStmt_Put( + OFFB_CC_SRC, + IRExpr_Mux0X( mkexpr(src8), + /* src==0 */ + mkU32(CC_MASK_Z), + /* src!=0 */ + mkU32(0) + ) + )); + + /* Result: iff source value is zero, we can't use + Iop_Clz32/Iop_Ctz32 as they have no defined result in that case. + But anyway, Intel x86 semantics say the result is undefined in + such situations. Hence handle the zero case specially. */ + + /* Bleh. What we compute: + + bsf32: if src == 0 then 0 else Ctz32(src) + bsr32: if src == 0 then 0 else 31 - Clz32(src) + + bsf16: if src == 0 then 0 else Ctz32(16Uto32(src)) + bsr16: if src == 0 then 0 else 31 - Clz32(16Uto32(src)) + + First, widen src to 32 bits if it is not already. + */ + if (sz == 2) + assign( src32, unop(Iop_16Uto32, mkexpr(src)) ); + else + assign( src32, mkexpr(src) ); + + /* The main computation, guarding against zero. */ + assign( dst32, + IRExpr_Mux0X( + mkexpr(src8), + /* src == 0 */ + mkU32(0), + /* src != 0 */ + fwds ? unop(Iop_Ctz32, mkexpr(src32)) + : binop(Iop_Sub32, + mkU32(31), + unop(Iop_Clz32, mkexpr(src32))) + ) + ); + + if (sz == 2) + assign( dst, unop(Iop_32to16, mkexpr(dst32)) ); + else + assign( dst, mkexpr(dst32) ); + + /* dump result back */ + putIReg( sz, gregOfRM(modrm), mkexpr(dst) ); + + return delta; +} static @@ -7761,15 +7831,15 @@ static UInt disInstr ( UInt delta, Bool* isEnd ) //-- d32 = getSDisp8(eip + am_sz); //-- eip = dis_Grp8_BT ( cb, sorb, eip, modrm, am_sz, sz, d32 ); //-- break; -//-- -//-- /* =-=-=-=-=-=-=-=-=- BSF/BSR -=-=-=-=-=-=-=-=-=-= */ -//-- -//-- case 0xBC: /* BSF Gv,Ev */ -//-- eip = dis_bs_E_G ( cb, sorb, sz, eip, True ); -//-- break; -//-- case 0xBD: /* BSR Gv,Ev */ -//-- eip = dis_bs_E_G ( cb, sorb, sz, eip, False ); -//-- break; + + /* =-=-=-=-=-=-=-=-=- BSF/BSR -=-=-=-=-=-=-=-=-=-= */ + + case 0xBC: /* BSF Gv,Ev */ + delta = dis_bs_E_G ( sorb, sz, delta, True ); + break; + case 0xBD: /* BSR Gv,Ev */ + delta = dis_bs_E_G ( sorb, sz, delta, False ); + break; /* =-=-=-=-=-=-=-=-=- BSWAP -=-=-=-=-=-=-=-=-=-=-= */ @@ -7834,7 +7904,7 @@ static UInt disInstr ( UInt delta, Bool* isEnd ) case 0x45: /* CMOVNZb/CMOVNEb (cmov not zero) */ case 0x46: /* CMOVBEb/CMOVNAb (cmov below or equal) */ case 0x47: /* CMOVNBEb/CMOVAb (cmov not below or equal) */ -//-- case 0x48: /* CMOVSb (cmov negative) */ + case 0x48: /* CMOVSb (cmov negative) */ case 0x49: /* CMOVSb (cmov not negative) */ //-- case 0x4A: /* CMOVP (cmov parity even) */ //-- case 0x4B: /* CMOVNP (cmov parity odd) */ diff --git a/VEX/priv/host-x86/hdefs.c b/VEX/priv/host-x86/hdefs.c index 0c3bf5e9ca..8e90ac911e 100644 --- a/VEX/priv/host-x86/hdefs.c +++ b/VEX/priv/host-x86/hdefs.c @@ -544,6 +544,15 @@ X86Instr* X86Instr_Set32 ( X86CondCode cond, HReg dst ) { i->Xin.Set32.dst = dst; return i; } +X86Instr* X86Instr_Bsfr32 ( Bool isFwds, HReg src, HReg dst ) +{ + X86Instr* i = LibVEX_Alloc(sizeof(X86Instr)); + i->tag = Xin_Bsfr32; + i->Xin.Bsfr32.isFwds = isFwds; + i->Xin.Bsfr32.src = src; + i->Xin.Bsfr32.dst = dst; + return i; +} X86Instr* X86Instr_FpUnary ( X86FpOp op, HReg src, HReg dst ) { X86Instr* i = LibVEX_Alloc(sizeof(X86Instr)); i->tag = Xin_FpUnary; @@ -695,6 +704,12 @@ void ppX86Instr ( X86Instr* i ) { vex_printf("setl%s ", showX86CondCode(i->Xin.Set32.cond)); ppHRegX86(i->Xin.Set32.dst); return; + case Xin_Bsfr32: + vex_printf("bs%cl ", i->Xin.Bsfr32.isFwds ? 'f' : 'r'); + ppHRegX86(i->Xin.Bsfr32.src); + vex_printf(","); + ppHRegX86(i->Xin.Bsfr32.dst); + return; case Xin_FpUnary: vex_printf("g%sD ", showX86FpOp(i->Xin.FpUnary.op)); ppHRegX86(i->Xin.FpUnary.src); @@ -829,6 +844,10 @@ void getRegUsage_X86Instr (HRegUsage* u, X86Instr* i) case Xin_Set32: addHRegUse(u, HRmWrite, i->Xin.Set32.dst); return; + case Xin_Bsfr32: + addHRegUse(u, HRmRead, i->Xin.Bsfr32.src); + addHRegUse(u, HRmWrite, i->Xin.Bsfr32.dst); + return; case Xin_FpUnary: addHRegUse(u, HRmRead, i->Xin.FpUnary.src); addHRegUse(u, HRmWrite, i->Xin.FpUnary.dst); @@ -923,6 +942,10 @@ void mapRegs_X86Instr (HRegRemap* m, X86Instr* i) case Xin_Set32: mapReg(m, &i->Xin.Set32.dst); return; + case Xin_Bsfr32: + mapReg(m, &i->Xin.Bsfr32.src); + mapReg(m, &i->Xin.Bsfr32.dst); + return; case Xin_FpBinary: mapReg(m, &i->Xin.FpBinary.srcL); mapReg(m, &i->Xin.FpBinary.srcR); @@ -1187,7 +1210,9 @@ static UChar* do_fop_st ( UChar* p, X86FpOp op, Int i ) Int subopc; switch (op) { case Xfp_ADD: subopc = 0; break; + case Xfp_SUB: subopc = 4; break; case Xfp_MUL: subopc = 1; break; + case Xfp_DIV: subopc = 6; break; default: vpanic("do_fop_st: unknown op"); } *p++ = 0xD8; @@ -1648,6 +1673,16 @@ Int emit_X86Instr ( UChar* buf, Int nbuf, X86Instr* i ) } goto done; + case Xin_Bsfr32: + *p++ = 0x0F; + if (i->Xin.Bsfr32.isFwds) { + *p++ = 0xBC; + } else { + *p++ = 0xBD; + } + p = doAMode_R(p, i->Xin.Bsfr32.dst, i->Xin.Bsfr32.src); + goto done; + case Xin_Store: if (i->Xin.Store.sz == 2) { /* This case, at least, is simple, given that we can diff --git a/VEX/priv/host-x86/hdefs.h b/VEX/priv/host-x86/hdefs.h index 145358f28e..4b861363f3 100644 --- a/VEX/priv/host-x86/hdefs.h +++ b/VEX/priv/host-x86/hdefs.h @@ -274,6 +274,7 @@ typedef Xin_LoadEX, /* mov{s,z}{b,w}l from mem to reg */ Xin_Store, /* store 16/8 bit value in memory */ Xin_Set32, /* convert condition code to 32-bit value */ + Xin_Bsfr32, /* 32-bit bsf/bsr */ Xin_FpUnary, /* FP fake unary op */ Xin_FpBinary, /* FP fake binary op */ Xin_FpLdSt, /* FP fake load/store */ @@ -373,6 +374,12 @@ typedef X86CondCode cond; HReg dst; } Set32; + /* 32-bit bsf or bsr. */ + struct { + Bool isFwds; + HReg src; + HReg dst; + } Bsfr32; /* X86 Floating point (fake 3-operand, "flat reg file" insns) */ struct { X86FpOp op; @@ -424,6 +431,7 @@ extern X86Instr* X86Instr_LoadEX ( UChar szSmall, Bool syned, X86AMode* src, HReg dst ); extern X86Instr* X86Instr_Store ( UChar sz, HReg src, X86AMode* dst ); extern X86Instr* X86Instr_Set32 ( X86CondCode cond, HReg dst ); +extern X86Instr* X86Instr_Bsfr32 ( Bool isFwds, HReg src, HReg dst ); extern X86Instr* X86Instr_FpUnary ( X86FpOp op, HReg src, HReg dst ); extern X86Instr* X86Instr_FpBinary ( X86FpOp op, HReg srcL, HReg srcR, HReg dst ); extern X86Instr* X86Instr_FpLdSt ( Bool isLoad, UChar sz, HReg reg, X86AMode* ); diff --git a/VEX/priv/host-x86/isel.c b/VEX/priv/host-x86/isel.c index 146077f491..03bed0cf8e 100644 --- a/VEX/priv/host-x86/isel.c +++ b/VEX/priv/host-x86/isel.c @@ -587,6 +587,27 @@ static HReg iselIntExpr_R_wrk ( ISelEnv* env, IRExpr* e ) addInstr(env, X86Instr_Set32(cond,dst)); return dst; } + case Iop_Ctz32: { + /* Count trailing zeroes, implemented by x86 'bsfl' */ + HReg dst = newVRegI(env); + HReg src = iselIntExpr_R(env, e->Iex.Unop.arg); + addInstr(env, X86Instr_Bsfr32(True,src,dst)); + return dst; + } + case Iop_Clz32: { + /* Count leading zeroes. Do 'bsrl' to establish the index + of the highest set bit, and subtract that value from + 31. */ + HReg tmp = newVRegI(env); + HReg dst = newVRegI(env); + HReg src = iselIntExpr_R(env, e->Iex.Unop.arg); + addInstr(env, X86Instr_Bsfr32(False,src,tmp)); + addInstr(env, X86Instr_Alu32R(Xalu_MOV, + X86RMI_Imm(31), dst)); + addInstr(env, X86Instr_Alu32R(Xalu_SUB, + X86RMI_Reg(tmp), dst)); + return dst; + } case Iop_16to8: case Iop_32to8: case Iop_32to16: @@ -1352,7 +1373,9 @@ static HReg iselDblExpr ( ISelEnv* env, IRExpr* e ) X86FpOp fpop = Xfp_INVALID; switch (e->Iex.Binop.op) { case Iop_AddF64: fpop = Xfp_ADD; break; + case Iop_SubF64: fpop = Xfp_SUB; break; case Iop_MulF64: fpop = Xfp_MUL; break; + case Iop_DivF64: fpop = Xfp_DIV; break; default: break; } if (fpop != Xfp_INVALID) { diff --git a/VEX/priv/ir/irdefs.c b/VEX/priv/ir/irdefs.c index accf6f7a13..9b1dfc3361 100644 --- a/VEX/priv/ir/irdefs.c +++ b/VEX/priv/ir/irdefs.c @@ -106,6 +106,9 @@ void ppIROp ( IROp op ) case Iop_MullU16: vex_printf("MullU16"); return; case Iop_MullU32: vex_printf("MullU32"); return; + case Iop_Clz32: vex_printf("Clz32"); return; + case Iop_Ctz32: vex_printf("Ctz32"); return; + case Iop_CmpLT32S: vex_printf("CmpLT32S"); return; case Iop_CmpLE32S: vex_printf("CmpLE32S"); return; case Iop_CmpLT32U: vex_printf("CmpLT32U"); return; @@ -127,7 +130,9 @@ void ppIROp ( IROp op ) case Iop_32HLto64: vex_printf("32HLto64"); return; case Iop_AddF64: vex_printf("AddF64"); return; + case Iop_SubF64: vex_printf("SubF64"); return; case Iop_MulF64: vex_printf("MulF64"); return; + case Iop_DivF64: vex_printf("DivF64"); return; case Iop_I64toF64: vex_printf("I64toF64"); return; default: vpanic("ppIROp(1)"); @@ -577,6 +582,10 @@ void typeOfPrimop ( IROp op, IRType* t_dst, IRType* t_arg1, IRType* t_arg2 ) case Iop_MullU32: case Iop_MullS32: BINARY(Ity_I64,Ity_I32,Ity_I32); + case Iop_Clz32: + case Iop_Ctz32: + UNARY(Ity_I32,Ity_I32); + case Iop_DivModU64to32: case Iop_DivModS64to32: BINARY(Ity_I64,Ity_I64,Ity_I32); @@ -606,7 +615,8 @@ void typeOfPrimop ( IROp op, IRType* t_dst, IRType* t_arg1, IRType* t_arg2 ) case Iop_32Sto64: UNARY(Ity_I64,Ity_I32); case Iop_32to8: UNARY(Ity_I8,Ity_I32); - case Iop_AddF64: case Iop_MulF64: + case Iop_AddF64: case Iop_SubF64: + case Iop_MulF64: case Iop_DivF64: BINARY(Ity_F64,Ity_F64,Ity_F64); case Iop_I64toF64: UNARY(Ity_F64,Ity_I64); diff --git a/VEX/pub/libvex_ir.h b/VEX/pub/libvex_ir.h index 6e211d8e08..fc6814fc2a 100644 --- a/VEX/pub/libvex_ir.h +++ b/VEX/pub/libvex_ir.h @@ -98,6 +98,9 @@ typedef /* 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 */ /* Ordering not important after here. */ Iop_CmpLT32S, Iop_CmpLE32S, @@ -135,6 +138,11 @@ typedef } 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 );