Add rule to simplify lzcount followed by right shift

This commit is contained in:
Pokechu22 2021-03-04 19:34:54 -08:00
parent 14880b53c4
commit e4ab760242
3 changed files with 71 additions and 0 deletions

View file

@ -5386,6 +5386,7 @@ void ActionDatabase::universalAction(Architecture *conf)
actprop->addRule( new RulePopcountBoolXor("analysis") );
actprop->addRule( new RuleOrMultiBool("analysis") );
actprop->addRule( new RuleXorSwap("analysis") );
actprop->addRule( new RuleLzcountShiftBool("analysis") );
actprop->addRule( new RuleSubvarAnd("subvar") );
actprop->addRule( new RuleSubvarSubpiece("subvar") );
actprop->addRule( new RuleSplitFlow("subvar") );

View file

@ -10125,3 +10125,63 @@ int4 RuleXorSwap::applyOp(PcodeOp *op,Funcdata &data)
}
return 0;
}
/// \class RuleLzcountShiftBool
/// \brief Simplify equality checks that use lzcount.
///
/// Some compilers check if a value is equal to zero by checking the most
/// significant bit in lzcount; for instance on a 32-bit system,
/// it being equal to 32 would have the 5th bit set.
/// - `lzcount(a ^ 3) >> 5 => a ^ 3 == 0 => a == 3` (by RuleXorCollapse)
/// - `lzcount(a - 3) >> 5 => a - 3 == 0 => a == 3` (by RuleEqual2Zero)
void RuleLzcountShiftBool::getOpList(vector<uint4> &oplist) const
{
oplist.push_back(CPUI_LZCOUNT);
}
int4 RuleLzcountShiftBool::applyOp(PcodeOp *op,Funcdata &data)
{
Varnode *outVn = op->getOut();
list<PcodeOp *>::const_iterator iter, iter2;
uintb max_return = 8 * op->getIn(0)->getSize();
if (popcount(max_return) != 1) {
// This rule only makes sense with sizes that are powers of 2; if the maximum value
// returned by lzcount was, say, 24, then both 16 >> 4 and 24 >> 4
// are 1, and thus the check does not make sense. (Such processors couldn't
// use lzcount for checking equality in any case.)
return 0;
}
for(iter=outVn->beginDescend();iter!=outVn->endDescend();++iter) {
PcodeOp *baseOp = *iter;
if (baseOp->code() != CPUI_INT_RIGHT && baseOp->code() != CPUI_INT_SRIGHT) continue;
Varnode *vn1 = baseOp->getIn(1);
if (!vn1->isConstant()) continue;
uintb shift = vn1->getOffset();
if ((max_return >> shift) == 1) {
// Becomes a comparison with zero
PcodeOp* newOp = data.newOp(2, baseOp->getAddr());
data.opSetOpcode(newOp, CPUI_INT_EQUAL);
Varnode* b = data.newConstant(op->getIn(0)->getSize(), 0);
data.opSetInput(newOp, op->getIn(0), 0);
data.opSetInput(newOp, b, 1);
// CPUI_INT_EQUAL must produce a 1-byte boolean result
Varnode* eqResVn = data.newUniqueOut(1, newOp);
data.opSetOutput(newOp, eqResVn);
data.opInsertBefore(newOp, baseOp);
// Because the old output had size op->getIn(0)->getSize(),
// we have to guarantee that a Varnode of this size gets outputted
// to the descending PcodeOps. This is handled here with CPUI_INT_ZEXT.
data.opRemoveInput(baseOp, 1);
data.opSetOpcode(baseOp, CPUI_INT_ZEXT);
data.opSetInput(baseOp, eqResVn, 0);
return 1;
}
}
return 0;
}

View file

@ -1585,4 +1585,14 @@ public:
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
};
class RuleLzcountShiftBool : public Rule {
public:
RuleLzcountShiftBool(const string &g) : Rule( g, 0, "lzcountshiftbool") {} ///< Constructor
virtual Rule *clone(const ActionGroupList &grouplist) const {
if (!grouplist.contains(getGroup())) return (Rule *)0;
return new RuleLzcountShiftBool(getGroup());
}
virtual void getOpList(vector<uint4> &oplist) const;
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
};
#endif