# define unop(_op,_a1) IRExpr_Unop((_op),(_a1))
# define binop(_op,_a1,_a2) IRExpr_Binop((_op),(_a1),(_a2))
# define mkU64(_n) IRExpr_Const(IRConst_U64(_n))
+# define mkU32(_n) IRExpr_Const(IRConst_U32(_n))
# define mkU8(_n) IRExpr_Const(IRConst_U8(_n))
Int i, arity = 0;
if (isU64(cc_op, AMD64G_CC_OP_SUBL) && isU64(cond, AMD64CondZ)) {
/* long sub/cmp, then Z --> test dst==src */
return unop(Iop_1Uto64,
- binop(Iop_CmpEQ64,
- binop(Iop_Shl64,cc_dep1,mkU8(32)),
- binop(Iop_Shl64,cc_dep2,mkU8(32))));
+ binop(Iop_CmpEQ32,
+ unop(Iop_64to32, cc_dep1),
+ unop(Iop_64to32, cc_dep2)));
}
if (isU64(cc_op, AMD64G_CC_OP_SUBL) && isU64(cond, AMD64CondNZ)) {
/* long sub/cmp, then NZ --> test dst!=src */
return unop(Iop_1Uto64,
- binop(Iop_CmpNE64,
- binop(Iop_Shl64,cc_dep1,mkU8(32)),
- binop(Iop_Shl64,cc_dep2,mkU8(32))));
+ binop(Iop_CmpNE32,
+ unop(Iop_64to32, cc_dep1),
+ unop(Iop_64to32, cc_dep2)));
}
if (isU64(cc_op, AMD64G_CC_OP_SUBL) && isU64(cond, AMD64CondL)) {
/* long sub/cmp, then L (signed less than)
--> test dst <s src */
return unop(Iop_1Uto64,
- binop(Iop_CmpLT64S,
- binop(Iop_Shl64,cc_dep1,mkU8(32)),
- binop(Iop_Shl64,cc_dep2,mkU8(32))));
+ binop(Iop_CmpLT32S,
+ unop(Iop_64to32, cc_dep1),
+ unop(Iop_64to32, cc_dep2)));
}
if (isU64(cc_op, AMD64G_CC_OP_SUBL) && isU64(cond, AMD64CondLE)) {
/* long sub/cmp, then LE (signed less than or equal)
--> test dst <=s src */
return unop(Iop_1Uto64,
- binop(Iop_CmpLE64S,
- binop(Iop_Shl64,cc_dep1,mkU8(32)),
- binop(Iop_Shl64,cc_dep2,mkU8(32))));
+ binop(Iop_CmpLE32S,
+ unop(Iop_64to32, cc_dep1),
+ unop(Iop_64to32, cc_dep2)));
}
if (isU64(cc_op, AMD64G_CC_OP_SUBL) && isU64(cond, AMD64CondNLE)) {
--> test (dst >s src)
--> test (src <s dst) */
return unop(Iop_1Uto64,
- binop(Iop_CmpLT64S,
- binop(Iop_Shl64,cc_dep2,mkU8(32)),
- binop(Iop_Shl64,cc_dep1,mkU8(32))));
+ binop(Iop_CmpLT32S,
+ unop(Iop_64to32, cc_dep2),
+ unop(Iop_64to32, cc_dep1)));
}
/* long sub/cmp, then BE (unsigned less than or equal)
--> test dst <=u src */
return unop(Iop_1Uto64,
- binop(Iop_CmpLE64U,
- binop(Iop_Shl64,cc_dep1,mkU8(32)),
- binop(Iop_Shl64,cc_dep2,mkU8(32))));
+ binop(Iop_CmpLE32U,
+ unop(Iop_64to32, cc_dep1),
+ unop(Iop_64to32, cc_dep2)));
}
if (isU64(cc_op, AMD64G_CC_OP_SUBL) && isU64(cond, AMD64CondNBE)) {
/* long sub/cmp, then NBE (unsigned greater than)
--> test src <u dst */
/* Note, args are opposite way round from the usual */
return unop(Iop_1Uto64,
- binop(Iop_CmpLT64U,
- binop(Iop_Shl64,cc_dep2,mkU8(32)),
- binop(Iop_Shl64,cc_dep1,mkU8(32))));
+ binop(Iop_CmpLT32U,
+ unop(Iop_64to32, cc_dep2),
+ unop(Iop_64to32, cc_dep1)));
}
if (isU64(cc_op, AMD64G_CC_OP_SUBL) && isU64(cond, AMD64CondS)) {
/* long sub/cmp, then S (negative) --> test (dst-src <s 0) */
return unop(Iop_1Uto64,
- binop(Iop_CmpLT64S,
- binop(Iop_Sub64,
- binop(Iop_Shl64, cc_dep1, mkU8(32)),
- binop(Iop_Shl64, cc_dep2, mkU8(32))),
- mkU64(0)));
+ binop(Iop_CmpLT32S,
+ binop(Iop_Sub32,
+ unop(Iop_64to32, cc_dep1),
+ unop(Iop_64to32, cc_dep2)),
+ mkU32(0)));
}
/*---------------- SUBW ----------------*/
if (isU64(cc_op, AMD64G_CC_OP_LOGICL) && isU64(cond, AMD64CondZ)) {
/* long and/or/xor, then Z --> test dst==0 */
return unop(Iop_1Uto64,
- binop(Iop_CmpEQ64,
- binop(Iop_Shl64,cc_dep1,mkU8(32)),
- mkU64(0)));
+ binop(Iop_CmpEQ32,
+ unop(Iop_64to32, cc_dep1),
+ mkU32(0)));
}
if (isU64(cc_op, AMD64G_CC_OP_LOGICL) && isU64(cond, AMD64CondNZ)) {
/* long and/or/xor, then NZ --> test dst!=0 */
return unop(Iop_1Uto64,
- binop(Iop_CmpNE64,
- binop(Iop_Shl64,cc_dep1,mkU8(32)),
- mkU64(0)));
+ binop(Iop_CmpNE32,
+ unop(Iop_64to32, cc_dep1),
+ mkU32(0)));
}
if (isU64(cc_op, AMD64G_CC_OP_LOGICL) && isU64(cond, AMD64CondLE)) {
the result is <=signed 0. Hence ...
*/
return unop(Iop_1Uto64,
- binop(Iop_CmpLE64S,
- binop(Iop_Shl64,cc_dep1,mkU8(32)),
- mkU64(0)));
+ binop(Iop_CmpLE32S,
+ unop(Iop_64to32, cc_dep1),
+ mkU32(0)));
}
/*---------------- LOGICB ----------------*/
if (isU64(cc_op, AMD64G_CC_OP_DECL) && isU64(cond, AMD64CondZ)) {
/* dec L, then Z --> test dst == 0 */
return unop(Iop_1Uto64,
- binop(Iop_CmpEQ64,
- binop(Iop_Shl64,cc_dep1,mkU8(32)),
- mkU64(0)));
+ binop(Iop_CmpEQ32,
+ unop(Iop_64to32, cc_dep1),
+ mkU32(0)));
}
/*---------------- DECW ----------------*/
if (isU64(cc_op, AMD64G_CC_OP_SUBL)) {
/* C after sub denotes unsigned less than */
return unop(Iop_1Uto64,
- binop(Iop_CmpLT64U,
- binop(Iop_Shl64,cc_dep1,mkU8(32)),
- binop(Iop_Shl64,cc_dep2,mkU8(32))));
+ binop(Iop_CmpLT32U,
+ unop(Iop_64to32, cc_dep1),
+ unop(Iop_64to32, cc_dep2)));
}
if (isU64(cc_op, AMD64G_CC_OP_SUBB)) {
/* C after sub denotes unsigned less than */
# undef unop
# undef binop
# undef mkU64
+# undef mkU32
# undef mkU8
return NULL;
return op;
}
-void ppAMD64RMI ( AMD64RMI* op ) {
+static void ppAMD64RMI_wrk ( AMD64RMI* op, Bool lo32 ) {
switch (op->tag) {
case Armi_Imm:
vex_printf("$0x%x", op->Armi.Imm.imm32);
return;
- case Armi_Reg:
- ppHRegAMD64(op->Armi.Reg.reg);
+ case Armi_Reg:
+ if (lo32)
+ ppHRegAMD64_lo32(op->Armi.Reg.reg);
+ else
+ ppHRegAMD64(op->Armi.Reg.reg);
return;
case Armi_Mem:
ppAMD64AMode(op->Armi.Mem.am);
vpanic("ppAMD64RMI");
}
}
+void ppAMD64RMI ( AMD64RMI* op ) {
+ ppAMD64RMI_wrk(op, False/*!lo32*/);
+}
+void ppAMD64RMI_lo32 ( AMD64RMI* op ) {
+ ppAMD64RMI_wrk(op, True/*lo32*/);
+}
/* An AMD64RMI can only be used in a "read" context (what would it mean
to write or modify a literal?) and so we enumerate its registers
i->Ain.Lea64.dst = dst;
return i;
}
+AMD64Instr* AMD64Instr_Alu32R ( AMD64AluOp op, AMD64RMI* src, HReg dst ) {
+ AMD64Instr* i = LibVEX_Alloc(sizeof(AMD64Instr));
+ i->tag = Ain_Alu32R;
+ i->Ain.Alu32R.op = op;
+ i->Ain.Alu32R.src = src;
+ i->Ain.Alu32R.dst = dst;
+ switch (op) {
+ case Aalu_ADD: case Aalu_SUB: case Aalu_CMP:
+ case Aalu_AND: case Aalu_OR: case Aalu_XOR: break;
+ default: vassert(0);
+ }
+ return i;
+}
AMD64Instr* AMD64Instr_MulL ( Bool syned, AMD64RM* src ) {
AMD64Instr* i = LibVEX_Alloc(sizeof(AMD64Instr));
i->tag = Ain_MulL;
vex_printf(",");
ppHRegAMD64(i->Ain.Lea64.dst);
return;
+ case Ain_Alu32R:
+ vex_printf("%sl ", showAMD64AluOp(i->Ain.Alu32R.op));
+ ppAMD64RMI_lo32(i->Ain.Alu32R.src);
+ vex_printf(",");
+ ppHRegAMD64_lo32(i->Ain.Alu32R.dst);
+ return;
case Ain_MulL:
vex_printf("%cmulq ", i->Ain.MulL.syned ? 's' : 'u');
ppAMD64RM(i->Ain.MulL.src);
addRegUsage_AMD64AMode(u, i->Ain.Lea64.am);
addHRegUse(u, HRmWrite, i->Ain.Lea64.dst);
return;
+ case Ain_Alu32R:
+ vassert(i->Ain.Alu32R.op != Aalu_MOV);
+ addRegUsage_AMD64RMI(u, i->Ain.Alu32R.src);
+ if (i->Ain.Alu32R.op == Aalu_CMP) {
+ addHRegUse(u, HRmRead, i->Ain.Alu32R.dst);
+ return;
+ }
+ addHRegUse(u, HRmModify, i->Ain.Alu32R.dst);
+ return;
case Ain_MulL:
addRegUsage_AMD64RM(u, i->Ain.MulL.src, HRmRead);
addHRegUse(u, HRmModify, hregAMD64_RAX());
mapRegs_AMD64AMode(m, i->Ain.Lea64.am);
mapReg(m, &i->Ain.Lea64.dst);
return;
+ case Ain_Alu32R:
+ mapRegs_AMD64RMI(m, i->Ain.Alu32R.src);
+ mapReg(m, &i->Ain.Alu32R.dst);
+ return;
case Ain_MulL:
mapRegs_AMD64RM(m, i->Ain.MulL.src);
return;
p = doAMode_M(p, i->Ain.Lea64.dst, i->Ain.Lea64.am);
goto done;
+ case Ain_Alu32R:
+ /* ADD/SUB/AND/OR/XOR/CMP */
+ opc = opc_rr = subopc_imm = opc_imma = 0;
+ switch (i->Ain.Alu32R.op) {
+ case Aalu_ADD: opc = 0x03; opc_rr = 0x01;
+ subopc_imm = 0; opc_imma = 0x05; break;
+ case Aalu_SUB: opc = 0x2B; opc_rr = 0x29;
+ subopc_imm = 5; opc_imma = 0x2D; break;
+ case Aalu_AND: opc = 0x23; opc_rr = 0x21;
+ subopc_imm = 4; opc_imma = 0x25; break;
+ case Aalu_XOR: opc = 0x33; opc_rr = 0x31;
+ subopc_imm = 6; opc_imma = 0x35; break;
+ case Aalu_OR: opc = 0x0B; opc_rr = 0x09;
+ subopc_imm = 1; opc_imma = 0x0D; break;
+ case Aalu_CMP: opc = 0x3B; opc_rr = 0x39;
+ subopc_imm = 7; opc_imma = 0x3D; break;
+ default: goto bad;
+ }
+ switch (i->Ain.Alu32R.src->tag) {
+ case Armi_Imm:
+ if (i->Ain.Alu32R.dst == hregAMD64_RAX()
+ && !fits8bits(i->Ain.Alu32R.src->Armi.Imm.imm32)) {
+ goto bad; /* FIXME: awaiting test case */
+ *p++ = toUChar(opc_imma);
+ p = emit32(p, i->Ain.Alu32R.src->Armi.Imm.imm32);
+ } else
+ if (fits8bits(i->Ain.Alu32R.src->Armi.Imm.imm32)) {
+ rex = clearWBit( rexAMode_R( fake(0), i->Ain.Alu32R.dst ) );
+ if (rex != 0x40) *p++ = rex;
+ *p++ = 0x83;
+ p = doAMode_R(p, fake(subopc_imm), i->Ain.Alu32R.dst);
+ *p++ = toUChar(0xFF & i->Ain.Alu32R.src->Armi.Imm.imm32);
+ } else {
+ rex = clearWBit( rexAMode_R( fake(0), i->Ain.Alu32R.dst) );
+ if (rex != 0x40) *p++ = rex;
+ *p++ = 0x81;
+ p = doAMode_R(p, fake(subopc_imm), i->Ain.Alu32R.dst);
+ p = emit32(p, i->Ain.Alu32R.src->Armi.Imm.imm32);
+ }
+ goto done;
+ case Armi_Reg:
+ rex = clearWBit(
+ rexAMode_R( i->Ain.Alu32R.src->Armi.Reg.reg,
+ i->Ain.Alu32R.dst) );
+ if (rex != 0x40) *p++ = rex;
+ *p++ = toUChar(opc_rr);
+ p = doAMode_R(p, i->Ain.Alu32R.src->Armi.Reg.reg,
+ i->Ain.Alu32R.dst);
+ goto done;
+ case Armi_Mem:
+ rex = clearWBit(
+ rexAMode_M( i->Ain.Alu32R.dst,
+ i->Ain.Alu32R.src->Armi.Mem.am) );
+ if (rex != 0x40) *p++ = rex;
+ *p++ = toUChar(opc);
+ p = doAMode_M(p, i->Ain.Alu32R.dst,
+ i->Ain.Alu32R.src->Armi.Mem.am);
+ goto done;
+ default:
+ goto bad;
+ }
+ break;
+
case Ain_MulL:
subopc = i->Ain.MulL.syned ? 5 : 4;
switch (i->Ain.MulL.src->tag) {
extern AMD64RMI* AMD64RMI_Reg ( HReg );
extern AMD64RMI* AMD64RMI_Mem ( AMD64AMode* );
-extern void ppAMD64RMI ( AMD64RMI* );
+extern void ppAMD64RMI ( AMD64RMI* );
+extern void ppAMD64RMI_lo32 ( AMD64RMI* );
/* --------- Operand, which can be reg or immediate only. --------- */
Ain_Test64, /* 64-bit test (AND, set flags, discard result) */
Ain_Unary64, /* 64-bit not and neg */
Ain_Lea64, /* 64-bit compute EA into a reg */
+ Ain_Alu32R, /* 32-bit add/sub/and/or/xor/cmp, dst=REG (a la Alu64R) */
Ain_MulL, /* widening multiply */
Ain_Div, /* div and mod */
//.. Xin_Sh3232, /* shldl or shrdl */
AMD64AMode* am;
HReg dst;
} Lea64;
+ /* 32-bit add/sub/and/or/xor/cmp, dst=REG (a la Alu64R) */
+ struct {
+ AMD64AluOp op;
+ AMD64RMI* src;
+ HReg dst;
+ } Alu32R;
/* 64 x 64 -> 128 bit widening multiply: RDX:RAX = RAX *s/u
r/m64 */
struct {
extern AMD64Instr* AMD64Instr_Alu64M ( AMD64AluOp, AMD64RI*, AMD64AMode* );
extern AMD64Instr* AMD64Instr_Unary64 ( AMD64UnaryOp op, HReg dst );
extern AMD64Instr* AMD64Instr_Lea64 ( AMD64AMode* am, HReg dst );
+extern AMD64Instr* AMD64Instr_Alu32R ( AMD64AluOp, AMD64RMI*, HReg );
extern AMD64Instr* AMD64Instr_Sh64 ( AMD64ShiftOp, UInt, HReg );
extern AMD64Instr* AMD64Instr_Test64 ( UInt imm32, HReg dst );
extern AMD64Instr* AMD64Instr_MulL ( Bool syned, AMD64RM* );
/* Handle misc other ops. */
if (e->Iex.Binop.op == Iop_Max32U) {
- /* This generates a truly rotten piece of code. Just as well
- it doesn't happen very often. */
- HReg src1 = iselIntExpr_R(env, e->Iex.Binop.arg1);
- HReg src1L = newVRegI(env);
- HReg src2 = iselIntExpr_R(env, e->Iex.Binop.arg2);
- HReg src2L = newVRegI(env);
- HReg dst = newVRegI(env);
- addInstr(env, mk_iMOVsd_RR(src1,dst));
- addInstr(env, mk_iMOVsd_RR(src1,src1L));
- addInstr(env, AMD64Instr_Sh64(Ash_SHL, 32, src1L));
- addInstr(env, mk_iMOVsd_RR(src2,src2L));
- addInstr(env, AMD64Instr_Sh64(Ash_SHL, 32, src2L));
- addInstr(env, AMD64Instr_Alu64R(Aalu_CMP, AMD64RMI_Reg(src2L), src1L));
+ HReg src1 = iselIntExpr_R(env, e->Iex.Binop.arg1);
+ HReg dst = newVRegI(env);
+ HReg src2 = iselIntExpr_R(env, e->Iex.Binop.arg2);
+ addInstr(env, mk_iMOVsd_RR(src1, dst));
+ addInstr(env, AMD64Instr_Alu32R(Aalu_CMP, AMD64RMI_Reg(src2), dst));
addInstr(env, AMD64Instr_CMov64(Acc_B, AMD64RM_Reg(src2), dst));
return dst;
}
}
}
+ /* 32Uto64( Add32/Sub32/And32/Or32/Xor32(expr32, expr32) )
+ Use 32 bit arithmetic and let the default zero-extend rule
+ do the 32Uto64 for free. */
+ if (e->Iex.Unop.op == Iop_32Uto64 && e->Iex.Unop.arg->tag == Iex_Binop) {
+ IROp opi = e->Iex.Unop.arg->Iex.Binop.op; /* inner op */
+ IRExpr* argL = e->Iex.Unop.arg->Iex.Binop.arg1;
+ IRExpr* argR = e->Iex.Unop.arg->Iex.Binop.arg2;
+ AMD64AluOp aluOp = Aalu_INVALID;
+ switch (opi) {
+ case Iop_Add32: aluOp = Aalu_ADD; break;
+ case Iop_Sub32: aluOp = Aalu_SUB; break;
+ case Iop_And32: aluOp = Aalu_AND; break;
+ case Iop_Or32: aluOp = Aalu_OR; break;
+ case Iop_Xor32: aluOp = Aalu_XOR; break;
+ default: break;
+ }
+ if (aluOp != Aalu_INVALID) {
+ /* For commutative ops we assume any literal values are on
+ the second operand. */
+ HReg dst = newVRegI(env);
+ HReg reg = iselIntExpr_R(env, argL);
+ AMD64RMI* rmi = iselIntExpr_RMI(env, argR);
+ addInstr(env, mk_iMOVsd_RR(reg,dst));
+ addInstr(env, AMD64Instr_Alu32R(aluOp, rmi, dst));
+ return dst;
+ }
+ /* just fall through to normal handling for Iop_32Uto64 */
+ }
+
+ /* Fallback cases */
switch (e->Iex.Unop.op) {
case Iop_32Uto64:
case Iop_32Sto64: {
if (e->tag == Iex_Unop
&& e->Iex.Unop.op == Iop_CmpNEZ32) {
HReg r1 = iselIntExpr_R(env, e->Iex.Unop.arg);
- HReg tmp = newVRegI(env);
AMD64RMI* rmi2 = AMD64RMI_Imm(0);
- addInstr(env, AMD64Instr_MovxLQ(False, r1, tmp));
- addInstr(env, AMD64Instr_Alu64R(Aalu_CMP,rmi2,tmp));
+ addInstr(env, AMD64Instr_Alu32R(Aalu_CMP,rmi2,r1));
return Acc_NZ;
}
}
}
- /* CmpEQ32 / CmpNE32 */
- if (e->tag == Iex_Binop
- && (e->Iex.Binop.op == Iop_CmpEQ32
- || e->Iex.Binop.op == Iop_CmpNE32
- || e->Iex.Binop.op == Iop_CasCmpEQ32
- || e->Iex.Binop.op == Iop_CasCmpNE32)) {
- HReg r1 = iselIntExpr_R(env, e->Iex.Binop.arg1);
- AMD64RMI* rmi2 = iselIntExpr_RMI(env, e->Iex.Binop.arg2);
- HReg r = newVRegI(env);
- addInstr(env, mk_iMOVsd_RR(r1,r));
- addInstr(env, AMD64Instr_Alu64R(Aalu_XOR,rmi2,r));
- addInstr(env, AMD64Instr_Sh64(Ash_SHL, 32, r));
- switch (e->Iex.Binop.op) {
- case Iop_CmpEQ32: case Iop_CasCmpEQ32: return Acc_Z;
- case Iop_CmpNE32: case Iop_CasCmpNE32: return Acc_NZ;
- default: vpanic("iselCondCode(amd64): CmpXX32");
- }
- }
-
/* CmpNE64(ccall, 64-bit constant) (--smc-check=all optimisation).
Saves a "movq %rax, %tmp" compared to the default route. */
if (e->tag == Iex_Binop
}
}
+ /* Cmp*32*(x,y) */
+ if (e->tag == Iex_Binop
+ && (e->Iex.Binop.op == Iop_CmpEQ32
+ || e->Iex.Binop.op == Iop_CmpNE32
+ || e->Iex.Binop.op == Iop_CmpLT32S
+ || e->Iex.Binop.op == Iop_CmpLT32U
+ || e->Iex.Binop.op == Iop_CmpLE32S
+ || e->Iex.Binop.op == Iop_CmpLE32U
+ || e->Iex.Binop.op == Iop_CasCmpEQ32
+ || e->Iex.Binop.op == Iop_CasCmpNE32)) {
+ HReg r1 = iselIntExpr_R(env, e->Iex.Binop.arg1);
+ AMD64RMI* rmi2 = iselIntExpr_RMI(env, e->Iex.Binop.arg2);
+ addInstr(env, AMD64Instr_Alu32R(Aalu_CMP,rmi2,r1));
+ switch (e->Iex.Binop.op) {
+ case Iop_CmpEQ32: case Iop_CasCmpEQ32: return Acc_Z;
+ case Iop_CmpNE32: case Iop_CasCmpNE32: return Acc_NZ;
+ case Iop_CmpLT32S: return Acc_L;
+ case Iop_CmpLT32U: return Acc_B;
+ case Iop_CmpLE32S: return Acc_LE;
+ case Iop_CmpLE32U: return Acc_BE;
+ default: vpanic("iselCondCode(amd64): CmpXX32");
+ }
+ }
+
ppIRExpr(e);
vpanic("iselCondCode(amd64)");
}