Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Pentium pairability testing (without float ops)
// Determine which argument references memory, if any
static void determineMem(const TInst *inst, const TArg **mem)
{
int nSrcs = inst->getNumSrcs();
int nDsts = inst->getNumDsts();
for (int i=0; i < nSrcs; i++)
{
const TArg& arg = inst->getSrc(i);
if (arg.isIndirect()) *mem = &arg;
}
for (int i=0; i < nDsts; i++)
{
const TArg &arg = inst->getDst(i);
if (arg.isIndirect()) *mem = &arg;
}
}
/* 1) TEST [MEM], IMM is !pairable
2) TEST REG , IMM is only pairable if REG = {AL, AX, RAX} */
static bool isTestPairable(const TInst* inst)
{
using namespace OpType;
if (inst->getOp() == eTEST && inst->getSrc(1).isImmediate()) {
const TArg& src0 = inst->getSrc(0);
if(src0.isIndirect())
return false;
/* TODO: If we ever generate AL, support must be added */
if(src0.getBaseRegNum() != RegNames::eRAX ||
src0.getBaseRegNum() != RegNames::eEAX)
return false;
}
return true;
}
/* PUSH/POP are !pairable if their argument is either a [MEM] or the SR */
static bool isPushPopPairable(const TInst* inst)
{
using namespace OpType;
const TOp& op = inst->getOp();
if(op == ePUSH && inst->getSrc(0).isIndirect())
return false;
if(op == ePOP && inst->getDst(0).isIndirect())
return false;
/* TODO: If we ever generate SR, support must be added */
return true;
}
/* SHR/SAR/SHL/SAL are U-pairable with immediate count */
static bool isShiftUPairable(const TInst *inst)
{
using namespace OpType;
const TOp& op = inst->getOp();
if ((op == eSHR || op == eSAR || op == eSHL || op == eSAL) &&
!inst->getSrc(inst->getNumSrcs() - 1).isImmediate())
return false;
return true;
}
/* ROR, ROL, RCR, RCL are U-pairable with an immediate count == 1 */
static bool isRotateUPairable(const TInst *inst)
{
using namespace OpType;
const TOp& op = inst->getOp();
if (op == eROR || op == eROL || op == eRCR || op == eRCL) {
TArg lastSrc = inst->getSrc(inst->getNumSrcs() - 1);
if (!lastSrc.isImmediate() || !(lastSrc.getIVal() == 1))
return false;
}
return true;
}
/* Listed ops are !pairable if address contains a displacement */
static bool hasDispImmPair(const TInst *inst, const TArg* memAccess)
{
using namespace OpType;
using namespace AddrMode;
/* TODO: Check that it is indeed the case that there can be no memory
operands on both dst & src positions simultaneously, as well
as a src memory operand coupled with an immediate. */
if (memAccess && inst->getNumDsts() &&
inst->getSrc(inst->getNumSrcs() - 1).isImmediate())
{
// @ryg Why is this only checking destination arg? Doesn't seem right!
if (inst->getDst(0).getAddrMode() & _eDisplacement)
return inst->getDst(0).getDisplacement() != 0;
}
return false;
}
enum tSizeClass
{
SizeBelow8b,
Size8b,
SizeAbove8b,
};
// *RAD*
/* Determine whether an op is <8b, =8b or >8b. This influences pairability. */
static tSizeClass getSizeClass(const TInst *inst, const TArg *memAccess)
{
using namespace OpType;
using namespace AddrMode;
// MOVs can have 64-bit immediates - either an actual immediate or a label
const TOp& op = inst->getOp();
if (op == eMOV &&
(inst->getSrc(0).isLabel() ||
inst->getSrc(0).isImmediate() && inst->getDst(0).isDirect() &&
(inst->getSrc(0).getImmedType() == DataType::uint64 || inst->getSrc(0).getImmedType() == DataType::sint64) &&
(inst->getSrc(0).getU64Val() >> 32) != 0))
return SizeAbove8b;
// Anything else without a memory access is <8b (TODO double-check this!)
if (!memAccess)
return SizeBelow8b;
// Most ops we need to worry about have displacements
tAddrMode addrMode = memAccess->getAddrMode();
bool hasDisp = (addrMode == eDisplacement || addrMode == eBaseDisplacement ||
addrMode == eBaseIndexDisplacement);
bool hasSIB = (addrMode == eBaseIndex || addrMode == eBaseIndexDisplacement);
// If there's a displacement, determine if it requires disp32
bool hasDisp32 = false;
if (hasDisp)
{
_sint64 disp = memAccess->getDisplacement();
int granularity = memGranularity(inst);
if ((disp & (granularity - 1)) ||
disp < -128 * granularity || disp > 127 * granularity)
hasDisp32 = true;
}
// Now, the actual size class depends on the instruction
if (op == eMOV || op == eLEA)
{
// Here it gets ugly: mov reg, [mem] or mov [mem], reg hit 8b iff
// there's a REX byte, a SIB, and a 32-bit displacement
// Same for LEA, though of course there's no [mem], reg variant.
if (hasSIB && hasDisp32)
{
const TArg *regOp = &inst->getSrc(0);
if (regOp == memAccess) // oops, other one!
regOp = &inst->getDst(0);
// We get REX if we have a 64-bit operand or one of the register numbers involved is >=8
if (regOp->getGPRWidth() == RegWidth::eWidth64 || regOp->getBaseRegNum() >= 8 ||
memAccess->getBaseRegNum() >= 8 || memAccess->getIndexRegNum() >= 8)
return Size8b;
}
}
return SizeBelow8b;
}
bool doPair(TContext &context, const TInst *uInst, const TInst *vInst, bool ignoreBankConflicts)
{
using namespace Pipe;
using namespace OpType;
const TOp& uOp = uInst->getOp();
const TOp& vOp = vInst->getOp();
TCorePipe uPairability = uOp.getCorePipe();
TCorePipe vPairability = vOp.getCorePipe();
/* We can only pair if the U-pipe's getting something that's pairable
on U and the V-pipe's getting something that is pairable on V :) */
if ((uPairability == eUPairable || uPairability == eUVPairable) &&
(vPairability == eVPairable || vPairability == eUVPairable))
{
/* Determine which arguments access memory (if any) and how many
vector read ports are used. */
const TArg *uMem = NULL;
const TArg *vMem = NULL;
determineMem(uInst, &uMem);
determineMem(vInst, &vMem);
/* Check for bank collisions. TODO what to do with non-{0,1} probabilities? */
if (!ignoreBankConflicts &&
computeBankCollisionProbability(context, uInst, uMem, vInst, vMem) == 1.0f)
return false;
/* Neither inst can contain a disp and an immediate simultaneously */
if (hasDispImmPair(uInst, uMem) || hasDispImmPair(vInst, vMem))
return false;
/* Two 8b instructions can't pair with each other, and any inst >8b is unpairable */
tSizeClass uSize = getSizeClass(uInst, uMem);
tSizeClass vSize = getSizeClass(vInst, vMem);
if ((uSize == Size8b && vSize == Size8b) || uSize == SizeAbove8b || vSize == SizeAbove8b)
return false;
/* PUSH/POP are !pairable if their argument is either a [MEM] or the SR */
if (!isPushPopPairable(uInst)) return false;
if (!isPushPopPairable(vInst)) return false;
/* TEST REG,IMM is only pairable if REG = {AX, RAX}; [MEM],IMM is !pairable */
if (!isTestPairable(uInst)) return false;
if (!isTestPairable(vInst)) return false;
/* SHR/SAR/SHL/SAL are U-pairable with immediate count */
if (!isShiftUPairable(uInst)) return false;
/* ROR, ROL, RCR, RCL are U-pairable with an immediate count == 1 */
if (!isRotateUPairable(uInst)) return false;
/* TODO: JMP, CALL to a far location are NP */
}
else /* Well, these can't pair */
return false;
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment