From: Julian Seward Date: Mon, 19 Jul 2004 12:48:11 +0000 (+0000) Subject: Complete first pass thru x86 instruction selector. X-Git-Tag: svn/VALGRIND_3_0_1^2~1237 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=439b0146d4704dc5d536a4c3ac58e9f8407ddae2;p=thirdparty%2Fvalgrind.git Complete first pass thru x86 instruction selector. git-svn-id: svn://svn.valgrind.org/vex/trunk@97 --- diff --git a/VEX/priv/host-x86/isel_x86.c b/VEX/priv/host-x86/isel_x86.c index 1842395cc2..33d84cf53f 100644 --- a/VEX/priv/host-x86/isel_x86.c +++ b/VEX/priv/host-x86/isel_x86.c @@ -282,8 +282,8 @@ static HReg iselIntExpr_R ( ISelEnv* env, IRExpr* e ) case Iop_Sub8: case Iop_Sub16: case Iop_Sub32: aluOp = Xalu_SUB; break; - case Iop_And8: - case Iop_And32: aluOp = Xalu_AND; break; + case Iop_And8: case Iop_And16: case Iop_And32: + aluOp = Xalu_AND; break; case Iop_Or8: case Iop_Or32: aluOp = Xalu_OR; break; @@ -305,7 +305,8 @@ static HReg iselIntExpr_R ( ISelEnv* env, IRExpr* e ) /* Perhaps a shift op? */ switch (e->Iex.Binop.op) { case Iop_Shl32: shOp = Xsh_SHL; break; - case Iop_Shr32: shOp = Xsh_SHR; break; + case Iop_Shr8: case Iop_Shr32: + shOp = Xsh_SHR; break; case Iop_Sar32: shOp = Xsh_SAR; break; default: shOp = Xsh_INVALID; break; } @@ -315,6 +316,19 @@ static HReg iselIntExpr_R ( ISelEnv* env, IRExpr* e ) HReg regR = iselIntExpr_R(env, e->Iex.Binop.arg2); addInstr(env, mk_MOVsd_RR(regL,dst)); addInstr(env, mk_MOVsd_RR(regR,hregX86_ECX())); + /* Do any necessary widening for 16/8 bit operands */ + switch (e->Iex.Binop.op) { + case Iop_Shr8: + addInstr(env, X86Instr_Alu32R( + Xalu_AND, X86RMI_Imm(0xFF), dst)); + break; + case Iop_Shr16: + addInstr(env, X86Instr_Alu32R( + Xalu_AND, X86RMI_Imm(0xFFFF), dst)); + break; + case Iop_Sar8: case Iop_Sar16: vassert(0); // fill this in! + default: break; + } addInstr(env, X86Instr_Sh32(shOp, 0/* %cl */, X86RM_Reg(dst))); return dst; } @@ -445,7 +459,7 @@ static HReg iselIntExpr_R ( ISelEnv* env, IRExpr* e ) HReg r8 = iselIntExpr_R(env, e->Iex.Mux0X.cond); addInstr(env, X86Instr_Alu32R(Xalu_TEST, X86RMI_Imm(0xFF), r8)); - addInstr(env, X86Instr_CMovZ(r0,dst)); + addInstr(env, X86Instr_CMov32(Xcc_Z,r0,dst)); return dst; } break; @@ -688,6 +702,76 @@ static void iselIntExpr64 ( HReg* rHi, HReg* rLo, ISelEnv* env, IRExpr* e ) return; } + /* 64 x 32 -> (32(rem),32(div)) division */ + if (e->tag == Iex_Binop + && (e->Iex.Binop.op == Iop_DivModU64to32 + || e->Iex.Binop.op == Iop_DivModS64to32)) { + /* Get the 64-bit operand into edx:eax, and the other + into any old R/M. */ + HReg sHi, sLo; + Bool syned = e->Iex.Binop.op == Iop_MullS32; + X86RM* rmRight = iselIntExpr_RM(env, e->Iex.Binop.arg2); + iselIntExpr64(&sHi,&sLo, env, e->Iex.Binop.arg1); + addInstr(env, mk_MOVsd_RR(sHi, hregX86_EDX())); + addInstr(env, mk_MOVsd_RR(sLo, hregX86_EAX())); + addInstr(env, X86Instr_Div(syned, Xss_32, rmRight)); + *rHi = hregX86_EDX(); + *rLo = hregX86_EAX(); + return; + } + + /* 32HLto64(e1,e2) */ + if (e->tag == Iex_Binop + && e->Iex.Binop.op == Iop_32HLto64) { + *rHi = iselIntExpr_R(env, e->Iex.Binop.arg1); + *rLo = iselIntExpr_R(env, e->Iex.Binop.arg2); + return; + } + + /* 64-bit shifts */ + if (e->tag == Iex_Binop + && e->Iex.Binop.op == Iop_Shl64) { + /* We use the same ingenious scheme as gcc. Put the value + to be shifted into %hi:%lo, and the shift amount into %cl. + Then (dsts on right, a la ATT syntax): + + shldl %cl, %lo, %hi -- make %hi be right for the shift amt + -- %cl % 32 + shll %cl, %lo -- make %lo be right for the shift amt + -- %cl % 32 + + Now, if (shift amount % 64) is in the range 32 .. 63, we have + to do a fixup, which puts the result low half into the result + high half, and zeroes the low half: + + testl $32, %ecx + + cmovnz %lo, %hi + movl $0, %tmp -- sigh; need yet another reg + cmovnz %tmp, %lo + */ + HReg rAmt, sHi, sLo, tHi, tLo, tTemp; + tLo = newVRegI(env); + tHi = newVRegI(env); + tTemp = newVRegI(env); + rAmt = iselIntExpr_R(env, e->Iex.Binop.arg2); + iselIntExpr64(&sHi,&sLo, env, e->Iex.Binop.arg1); + addInstr(env, mk_MOVsd_RR(rAmt, hregX86_ECX())); + addInstr(env, mk_MOVsd_RR(sHi, tHi)); + addInstr(env, mk_MOVsd_RR(sLo, tLo)); + /* Ok. Now shift amt is in %ecx, and value is in tHi/tLo and + those regs are legitimately modifiable. */ + addInstr(env, X86Instr_Sh3232(Xsh_SHL, 0/*%cl*/, tHi, tLo)); + addInstr(env, X86Instr_Sh32(Xsh_SHL, 0/*%cl*/, X86RM_Reg(tLo))); + addInstr(env, X86Instr_Alu32R(Xalu_TEST, X86RMI_Imm(32), hregX86_ECX())); + addInstr(env, X86Instr_CMov32(Xcc_NZ, X86RM_Reg(tLo), tHi)); + addInstr(env, X86Instr_Alu32R(Xalu_MOV, X86RMI_Imm(0), tTemp)); + addInstr(env, X86Instr_CMov32(Xcc_NZ, X86RM_Reg(tTemp), tLo)); + *rHi = tHi; + *rLo = tLo; + return; + } + ppIRExpr(e); vpanic("iselIntExpr64"); } diff --git a/VEX/priv/host-x86/x86h_defs.c b/VEX/priv/host-x86/x86h_defs.c index fc81014582..dc4fde7bfb 100644 --- a/VEX/priv/host-x86/x86h_defs.c +++ b/VEX/priv/host-x86/x86h_defs.c @@ -428,6 +428,14 @@ X86Instr* X86Instr_MulL ( Bool syned, X86ScalarSz ssz , X86RM* src ) { i->Xin.MulL.src = src; return i; } +X86Instr* X86Instr_Div ( Bool syned, X86ScalarSz ssz, X86RM* src ) { + X86Instr* i = LibVEX_Alloc(sizeof(X86Instr)); + i->tag = Xin_Div; + i->Xin.Div.syned = syned; + i->Xin.Div.ssz = ssz; + i->Xin.Div.src = src; + return i; +} X86Instr* X86Instr_Sh32 ( X86ShiftOp op, UInt src, X86RM* dst ) { X86Instr* i = LibVEX_Alloc(sizeof(X86Instr)); i->tag = Xin_Sh32; @@ -436,6 +444,16 @@ X86Instr* X86Instr_Sh32 ( X86ShiftOp op, UInt src, X86RM* dst ) { i->Xin.Sh32.dst = dst; return i; } +X86Instr* X86Instr_Sh3232 ( X86ShiftOp op, UInt amt, HReg rHi, HReg rLo ) { + X86Instr* i = LibVEX_Alloc(sizeof(X86Instr)); + i->tag = Xin_Sh3232; + i->Xin.Sh3232.op = op; + i->Xin.Sh3232.amt = amt; + i->Xin.Sh3232.rHi = rHi; + i->Xin.Sh3232.rLo = rLo; + vassert(op == Xsh_SHL || op == Xsh_SHR); + return i; +} X86Instr* X86Instr_Push( X86RMI* src ) { X86Instr* i = LibVEX_Alloc(sizeof(X86Instr)); i->tag = Xin_Push; @@ -455,11 +473,13 @@ X86Instr* X86Instr_Goto ( X86CondCode cond, X86RI* dst ) { i->Xin.Goto.dst = dst; return i; } -X86Instr* X86Instr_CMovZ ( X86RM* src, HReg dst ) { - X86Instr* i = LibVEX_Alloc(sizeof(X86Instr)); - i->tag = Xin_CMovZ; - i->Xin.CMovZ.src = src; - i->Xin.CMovZ.dst = dst; +X86Instr* X86Instr_CMov32 ( X86CondCode cond, X86RM* src, HReg dst ) { + X86Instr* i = LibVEX_Alloc(sizeof(X86Instr)); + i->tag = Xin_CMov32; + i->Xin.CMov32.cond = cond; + i->Xin.CMov32.src = src; + i->Xin.CMov32.dst = dst; + vassert(cond != Xcc_ALWAYS); return i; } X86Instr* X86Instr_LoadEX ( UChar szSmall, Bool syned, @@ -508,6 +528,12 @@ void ppX86Instr ( X86Instr* i ) { showX86ScalarSz(i->Xin.MulL.ssz)); ppX86RM(i->Xin.MulL.src); return; + case Xin_Div: + vex_printf("%cdiv%s ", + i->Xin.Div.syned ? 's' : 'u', + showX86ScalarSz(i->Xin.Div.ssz)); + ppX86RM(i->Xin.Div.src); + return; case Xin_Sh32: vex_printf("%sl ", showX86ShiftOp(i->Xin.Sh32.op)); if (i->Xin.Sh32.src == 0) @@ -516,6 +542,16 @@ void ppX86Instr ( X86Instr* i ) { vex_printf(" $%d,", i->Xin.Sh32.src); ppX86RM(i->Xin.Sh32.dst); return; + case Xin_Sh3232: + vex_printf("%sdl ", showX86ShiftOp(i->Xin.Sh3232.op)); + if (i->Xin.Sh3232.amt == 0) + vex_printf(" %%cl,"); + else + vex_printf(" $%d,", i->Xin.Sh3232.amt); + ppHRegX86(i->Xin.Sh3232.rLo); + vex_printf(","); + ppHRegX86(i->Xin.Sh3232.rHi); + return; case Xin_Push: vex_printf("pushl "); ppX86RMI(i->Xin.Push.src); @@ -536,11 +572,11 @@ void ppX86Instr ( X86Instr* i ) { vex_printf(",%%eax ; ret }"); } return; - case Xin_CMovZ: - vex_printf("cmovz "); - ppX86RM(i->Xin.CMovZ.src); + case Xin_CMov32: + vex_printf("cmovl%s ", showX86CondCode(i->Xin.CMov32.cond)); + ppX86RM(i->Xin.CMov32.src); vex_printf(","); - ppHRegX86(i->Xin.CMovZ.dst); + ppHRegX86(i->Xin.CMov32.dst); return; case Xin_LoadEX: vex_printf("mov%c%cl ", diff --git a/VEX/priv/host-x86/x86h_defs.h b/VEX/priv/host-x86/x86h_defs.h index c09fabb664..eb8231796c 100644 --- a/VEX/priv/host-x86/x86h_defs.h +++ b/VEX/priv/host-x86/x86h_defs.h @@ -250,11 +250,13 @@ typedef Xin_Alu32M, /* 32-bit mov/arith/logical, dst=MEM */ Xin_Unary32, /* 32-bit not and neg */ Xin_MulL, /* widening multiply */ + Xin_Div, /* div and mod */ Xin_Sh32, /* 32-bit shift/rotate, dst=REG or MEM */ + Xin_Sh3232, /* shldl or shrdl */ Xin_Push, /* push (32-bit?) value on stack */ Xin_Call, /* call to address in register */ Xin_Goto, /* conditional/unconditional jmp to dst */ - Xin_CMovZ, /* conditional move when Z flag set */ + Xin_CMov32, /* conditional move */ Xin_LoadEX, /* mov{s,z}{b,w}l from mem to reg */ Xin_Store /* store 16/8 bit value in memory */ } @@ -287,11 +289,24 @@ typedef X86ScalarSz ssz; X86RM* src; } MulL; + /* x86 div/idiv instruction */ + struct { + Bool syned; + X86ScalarSz ssz; + X86RM* src; + } Div; struct { X86ShiftOp op; UInt src; /* shift amount, or 0 means %cl */ X86RM* dst; } Sh32; + /* shld/shrd. op may only be Xsh_SHL or Xsh_SHR */ + struct { + X86ShiftOp op; + UInt amt; /* shift amount, or 0 means %cl */ + HReg rHi; + HReg rLo; + } Sh3232; struct { X86RMI* src; } Push; @@ -304,12 +319,13 @@ typedef X86CondCode cond; X86RI* dst; } Goto; - /* Mov src to dst (both 32-bit regs?) when the Z flag is - set. */ + /* Mov src to dst on the given condition, which may not + be the bogus Xcc_ALWAYS. */ struct { + X86CondCode cond; X86RM* src; HReg dst; - } CMovZ; + } CMov32; /* Sign/Zero extending loads. Dst size is always 32 bits. */ struct { UChar szSmall; @@ -332,11 +348,13 @@ extern X86Instr* X86Instr_Alu32R ( X86AluOp, X86RMI*, HReg ); extern X86Instr* X86Instr_Alu32M ( X86AluOp, X86RI*, X86AMode* ); extern X86Instr* X86Instr_Unary32 ( X86UnaryOp op, X86RM* dst ); extern X86Instr* X86Instr_MulL ( Bool syned, X86ScalarSz, X86RM* ); +extern X86Instr* X86Instr_Div ( Bool syned, X86ScalarSz, X86RM* ); extern X86Instr* X86Instr_Sh32 ( X86ShiftOp, UInt, X86RM* ); +extern X86Instr* X86Instr_Sh3232 ( X86ShiftOp, UInt amt, HReg rHi, HReg rLo ); extern X86Instr* X86Instr_Push ( X86RMI* ); extern X86Instr* X86Instr_Call ( HReg ); extern X86Instr* X86Instr_Goto ( X86CondCode cond, X86RI* dst ); -extern X86Instr* X86Instr_CMovZ ( X86RM* src, HReg dst ); +extern X86Instr* X86Instr_CMov32 ( X86CondCode, X86RM* src, HReg dst ); extern X86Instr* X86Instr_LoadEX ( UChar szSmall, Bool syned, X86AMode* src, HReg dst ); extern X86Instr* X86Instr_Store ( UChar sz, HReg src, X86AMode* dst );