mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GP-5158 Conditional comparisons in AARCH64
This commit is contained in:
parent
f6dfa0964a
commit
2511498fcf
14 changed files with 777 additions and 471 deletions
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -19,172 +19,13 @@ namespace ghidra {
|
|||
|
||||
const int4 BooleanExpressionMatch::maxDepth = 1;
|
||||
|
||||
/// \brief Test if two operations with same opcode produce complementary boolean values
|
||||
///
|
||||
/// This only tests for cases where the opcode is INT_LESS or INT_SLESS and one of the
|
||||
/// inputs is constant.
|
||||
/// \param bin1op is the first p-code op to compare
|
||||
/// \param bin2op is the second p-code op to compare
|
||||
/// \return \b true if the two operations always produce complementary values
|
||||
bool BooleanExpressionMatch::sameOpComplement(PcodeOp *bin1op,PcodeOp *bin2op)
|
||||
|
||||
{
|
||||
OpCode opcode = bin1op->code();
|
||||
if ((opcode == CPUI_INT_SLESS)||(opcode==CPUI_INT_LESS)) {
|
||||
// Basically we test for the scenario like: x < 9 8 < x
|
||||
int4 constslot = 0;
|
||||
if (bin1op->getIn(1)->isConstant())
|
||||
constslot = 1;
|
||||
if (!bin1op->getIn(constslot)->isConstant()) return false;
|
||||
if (!bin2op->getIn(1-constslot)->isConstant()) return false;
|
||||
if (!varnodeSame(bin1op->getIn(1-constslot),bin2op->getIn(constslot))) return false;
|
||||
uintb val1 = bin1op->getIn(constslot)->getOffset();
|
||||
uintb val2 = bin2op->getIn(1-constslot)->getOffset();
|
||||
if (constslot!=0) {
|
||||
uintb tmp = val2;
|
||||
val2 = val1;
|
||||
val1 = tmp;
|
||||
}
|
||||
if (val1 + 1 != val2) return false;
|
||||
if ((val2 == 0)&&(opcode==CPUI_INT_LESS)) return false; // Corner case for unsigned
|
||||
if (opcode==CPUI_INT_SLESS) { // Corner case for signed
|
||||
int4 sz = bin1op->getIn(constslot)->getSize();
|
||||
if (signbit_negative(val2,sz) && (!signbit_negative(val1,sz)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Do the given Varnodes hold the same value, possibly as constants
|
||||
///
|
||||
/// \param a is the first Varnode to compare
|
||||
/// \param b is the second Varnode
|
||||
/// \return \b true if the Varnodes (always) hold the same value
|
||||
bool BooleanExpressionMatch::varnodeSame(Varnode *a,Varnode *b)
|
||||
|
||||
{
|
||||
if (a == b) return true;
|
||||
if (a->isConstant() && b->isConstant())
|
||||
return (a->getOffset() == b->getOffset());
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Determine if two boolean Varnodes hold related values
|
||||
///
|
||||
/// The values may be the \e same, or opposite of each other (\e complementary).
|
||||
/// Otherwise the values are \e uncorrelated. The trees constructing each Varnode
|
||||
/// are examined up to a maximum \b depth. If this is exceeded \e uncorrelated is returned.
|
||||
/// \param vn1 is the first boolean Varnode
|
||||
/// \param vn2 is the second boolean Varnode
|
||||
/// \param depth is the maximum depth to traverse in the evaluation
|
||||
/// \return the correlation class
|
||||
int4 BooleanExpressionMatch::evaluate(Varnode *vn1,Varnode *vn2,int4 depth)
|
||||
|
||||
{
|
||||
if (vn1 == vn2) return same;
|
||||
PcodeOp *op1,*op2;
|
||||
OpCode opc1,opc2;
|
||||
if (vn1->isWritten()) {
|
||||
op1 = vn1->getDef();
|
||||
opc1 = op1->code();
|
||||
if (opc1 == CPUI_BOOL_NEGATE) {
|
||||
int res = evaluate(op1->getIn(0),vn2,depth);
|
||||
if (res == same) // Flip same <-> complementary result
|
||||
res = complementary;
|
||||
else if (res == complementary)
|
||||
res = same;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else {
|
||||
op1 = (PcodeOp *)0; // Don't give up before checking if op2 is BOOL_NEGATE
|
||||
opc1 = CPUI_MAX;
|
||||
}
|
||||
if (vn2->isWritten()) {
|
||||
op2 = vn2->getDef();
|
||||
opc2 = op2->code();
|
||||
if (opc2 == CPUI_BOOL_NEGATE) {
|
||||
int4 res = evaluate(vn1,op2->getIn(0),depth);
|
||||
if (res == same) // Flip same <-> complementary result
|
||||
res = complementary;
|
||||
else if (res == complementary)
|
||||
res = same;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else
|
||||
return uncorrelated;
|
||||
if (op1 == (PcodeOp *)0)
|
||||
return uncorrelated;
|
||||
if (!op1->isBoolOutput() || !op2->isBoolOutput())
|
||||
return uncorrelated;
|
||||
if (depth != 0 && (opc1 == CPUI_BOOL_AND || opc1 == CPUI_BOOL_OR || opc1 == CPUI_BOOL_XOR)) {
|
||||
if (opc2 == CPUI_BOOL_AND || opc2 == CPUI_BOOL_OR || opc2 == CPUI_BOOL_XOR) {
|
||||
if (opc1 == opc2 || (opc1 == CPUI_BOOL_AND && opc2 == CPUI_BOOL_OR) || (opc1 == CPUI_BOOL_OR && opc2 == CPUI_BOOL_AND)) {
|
||||
int4 pair1 = evaluate(op1->getIn(0),op2->getIn(0),depth-1);
|
||||
int4 pair2;
|
||||
if (pair1 == uncorrelated) {
|
||||
pair1 = evaluate(op1->getIn(0),op2->getIn(1),depth-1); // Try other possible pairing (commutative op)
|
||||
if (pair1 == uncorrelated)
|
||||
return uncorrelated;
|
||||
pair2 = evaluate(op1->getIn(1),op2->getIn(0),depth-1);
|
||||
}
|
||||
else {
|
||||
pair2 = evaluate(op1->getIn(1),op2->getIn(1),depth-1);
|
||||
}
|
||||
if (pair2 == uncorrelated)
|
||||
return uncorrelated;
|
||||
if (opc1 == opc2) {
|
||||
if (pair1 == same && pair2 == same)
|
||||
return same;
|
||||
else if (opc1 == CPUI_BOOL_XOR) {
|
||||
if (pair1 == complementary && pair2 == complementary)
|
||||
return same;
|
||||
return complementary;
|
||||
}
|
||||
}
|
||||
else { // Must be CPUI_BOOL_AND and CPUI_BOOL_OR
|
||||
if (pair1 == complementary && pair2 == complementary)
|
||||
return complementary; // De Morgan's Law
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Two boolean output ops, compare them directly
|
||||
if (opc1 == opc2) {
|
||||
if (varnodeSame(op1->getIn(0),op2->getIn(0)) && varnodeSame(op1->getIn(1),op2->getIn(1)))
|
||||
return same;
|
||||
if (sameOpComplement(op1,op2)) {
|
||||
return complementary;
|
||||
}
|
||||
return uncorrelated;
|
||||
}
|
||||
// Check if the binary ops are complements of one another
|
||||
int4 slot1 = 0;
|
||||
int4 slot2 = 0;
|
||||
bool reorder;
|
||||
if (opc1 != get_booleanflip(opc2,reorder))
|
||||
return uncorrelated;
|
||||
if (reorder) slot2 = 1;
|
||||
if (!varnodeSame(op1->getIn(slot1),op2->getIn(slot2)))
|
||||
return uncorrelated;
|
||||
if (!varnodeSame(op1->getIn(1-slot1),op2->getIn(1-slot2)))
|
||||
return uncorrelated;
|
||||
return complementary;
|
||||
}
|
||||
return uncorrelated;
|
||||
}
|
||||
|
||||
bool BooleanExpressionMatch::verifyCondition(PcodeOp *op, PcodeOp *iop)
|
||||
|
||||
{
|
||||
int4 res = evaluate(op->getIn(1), iop->getIn(1), maxDepth);
|
||||
if (res == uncorrelated)
|
||||
int4 res = BooleanMatch::evaluate(op->getIn(1), iop->getIn(1), maxDepth);
|
||||
if (res == BooleanMatch::uncorrelated)
|
||||
return false;
|
||||
matchflip = (res == complementary);
|
||||
matchflip = (res == BooleanMatch::complementary);
|
||||
if (op->isBooleanFlip())
|
||||
matchflip = !matchflip;
|
||||
if (iop->isBooleanFlip())
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -26,21 +26,9 @@ namespace ghidra {
|
|||
///
|
||||
/// This class determines if two CBRANCHs share the same condition. It also determines if the conditions
|
||||
/// are complements of each other, and/or they are shared along only one path.
|
||||
///
|
||||
/// Traverse (upto a specific depth) the two boolean expressions consisting of BOOL_AND, BOOL_OR, and
|
||||
/// BOOL_XOR operations. Leaf operators in the expression can be other operators with boolean output (INT_LESS,
|
||||
/// INT_SLESS, etc.).
|
||||
class BooleanExpressionMatch {
|
||||
enum {
|
||||
same = 1, ///< Pair always hold the same value
|
||||
complementary = 2, ///< Pair always hold complementary values
|
||||
uncorrelated = 3 ///< Pair values are uncorrelated
|
||||
};
|
||||
static const int4 maxDepth; ///< Maximum depth to trace a boolean expression
|
||||
bool matchflip; ///< True if the compared CBRANCH keys on the opposite boolean value of the root
|
||||
static bool sameOpComplement(PcodeOp *bin1op, PcodeOp *bin2op);
|
||||
static bool varnodeSame(Varnode *a,Varnode *b);
|
||||
static int4 evaluate(Varnode *vn1,Varnode *vn2,int4 depth);
|
||||
public:
|
||||
bool verifyCondition(PcodeOp *op, PcodeOp *iop); ///< Perform the correlation test on two CBRANCH operations
|
||||
int4 getMultiSlot(void) const { return -1; } ///< Get the MULTIEQUAL slot in the critical path
|
||||
|
|
|
@ -5472,6 +5472,8 @@ void ActionDatabase::universalAction(Architecture *conf)
|
|||
actprop->addRule( new RuleSlessToLess("analysis") );
|
||||
actprop->addRule( new RuleZextSless("analysis") );
|
||||
actprop->addRule( new RuleBitUndistribute("analysis") );
|
||||
actprop->addRule( new RuleBooleanUndistribute("analysis") );
|
||||
actprop->addRule( new RuleBooleanDedup("analysis") );
|
||||
actprop->addRule( new RuleBoolZext("analysis") );
|
||||
actprop->addRule( new RuleBooleanNegate("analysis") );
|
||||
actprop->addRule( new RuleLogic2Bool("analysis") );
|
||||
|
|
|
@ -123,10 +123,6 @@ class Funcdata {
|
|||
void switchOverJumpTables(const FlowInfo &flow); ///< Convert jump-table addresses to basic block indices
|
||||
void clearJumpTables(void); ///< Clear any jump-table information
|
||||
BlockBasic *nodeSplitBlockEdge(BlockBasic *b,int4 inedge);
|
||||
PcodeOp *nodeSplitCloneOp(PcodeOp *op);
|
||||
void nodeSplitCloneVarnode(PcodeOp *op,PcodeOp *newop);
|
||||
void nodeSplitRawDuplicate(BlockBasic *b,BlockBasic *bprime);
|
||||
void nodeSplitInputPatch(BlockBasic *b,BlockBasic *bprime,int4 inedge);
|
||||
|
||||
void sortCallSpecs(void); ///< Sort calls using a dominance based order
|
||||
void deleteCallSpecs(PcodeOp *op); ///< Remove the specification for a particular call
|
||||
|
@ -487,6 +483,7 @@ public:
|
|||
Varnode *createStackRef(AddrSpace *spc,uintb off,PcodeOp *op,Varnode *stackptr,bool insertafter);
|
||||
Varnode *opStackLoad(AddrSpace *spc,uintb off,uint4 sz,PcodeOp *op,Varnode *stackptr,bool insertafter);
|
||||
PcodeOp *opStackStore(AddrSpace *spc,uintb off,PcodeOp *op,bool insertafter);
|
||||
Varnode *opBoolNegate(Varnode *vn,PcodeOp *op,bool insertafter);
|
||||
void opUndoPtradd(PcodeOp *op,bool finalize); ///< Convert a CPUI_PTRADD back into a CPUI_INT_ADD
|
||||
static int4 opFlipInPlaceTest(PcodeOp *op,vector<PcodeOp *> &fliplist);
|
||||
void opFlipInPlaceExecute(vector<PcodeOp *> &fliplist);
|
||||
|
@ -618,6 +615,29 @@ public:
|
|||
void setFuncdata(Funcdata *f) { fd = f; } ///< Establish the container for \b this emitter
|
||||
};
|
||||
|
||||
/// \brief Control the cloning of PcodeOps from within a basic block into another block
|
||||
///
|
||||
/// Used for splitting control-flow at a merge point. Can duplicate either a whole basic block, or an expression
|
||||
/// subset within a basic block.
|
||||
class CloneBlockOps {
|
||||
/// \brief Helper class for pairing a p-code op with its clone
|
||||
struct ClonePair {
|
||||
PcodeOp *cloneOp; ///< New cloned op
|
||||
PcodeOp *origOp; ///< Original op that was cloned
|
||||
ClonePair(PcodeOp *c,PcodeOp *o) { cloneOp = c; origOp = o; } ///< Constructor
|
||||
};
|
||||
Funcdata &data;
|
||||
vector<ClonePair> cloneList; ///< List of cloned ops
|
||||
map<PcodeOp *,PcodeOp *> origToClone; ///< Map from original p-code op to its clone
|
||||
PcodeOp *buildOpClone(PcodeOp *op); ///< Produce a skeleton copy of the given PcodeOp
|
||||
void buildVarnodeOutput(PcodeOp *origOp,PcodeOp *cloneOp); ///< Clone the output Varnode of the given op onto its clone
|
||||
void patchInputs(int4 inedge); ///< Set the input Varnodes of all cloned ops
|
||||
public:
|
||||
CloneBlockOps(Funcdata &fd) : data(fd) {} ///< Constructor
|
||||
void cloneBlock(BlockBasic *b,BlockBasic *bprime,int4 inedge); ///< Clone all p-code ops from a block into its copy
|
||||
Varnode *cloneExpression(vector<PcodeOp *> &ops,PcodeOp *followOp); ///< Clone p-code ops in an expression
|
||||
};
|
||||
|
||||
/// \brief Helper class for determining if Varnodes can trace their value from a legitimate source
|
||||
///
|
||||
/// Try to determine if a Varnode (expressed as a particular input to a CALL, CALLIND, or RETURN op)
|
||||
|
|
|
@ -833,150 +833,6 @@ BlockBasic *Funcdata::nodeSplitBlockEdge(BlockBasic *b,int4 inedge)
|
|||
return bprime;
|
||||
}
|
||||
|
||||
/// \brief Duplicate the given PcodeOp as part of splitting a block
|
||||
///
|
||||
/// Make a basic clone of the p-code op copying its basic control-flow properties
|
||||
/// \param op is the given PcodeOp
|
||||
/// \return the cloned op
|
||||
PcodeOp *Funcdata::nodeSplitCloneOp(PcodeOp *op)
|
||||
|
||||
{
|
||||
PcodeOp *dup;
|
||||
|
||||
if (op->isBranch()) {
|
||||
if (op->code() != CPUI_BRANCH)
|
||||
throw LowlevelError("Cannot duplicate 2-way or n-way branch in nodeplit");
|
||||
return (PcodeOp *)0;
|
||||
}
|
||||
dup = newOp(op->numInput(),op->getAddr());
|
||||
opSetOpcode(dup,op->code());
|
||||
uint4 fl = op->flags & (PcodeOp::startbasic | PcodeOp::nocollapse |
|
||||
PcodeOp::startmark);
|
||||
dup->setFlag(fl);
|
||||
return dup;
|
||||
}
|
||||
|
||||
/// \brief Duplicate output Varnode of the given p-code op, as part of splitting a block
|
||||
///
|
||||
/// Make a basic clone of the Varnode and its basic flags. The clone is created
|
||||
/// as an output of a previously cloned PcodeOp.
|
||||
/// \param op is the given op whose output should be cloned
|
||||
/// \param newop is the cloned version
|
||||
void Funcdata::nodeSplitCloneVarnode(PcodeOp *op,PcodeOp *newop)
|
||||
|
||||
{
|
||||
Varnode *opvn = op->getOut();
|
||||
Varnode *newvn;
|
||||
|
||||
if (opvn == (Varnode *)0) return;
|
||||
newvn = newVarnodeOut(opvn->getSize(),opvn->getAddr(),newop);
|
||||
uint4 vflags = opvn->getFlags();
|
||||
vflags &= (Varnode::externref | Varnode::volatil | Varnode::incidental_copy |
|
||||
Varnode::readonly | Varnode::persist |
|
||||
Varnode::addrtied | Varnode::addrforce);
|
||||
newvn->setFlags(vflags);
|
||||
}
|
||||
|
||||
/// \brief Clone all p-code ops from a block into its copy
|
||||
///
|
||||
/// P-code in a basic block is cloned into the split version of the block.
|
||||
/// Only the output Varnodes are cloned, not the inputs.
|
||||
/// \param b is the original basic block
|
||||
/// \param bprime is the cloned block
|
||||
void Funcdata::nodeSplitRawDuplicate(BlockBasic *b,BlockBasic *bprime)
|
||||
|
||||
{
|
||||
PcodeOp *b_op,*prime_op;
|
||||
list<PcodeOp *>::iterator iter;
|
||||
|
||||
for(iter=b->beginOp();iter!=b->endOp();++iter) {
|
||||
b_op = *iter;
|
||||
prime_op = nodeSplitCloneOp(b_op);
|
||||
if (prime_op == (PcodeOp *)0) continue;
|
||||
nodeSplitCloneVarnode(b_op,prime_op);
|
||||
opInsertEnd(prime_op,bprime);
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Patch Varnode inputs to p-code ops in split basic block
|
||||
///
|
||||
/// Map Varnodes that are inputs for PcodeOps in the original basic block to the
|
||||
/// input slots of the cloned ops in the split block. Constants and code ref Varnodes
|
||||
/// need to be duplicated, other Varnodes are shared between the ops. This routine
|
||||
/// also pulls an input Varnode out of riginal MULTIEQUAL ops and adds it back
|
||||
/// to the cloned MULTIEQUAL ops.
|
||||
/// \param b is the original basic block
|
||||
/// \param bprime is the split clone of the block
|
||||
/// \param inedge is the incoming edge index that was split on
|
||||
void Funcdata::nodeSplitInputPatch(BlockBasic *b,BlockBasic *bprime,int4 inedge)
|
||||
|
||||
{
|
||||
list<PcodeOp *>::iterator biter,piter;
|
||||
PcodeOp *bop,*pop;
|
||||
Varnode *bvn,*pvn;
|
||||
map<PcodeOp *,PcodeOp *> btop; // Map from b to bprime
|
||||
vector<PcodeOp *> pind; // pop needing b input
|
||||
vector<PcodeOp *> bind; // bop giving input
|
||||
vector<int4> pslot; // slot within pop needing b input
|
||||
|
||||
biter = b->beginOp();
|
||||
piter = bprime->beginOp();
|
||||
|
||||
while(piter != bprime->endOp()) {
|
||||
bop = *biter;
|
||||
pop = *piter;
|
||||
btop[bop] = pop; // Establish mapping
|
||||
if (bop->code() == CPUI_MULTIEQUAL) {
|
||||
pop->setNumInputs(1); // One edge now goes into bprime
|
||||
opSetOpcode(pop,CPUI_COPY);
|
||||
opSetInput(pop,bop->getIn(inedge),0);
|
||||
opRemoveInput(bop,inedge); // One edge is removed from b
|
||||
if (bop->numInput() == 1)
|
||||
opSetOpcode(bop,CPUI_COPY);
|
||||
}
|
||||
else if (bop->code() == CPUI_INDIRECT) {
|
||||
throw LowlevelError("Can't handle INDIRECTs in nodesplit");
|
||||
}
|
||||
else if (bop->isCall()) {
|
||||
throw LowlevelError("Can't handle CALLs in nodesplit");
|
||||
}
|
||||
else {
|
||||
for(int4 i=0;i<pop->numInput();++i) {
|
||||
bvn = bop->getIn(i);
|
||||
if (bvn->isConstant())
|
||||
pvn = newConstant(bvn->getSize(),bvn->getOffset());
|
||||
else if (bvn->isAnnotation())
|
||||
pvn = newCodeRef(bvn->getAddr());
|
||||
else if (bvn->isFree())
|
||||
throw LowlevelError("Can't handle free varnode in nodesplit");
|
||||
else {
|
||||
if (bvn->isWritten()) {
|
||||
if (bvn->getDef()->getParent() == b) {
|
||||
pind.push_back(pop); // Need a cross reference
|
||||
bind.push_back(bvn->getDef());
|
||||
pslot.push_back(i);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
pvn = bvn;
|
||||
}
|
||||
else
|
||||
pvn = bvn;
|
||||
}
|
||||
opSetInput(pop,pvn,i);
|
||||
}
|
||||
}
|
||||
++piter;
|
||||
++biter;
|
||||
}
|
||||
|
||||
for(int4 i=0;i<pind.size();++i) {
|
||||
pop = pind[i];
|
||||
PcodeOp *cross = btop[bind[i]];
|
||||
opSetInput(pop,cross->getOut(),pslot[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Split control-flow into a basic block, duplicating its p-code into a new block
|
||||
///
|
||||
/// P-code is duplicated into another block, and control-flow is modified so that the new
|
||||
|
@ -1000,10 +856,8 @@ void Funcdata::nodeSplit(BlockBasic *b,int4 inedge)
|
|||
|
||||
// Create duplicate block
|
||||
BlockBasic *bprime = nodeSplitBlockEdge(b,inedge);
|
||||
// Make copy of b's ops
|
||||
nodeSplitRawDuplicate(b,bprime);
|
||||
// Patch up inputs based on split
|
||||
nodeSplitInputPatch(b,bprime,inedge);
|
||||
CloneBlockOps cloner(*this);
|
||||
cloner.cloneBlock(b, bprime, inedge); // Copy b's ops into bprime
|
||||
|
||||
// We would need to patch outputs here for the more general
|
||||
// case when b has out edges
|
||||
|
@ -1087,4 +941,152 @@ void Funcdata::spliceBlockBasic(BlockBasic *bl)
|
|||
structureReset();
|
||||
}
|
||||
|
||||
/// Make a basic clone of the p-code op copying its basic control-flow properties.
|
||||
/// In the case of a \e branch, the p-code op is not cloned and null is returned.
|
||||
/// \param op is the given PcodeOp
|
||||
/// \return the cloned op or null
|
||||
PcodeOp *CloneBlockOps::buildOpClone(PcodeOp *op)
|
||||
|
||||
{
|
||||
PcodeOp *dup;
|
||||
|
||||
if (op->isBranch()) {
|
||||
if (op->code() != CPUI_BRANCH)
|
||||
throw LowlevelError("Cannot duplicate 2-way or n-way branch in nodeplit");
|
||||
return (PcodeOp *)0;
|
||||
}
|
||||
dup = data.newOp(op->numInput(),op->getAddr());
|
||||
data.opSetOpcode(dup,op->code());
|
||||
uint4 fl = op->flags & (PcodeOp::startbasic | PcodeOp::nocollapse | PcodeOp::startmark |
|
||||
PcodeOp::nonprinting | PcodeOp::halt | PcodeOp::badinstruction | PcodeOp::unimplemented |
|
||||
PcodeOp::noreturn | PcodeOp::missing | PcodeOp::indirect_creation | PcodeOp::indirect_store |
|
||||
PcodeOp::no_indirect_collapse | PcodeOp::calculated_bool | PcodeOp::ptrflow);
|
||||
dup->setFlag(fl);
|
||||
fl = op->addlflags & (PcodeOp::special_prop | PcodeOp::special_print | PcodeOp::incidental_copy |
|
||||
PcodeOp::is_cpool_transformed | PcodeOp::stop_type_propagation | PcodeOp::store_unmapped);
|
||||
dup->setAdditionalFlag(fl);
|
||||
|
||||
cloneList.emplace_back(dup,op); // Map from clone to orig
|
||||
origToClone[op] = dup; // Map from orig to clone
|
||||
return dup;
|
||||
}
|
||||
|
||||
/// Make a basic clone of the Varnode and its flags. The clone is created
|
||||
/// as an output of a previously cloned PcodeOp.
|
||||
/// \param op is the given op whose output should be cloned
|
||||
/// \param newop is the cloned version
|
||||
void CloneBlockOps::buildVarnodeOutput(PcodeOp *origOp,PcodeOp *cloneOp)
|
||||
|
||||
{
|
||||
Varnode *opvn = origOp->getOut();
|
||||
Varnode *newvn;
|
||||
|
||||
if (opvn == (Varnode *)0) return;
|
||||
newvn = data.newVarnodeOut(opvn->getSize(),opvn->getAddr(),cloneOp);
|
||||
uint4 vflags = opvn->getFlags();
|
||||
vflags &= (Varnode::externref | Varnode::volatil | Varnode::incidental_copy | Varnode::readonly |
|
||||
Varnode::persist | Varnode::addrtied | Varnode::addrforce | Varnode::nolocalalias | Varnode::spacebase |
|
||||
Varnode::indirect_creation | Varnode::return_address | Varnode::precislo | Varnode::precishi |
|
||||
Varnode::incidental_copy);
|
||||
newvn->setFlags(vflags);
|
||||
uint2 aflags = opvn->addlflags;
|
||||
aflags &= (Varnode::writemask | Varnode::ptrflow | Varnode::stack_store);
|
||||
newvn->addlflags |= aflags;
|
||||
}
|
||||
|
||||
/// P-code in a basic block is cloned into the split version of the block.
|
||||
/// \param b is the original basic block
|
||||
/// \param bprime is the cloned block
|
||||
/// \param inedge is the incoming edge index that was split on
|
||||
void CloneBlockOps::cloneBlock(BlockBasic *b,BlockBasic *bprime,int4 inedge)
|
||||
|
||||
{
|
||||
PcodeOp *origOp,*cloneOp;
|
||||
list<PcodeOp *>::iterator iter;
|
||||
|
||||
for(iter=b->beginOp();iter!=b->endOp();++iter) {
|
||||
origOp = *iter;
|
||||
cloneOp = buildOpClone(origOp);
|
||||
if (cloneOp == (PcodeOp *)0) continue;
|
||||
buildVarnodeOutput(origOp,cloneOp);
|
||||
data.opInsertEnd(cloneOp,bprime);
|
||||
}
|
||||
patchInputs(inedge);
|
||||
}
|
||||
|
||||
/// P-code in the list is cloned right before the given \b followOp.
|
||||
/// \param ops is the list of ops to clone
|
||||
/// \param followOp is the point where the cloned ops are inserted
|
||||
/// \return the output Varnode of the last cloned op
|
||||
Varnode *CloneBlockOps::cloneExpression(vector<PcodeOp *> &ops,PcodeOp *followOp)
|
||||
|
||||
{
|
||||
PcodeOp *origOp,*cloneOp;
|
||||
for(int4 i=0;i<ops.size();++i) {
|
||||
origOp = ops[i];
|
||||
cloneOp = buildOpClone(origOp);
|
||||
if (cloneOp == (PcodeOp *)0) continue;
|
||||
buildVarnodeOutput(origOp,cloneOp);
|
||||
data.opInsertBefore(cloneOp, followOp);
|
||||
}
|
||||
if (cloneList.empty())
|
||||
throw LowlevelError("No expression to clone");
|
||||
patchInputs(0);
|
||||
cloneOp = cloneList.back().cloneOp;
|
||||
return cloneOp->getOut();
|
||||
}
|
||||
|
||||
/// Map Varnodes that are inputs for PcodeOps in the original basic block to the input slots of
|
||||
/// the cloned ops. Constants and code ref Varnodes need to be duplicated, other Varnodes are shared
|
||||
/// between the ops. This routine also pulls an input Varnode out of original MULTIEQUAL ops and adds
|
||||
/// it back to the cloned MULTIEQUAL ops.
|
||||
/// \param inedge is the incoming edge index that was split on
|
||||
void CloneBlockOps::patchInputs(int4 inedge)
|
||||
|
||||
{
|
||||
for(int4 pos=0;pos<cloneList.size();++pos) {
|
||||
PcodeOp *origOp = cloneList[pos].origOp;
|
||||
PcodeOp *cloneOp = cloneList[pos].cloneOp;
|
||||
if (origOp->code() == CPUI_MULTIEQUAL) {
|
||||
cloneOp->setNumInputs(1); // One edge now goes into the new block
|
||||
data.opSetOpcode(cloneOp,CPUI_COPY);
|
||||
data.opSetInput(cloneOp,origOp->getIn(inedge),0);
|
||||
data.opRemoveInput(origOp,inedge); // One edge is removed from original block
|
||||
if (origOp->numInput() == 1)
|
||||
data.opSetOpcode(origOp,CPUI_COPY);
|
||||
}
|
||||
else if (origOp->code() == CPUI_INDIRECT) {
|
||||
throw LowlevelError("Can't clone INDIRECTs");
|
||||
}
|
||||
else if (origOp->isCall()) {
|
||||
throw LowlevelError("Can't clone CALLs");
|
||||
}
|
||||
else {
|
||||
for(int4 i=0;i<cloneOp->numInput();++i) {
|
||||
Varnode *origVn = origOp->getIn(i);
|
||||
Varnode *cloneVn;
|
||||
if (origVn->isConstant())
|
||||
cloneVn = origVn;
|
||||
else if (origVn->isAnnotation())
|
||||
cloneVn = data.newCodeRef(origVn->getAddr());
|
||||
else if (origVn->isFree())
|
||||
throw LowlevelError("Can't clone free varnode");
|
||||
else {
|
||||
if (origVn->isWritten()) {
|
||||
map<PcodeOp *,PcodeOp *>::const_iterator iter = origToClone.find(origVn->getDef());
|
||||
if (iter != origToClone.end()) {
|
||||
cloneVn = (*iter).second->getOut();
|
||||
}
|
||||
else
|
||||
cloneVn = origVn;
|
||||
}
|
||||
else
|
||||
cloneVn = origVn;
|
||||
}
|
||||
data.opSetInput(cloneOp,cloneVn,i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End namespace ghidra
|
||||
|
|
|
@ -526,6 +526,25 @@ Varnode *Funcdata::opStackLoad(AddrSpace *spc,uintb off,uint4 sz,PcodeOp *op,Var
|
|||
return res;
|
||||
}
|
||||
|
||||
/// \brief Construct the boolean negation of a given boolean Varnode into a temporary register
|
||||
///
|
||||
/// \param vn is the given Varnode
|
||||
/// \param op is the point at which to insert the BOOL_NEGATE op
|
||||
/// \return the result Varnode
|
||||
Varnode *Funcdata::opBoolNegate(Varnode *vn,PcodeOp *op,bool insertafter)
|
||||
|
||||
{
|
||||
PcodeOp *negateop = newOp(1,op->getAddr());
|
||||
opSetOpcode(negateop,CPUI_BOOL_NEGATE);
|
||||
Varnode *resvn = newUniqueOut(1,negateop);
|
||||
opSetInput(negateop,vn,0);
|
||||
if (insertafter)
|
||||
opInsertAfter(negateop,op);
|
||||
else
|
||||
opInsertBefore(negateop,op);
|
||||
return resvn;
|
||||
}
|
||||
|
||||
/// Convert the given CPUI_PTRADD into the equivalent CPUI_INT_ADD. This may involve inserting a
|
||||
/// CPUI_INT_MULT PcodeOp. If finalization is requested and a new PcodeOp is needed, the output
|
||||
/// Varnode is marked as \e implicit and has its data-type set
|
||||
|
|
|
@ -1340,4 +1340,163 @@ bool functionalDifference(Varnode *vn1,Varnode *vn2,int4 depth)
|
|||
return false;
|
||||
}
|
||||
|
||||
/// \brief Test if two operations with same opcode produce complementary boolean values
|
||||
///
|
||||
/// This only tests for cases where the opcode is INT_LESS or INT_SLESS and one of the
|
||||
/// inputs is constant.
|
||||
/// \param bin1op is the first p-code op to compare
|
||||
/// \param bin2op is the second p-code op to compare
|
||||
/// \return \b true if the two operations always produce complementary values
|
||||
bool BooleanMatch::sameOpComplement(PcodeOp *bin1op,PcodeOp *bin2op)
|
||||
|
||||
{
|
||||
OpCode opcode = bin1op->code();
|
||||
if ((opcode == CPUI_INT_SLESS)||(opcode==CPUI_INT_LESS)) {
|
||||
// Basically we test for the scenario like: x < 9 8 < x
|
||||
int4 constslot = 0;
|
||||
if (bin1op->getIn(1)->isConstant())
|
||||
constslot = 1;
|
||||
if (!bin1op->getIn(constslot)->isConstant()) return false;
|
||||
if (!bin2op->getIn(1-constslot)->isConstant()) return false;
|
||||
if (!varnodeSame(bin1op->getIn(1-constslot),bin2op->getIn(constslot))) return false;
|
||||
uintb val1 = bin1op->getIn(constslot)->getOffset();
|
||||
uintb val2 = bin2op->getIn(1-constslot)->getOffset();
|
||||
if (constslot!=0) {
|
||||
uintb tmp = val2;
|
||||
val2 = val1;
|
||||
val1 = tmp;
|
||||
}
|
||||
if (val1 + 1 != val2) return false;
|
||||
if ((val2 == 0)&&(opcode==CPUI_INT_LESS)) return false; // Corner case for unsigned
|
||||
if (opcode==CPUI_INT_SLESS) { // Corner case for signed
|
||||
int4 sz = bin1op->getIn(constslot)->getSize();
|
||||
if (signbit_negative(val2,sz) && (!signbit_negative(val1,sz)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Do the given Varnodes hold the same value, possibly as constants
|
||||
///
|
||||
/// \param a is the first Varnode to compare
|
||||
/// \param b is the second Varnode
|
||||
/// \return \b true if the Varnodes (always) hold the same value
|
||||
bool BooleanMatch::varnodeSame(Varnode *a,Varnode *b)
|
||||
|
||||
{
|
||||
if (a == b) return true;
|
||||
if (a->isConstant() && b->isConstant())
|
||||
return (a->getOffset() == b->getOffset());
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Determine if two boolean Varnodes hold related values
|
||||
///
|
||||
/// The values may be the \e same, or opposite of each other (\e complementary).
|
||||
/// Otherwise the values are \e uncorrelated. The trees constructing each Varnode
|
||||
/// are examined up to a maximum \b depth. If this is exceeded \e uncorrelated is returned.
|
||||
/// \param vn1 is the first boolean Varnode
|
||||
/// \param vn2 is the second boolean Varnode
|
||||
/// \param depth is the maximum depth to traverse in the evaluation
|
||||
/// \return the correlation class
|
||||
int4 BooleanMatch::evaluate(Varnode *vn1,Varnode *vn2,int4 depth)
|
||||
|
||||
{
|
||||
if (vn1 == vn2) return same;
|
||||
PcodeOp *op1,*op2;
|
||||
OpCode opc1,opc2;
|
||||
if (vn1->isWritten()) {
|
||||
op1 = vn1->getDef();
|
||||
opc1 = op1->code();
|
||||
if (opc1 == CPUI_BOOL_NEGATE) {
|
||||
int res = evaluate(op1->getIn(0),vn2,depth);
|
||||
if (res == same) // Flip same <-> complementary result
|
||||
res = complementary;
|
||||
else if (res == complementary)
|
||||
res = same;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else {
|
||||
op1 = (PcodeOp *)0; // Don't give up before checking if op2 is BOOL_NEGATE
|
||||
opc1 = CPUI_MAX;
|
||||
}
|
||||
if (vn2->isWritten()) {
|
||||
op2 = vn2->getDef();
|
||||
opc2 = op2->code();
|
||||
if (opc2 == CPUI_BOOL_NEGATE) {
|
||||
int4 res = evaluate(vn1,op2->getIn(0),depth);
|
||||
if (res == same) // Flip same <-> complementary result
|
||||
res = complementary;
|
||||
else if (res == complementary)
|
||||
res = same;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else
|
||||
return uncorrelated;
|
||||
if (op1 == (PcodeOp *)0)
|
||||
return uncorrelated;
|
||||
if (!op1->isBoolOutput() || !op2->isBoolOutput())
|
||||
return uncorrelated;
|
||||
if (depth != 0 && (opc1 == CPUI_BOOL_AND || opc1 == CPUI_BOOL_OR || opc1 == CPUI_BOOL_XOR)) {
|
||||
if (opc2 == CPUI_BOOL_AND || opc2 == CPUI_BOOL_OR || opc2 == CPUI_BOOL_XOR) {
|
||||
if (opc1 == opc2 || (opc1 == CPUI_BOOL_AND && opc2 == CPUI_BOOL_OR) || (opc1 == CPUI_BOOL_OR && opc2 == CPUI_BOOL_AND)) {
|
||||
int4 pair1 = evaluate(op1->getIn(0),op2->getIn(0),depth-1);
|
||||
int4 pair2;
|
||||
if (pair1 == uncorrelated) {
|
||||
pair1 = evaluate(op1->getIn(0),op2->getIn(1),depth-1); // Try other possible pairing (commutative op)
|
||||
if (pair1 == uncorrelated)
|
||||
return uncorrelated;
|
||||
pair2 = evaluate(op1->getIn(1),op2->getIn(0),depth-1);
|
||||
}
|
||||
else {
|
||||
pair2 = evaluate(op1->getIn(1),op2->getIn(1),depth-1);
|
||||
}
|
||||
if (pair2 == uncorrelated)
|
||||
return uncorrelated;
|
||||
if (opc1 == opc2) {
|
||||
if (pair1 == same && pair2 == same)
|
||||
return same;
|
||||
else if (opc1 == CPUI_BOOL_XOR) {
|
||||
if (pair1 == complementary && pair2 == complementary)
|
||||
return same;
|
||||
return complementary;
|
||||
}
|
||||
}
|
||||
else { // Must be CPUI_BOOL_AND and CPUI_BOOL_OR
|
||||
if (pair1 == complementary && pair2 == complementary)
|
||||
return complementary; // De Morgan's Law
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Two boolean output ops, compare them directly
|
||||
if (opc1 == opc2) {
|
||||
if (varnodeSame(op1->getIn(0),op2->getIn(0)) && varnodeSame(op1->getIn(1),op2->getIn(1)))
|
||||
return same;
|
||||
if (sameOpComplement(op1,op2)) {
|
||||
return complementary;
|
||||
}
|
||||
return uncorrelated;
|
||||
}
|
||||
// Check if the binary ops are complements of one another
|
||||
int4 slot1 = 0;
|
||||
int4 slot2 = 0;
|
||||
bool reorder;
|
||||
if (opc1 != get_booleanflip(opc2,reorder))
|
||||
return uncorrelated;
|
||||
if (reorder) slot2 = 1;
|
||||
if (!varnodeSame(op1->getIn(slot1),op2->getIn(slot2)))
|
||||
return uncorrelated;
|
||||
if (!varnodeSame(op1->getIn(1-slot1),op2->getIn(1-slot2)))
|
||||
return uncorrelated;
|
||||
return complementary;
|
||||
}
|
||||
return uncorrelated;
|
||||
}
|
||||
|
||||
} // End namespace ghidra
|
||||
|
|
|
@ -63,6 +63,7 @@ public:
|
|||
class PcodeOp {
|
||||
friend class BlockBasic; // Just insert_before, insert_after, setOrder
|
||||
friend class Funcdata;
|
||||
friend class CloneBlockOps;
|
||||
friend class PcodeOpBank;
|
||||
friend class VarnodeBank; // Only uses setInput
|
||||
public:
|
||||
|
@ -365,6 +366,23 @@ extern int4 functionalEqualityLevel(Varnode *vn1,Varnode *vn2,Varnode **res1,Var
|
|||
extern bool functionalEquality(Varnode *vn1,Varnode *vn2);
|
||||
extern bool functionalDifference(Varnode *vn1,Varnode *vn2,int4 depth);
|
||||
|
||||
/// \brief Static methods for determining if two boolean expressions are the \b same or \b complementary
|
||||
///
|
||||
/// Traverse (upto a specific depth) the two boolean expressions consisting of BOOL_AND, BOOL_OR, and
|
||||
/// BOOL_XOR operations. Leaf operators in the expression can be other operators with boolean output (INT_LESS,
|
||||
/// INT_SLESS, etc.).
|
||||
class BooleanMatch {
|
||||
static bool sameOpComplement(PcodeOp *bin1op, PcodeOp *bin2op);
|
||||
static bool varnodeSame(Varnode *a,Varnode *b);
|
||||
public:
|
||||
enum {
|
||||
same = 1, ///< Pair always hold the same value
|
||||
complementary = 2, ///< Pair always hold complementary values
|
||||
uncorrelated = 3 ///< Pair values are uncorrelated
|
||||
};
|
||||
static int4 evaluate(Varnode *vn1,Varnode *vn2,int4 depth);
|
||||
};
|
||||
|
||||
/// Compare PcodeOps (as pointers) first, then slot
|
||||
/// \param op2 is the other edge to compare with \b this
|
||||
/// \return true if \b this should come before the other PcodeOp
|
||||
|
|
|
@ -2667,6 +2667,256 @@ int4 RuleBitUndistribute::applyOp(PcodeOp *op,Funcdata &data)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/// \class RuleBooleanUndistribute
|
||||
/// \brief Undo distributed BOOL_AND through INT_NOTEQUAL
|
||||
///
|
||||
/// - `A && B != A && C => A && (B != C)`
|
||||
/// - `A || B == A || C => A || (B == C)`
|
||||
/// - `A && B == A && C => !A || (B == C)`
|
||||
void RuleBooleanUndistribute::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_INT_EQUAL);
|
||||
oplist.push_back(CPUI_INT_NOTEQUAL);
|
||||
}
|
||||
|
||||
/// \brief Test if the two given Varnodes are matching boolean expressions
|
||||
///
|
||||
/// If the expressions are complementary, \b true is still returned, but the boolean parameter
|
||||
/// is flipped.
|
||||
/// \param leftVn is the first given expression to match
|
||||
/// \param rightVn is the second given expression to match
|
||||
/// \param rightFlip is flipped if the expressions are complementary
|
||||
/// \return \b true if the expressions match
|
||||
bool RuleBooleanUndistribute::isMatch(Varnode *leftVn,Varnode *rightVn,bool &rightFlip)
|
||||
|
||||
{
|
||||
int4 val = BooleanMatch::evaluate(leftVn,rightVn,1);
|
||||
if (val == BooleanMatch::same)
|
||||
return true;
|
||||
if (val == BooleanMatch::complementary) {
|
||||
rightFlip = !rightFlip;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int4 RuleBooleanUndistribute::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Varnode *vn0 = op->getIn(0);
|
||||
if (!vn0->isWritten()) return 0;
|
||||
Varnode *vn1 = op->getIn(1);
|
||||
if (!vn1->isWritten()) return 0;
|
||||
PcodeOp *op0 = vn0->getDef();
|
||||
OpCode opc0 = op0->code();
|
||||
if (opc0 != CPUI_BOOL_AND && opc0 != CPUI_BOOL_OR) return 0;
|
||||
PcodeOp *op1 = vn1->getDef();
|
||||
OpCode opc1 = op1->code();
|
||||
if (opc1 != CPUI_BOOL_AND && opc1 != CPUI_BOOL_OR) return 0;
|
||||
Varnode *ins[4];
|
||||
ins[0] = op0->getIn(0);
|
||||
ins[1] = op0->getIn(1);
|
||||
ins[2] = op1->getIn(0);
|
||||
ins[3] = op1->getIn(1);
|
||||
if (ins[0]->isFree() || ins[1]->isFree() || ins[2]->isFree() || ins[3]->isFree()) return 0;
|
||||
bool isflipped[4];
|
||||
isflipped[0] = isflipped[1] = isflipped[2] = isflipped[3] = false;
|
||||
bool centralEqual = (op->code() == CPUI_INT_EQUAL);
|
||||
if (opc0 == CPUI_BOOL_OR) {
|
||||
isflipped[0] = !isflipped[0];
|
||||
isflipped[1] = !isflipped[1];
|
||||
centralEqual = !centralEqual;
|
||||
}
|
||||
if (opc1 == CPUI_BOOL_OR) {
|
||||
isflipped[2] = !isflipped[2];
|
||||
isflipped[3] = !isflipped[3];
|
||||
centralEqual = !centralEqual;
|
||||
}
|
||||
int4 leftSlot,rightSlot;
|
||||
if (isMatch(ins[0],ins[2],isflipped[2])) {
|
||||
leftSlot = 0;
|
||||
rightSlot = 2;
|
||||
}
|
||||
else if (isMatch(ins[0],ins[3],isflipped[3])) {
|
||||
leftSlot = 0;
|
||||
rightSlot = 3;
|
||||
}
|
||||
else if (isMatch(ins[1],ins[2],isflipped[2])) {
|
||||
leftSlot = 1;
|
||||
rightSlot = 2;
|
||||
}
|
||||
else if (isMatch(ins[1],ins[3],isflipped[3])) {
|
||||
leftSlot = 1;
|
||||
rightSlot = 3;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
if (isflipped[leftSlot] != isflipped[rightSlot]) return 0;
|
||||
OpCode combineOpc;
|
||||
if (centralEqual) {
|
||||
combineOpc = CPUI_BOOL_OR;
|
||||
isflipped[leftSlot] = !isflipped[leftSlot];
|
||||
}
|
||||
else {
|
||||
combineOpc = CPUI_BOOL_AND;
|
||||
}
|
||||
Varnode *finalA = ins[leftSlot];
|
||||
if (isflipped[leftSlot])
|
||||
finalA = data.opBoolNegate(finalA, op, false);
|
||||
if (isflipped[1-leftSlot])
|
||||
centralEqual = !centralEqual;
|
||||
if (isflipped[5-rightSlot])
|
||||
centralEqual = !centralEqual;
|
||||
Varnode *finalB = ins[1-leftSlot];
|
||||
Varnode *finalC = ins[5-rightSlot];
|
||||
PcodeOp *eqOp = data.newOp(2,op->getAddr());
|
||||
data.opSetOpcode(eqOp, centralEqual ? CPUI_INT_EQUAL : CPUI_INT_NOTEQUAL);
|
||||
Varnode *tmp1 = data.newUniqueOut(1, eqOp);
|
||||
data.opSetInput(eqOp,finalB,0);
|
||||
data.opSetInput(eqOp,finalC,1);
|
||||
data.opInsertBefore(eqOp, op);
|
||||
data.opSetOpcode(op, combineOpc);
|
||||
data.opSetInput(op,tmp1,1);
|
||||
data.opSetInput(op,finalA,0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// \class RuleBooleanDedup
|
||||
/// \brief Remove duplicate clauses in boolean expressions
|
||||
///
|
||||
/// - `(A && B) || (A && C) => A && (B || C)`
|
||||
/// - `(A || B) && (A || C) => A || (B && C)`
|
||||
/// - `(A || B) || (!A && C) => A || (B || C)`
|
||||
/// - `(A && B) && (A && C) => A && (B && C)`
|
||||
/// - `(A || B) || (A || C) => A || (B || C)`
|
||||
void RuleBooleanDedup::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_BOOL_AND);
|
||||
oplist.push_back(CPUI_BOOL_OR);
|
||||
}
|
||||
|
||||
bool RuleBooleanDedup::isMatch(Varnode *leftVn,Varnode *rightVn,bool &isFlip)
|
||||
|
||||
{
|
||||
int4 val = BooleanMatch::evaluate(leftVn,rightVn,1);
|
||||
if (val == BooleanMatch::same) {
|
||||
isFlip = false;
|
||||
return true;
|
||||
}
|
||||
if (val == BooleanMatch::complementary) {
|
||||
isFlip = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int4 RuleBooleanDedup::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Varnode *vn0 = op->getIn(0);
|
||||
if (!vn0->isWritten()) return 0;
|
||||
Varnode *vn1 = op->getIn(1);
|
||||
if (!vn1->isWritten()) return 0;
|
||||
PcodeOp *op0 = vn0->getDef();
|
||||
OpCode opc0 = op0->code();
|
||||
if (opc0 != CPUI_BOOL_AND && opc0 != CPUI_BOOL_OR) return 0;
|
||||
PcodeOp *op1 = vn1->getDef();
|
||||
OpCode opc1 = op1->code();
|
||||
if (opc1 != CPUI_BOOL_AND && opc1 != CPUI_BOOL_OR) return 0;
|
||||
Varnode *ins[4];
|
||||
ins[0] = op0->getIn(0);
|
||||
ins[1] = op0->getIn(1);
|
||||
ins[2] = op1->getIn(0);
|
||||
ins[3] = op1->getIn(1);
|
||||
if (ins[0]->isFree() || ins[1]->isFree() || ins[2]->isFree() || ins[3]->isFree()) return 0;
|
||||
bool isflipped = false;
|
||||
Varnode *leftA,*rightA;
|
||||
Varnode *leftO,*rightO;
|
||||
if (isMatch(ins[0],ins[2],isflipped)) {
|
||||
leftA = ins[0];
|
||||
rightA = ins[2];
|
||||
leftO = ins[1];
|
||||
rightO = ins[3];
|
||||
}
|
||||
else if (isMatch(ins[0],ins[3],isflipped)) {
|
||||
leftA = ins[0];
|
||||
rightA = ins[3];
|
||||
leftO = ins[1];
|
||||
rightO = ins[2];
|
||||
}
|
||||
else if (isMatch(ins[1],ins[2],isflipped)) {
|
||||
leftA = ins[1];
|
||||
rightA = ins[2];
|
||||
leftO = ins[0];
|
||||
rightO = ins[3];
|
||||
}
|
||||
else if (isMatch(ins[1],ins[3],isflipped)) {
|
||||
leftA = ins[1];
|
||||
rightA = ins[3];
|
||||
leftO = ins[0];
|
||||
rightO = ins[2];
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
OpCode centralOpc = op->code();
|
||||
OpCode bcOpc,finalOpc;
|
||||
Varnode *finalA;
|
||||
if (isflipped) {
|
||||
if (centralOpc == CPUI_BOOL_AND && opc0 == CPUI_BOOL_AND && opc1 == CPUI_BOOL_AND) {
|
||||
// (A && B) && (!A && C)
|
||||
data.opSetOpcode(op, CPUI_COPY);
|
||||
data.opRemoveInput(op, 1);
|
||||
data.opSetInput(op,data.newConstant(1, 0),0); // Whole expression is false
|
||||
return 1;
|
||||
}
|
||||
if (centralOpc == CPUI_BOOL_OR && opc0 == CPUI_BOOL_OR && opc1 == CPUI_BOOL_OR) {
|
||||
// (A || B) || (!A || C)
|
||||
data.opSetOpcode(op, CPUI_COPY);
|
||||
data.opRemoveInput(op, 1);
|
||||
data.opSetInput(op,data.newConstant(1, 1),0); // Whole expression is true
|
||||
return 1;
|
||||
}
|
||||
if (centralOpc == CPUI_BOOL_OR && opc0 != opc1) {
|
||||
// (A || B) || (!A && C)
|
||||
finalA = (opc0 == CPUI_BOOL_OR) ? leftA : rightA;
|
||||
finalOpc = CPUI_BOOL_OR;
|
||||
bcOpc = CPUI_BOOL_OR;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (centralOpc == opc0 && centralOpc == opc1) {
|
||||
// (A && B) && (A && C) or (A || B) || (A || C)
|
||||
finalA = leftA;
|
||||
finalOpc = centralOpc;
|
||||
bcOpc = centralOpc;
|
||||
}
|
||||
else if (opc0 == opc1 && centralOpc != opc0) {
|
||||
// (A && B) || (A && C) or (A || B) && (A || C)
|
||||
finalA = leftA;
|
||||
finalOpc = opc0;
|
||||
bcOpc = centralOpc;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
PcodeOp *bcOp = data.newOp(2,op->getAddr());
|
||||
Varnode *tmp = data.newUniqueOut(1, bcOp);
|
||||
data.opSetOpcode(bcOp, bcOpc);
|
||||
data.opSetInput(bcOp, leftO, 0);
|
||||
data.opSetInput(bcOp, rightO, 1);
|
||||
data.opInsertBefore(bcOp, op);
|
||||
data.opSetOpcode(op, finalOpc);
|
||||
data.opSetInput(op, finalA, 0);
|
||||
data.opSetInput(op, tmp, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// \class RuleBooleanNegate
|
||||
/// \brief Simplify comparisons with boolean values: `V == false => !V, V == true => V`
|
||||
///
|
||||
|
@ -8925,128 +9175,90 @@ int4 RuleNegateNegate::applyOp(PcodeOp *op,Funcdata &data)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/// Check if given Varnode is a boolean value and break down its construction.
|
||||
/// Varnode is assumed to be an input to a MULTIEQUAL
|
||||
/// \param vn is the given root Varnode
|
||||
/// \return \b true if it is a boolean expression
|
||||
bool RuleConditionalMove::BoolExpress::initialize(Varnode *vn)
|
||||
/// Check if the given Varnode is a boolean value and return the root of the expression.
|
||||
/// The Varnode is assumed to be an input to a MULTIEQUAL.
|
||||
/// \param vn is the given Varnode
|
||||
/// \return null if the Varnode is not a boolean value, otherwise return the root Varnode of the expression
|
||||
Varnode *RuleConditionalMove::checkBoolean(Varnode *vn)
|
||||
|
||||
{
|
||||
if (!vn->isWritten()) return false;
|
||||
op = vn->getDef();
|
||||
opc = op->code();
|
||||
switch(opc) {
|
||||
case CPUI_COPY:
|
||||
in0 = op->getIn(0);
|
||||
if (in0->isConstant()) {
|
||||
optype = 0;
|
||||
val = in0->getOffset();
|
||||
return ((val & ~((uintb)1)) == 0);
|
||||
if (!vn->isWritten()) return (Varnode *)0;
|
||||
PcodeOp *op = vn->getDef();
|
||||
if (op->isBoolOutput()) {
|
||||
return vn;
|
||||
}
|
||||
if (op->code() == CPUI_COPY) {
|
||||
vn = op->getIn(0);
|
||||
if (vn->isConstant()) {
|
||||
uintb val = vn->getOffset();
|
||||
if ((val & ~((uintb)1)) == 0)
|
||||
return vn;
|
||||
}
|
||||
return false;
|
||||
case CPUI_INT_EQUAL:
|
||||
case CPUI_INT_NOTEQUAL:
|
||||
case CPUI_INT_SLESS:
|
||||
case CPUI_INT_SLESSEQUAL:
|
||||
case CPUI_INT_LESS:
|
||||
case CPUI_INT_LESSEQUAL:
|
||||
case CPUI_INT_CARRY:
|
||||
case CPUI_INT_SCARRY:
|
||||
case CPUI_INT_SBORROW:
|
||||
case CPUI_BOOL_XOR:
|
||||
case CPUI_BOOL_AND:
|
||||
case CPUI_BOOL_OR:
|
||||
case CPUI_FLOAT_EQUAL:
|
||||
case CPUI_FLOAT_NOTEQUAL:
|
||||
case CPUI_FLOAT_LESS:
|
||||
case CPUI_FLOAT_LESSEQUAL:
|
||||
in0 = op->getIn(0);
|
||||
in1 = op->getIn(1);
|
||||
optype = 2;
|
||||
break;
|
||||
case CPUI_BOOL_NEGATE:
|
||||
case CPUI_FLOAT_NAN:
|
||||
in0 = op->getIn(0);
|
||||
optype = 1;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return (Varnode *)0;
|
||||
}
|
||||
|
||||
/// Evaluate if \b this expression can be easily propagated past a merge point.
|
||||
/// Also can the Varnode be used past the merge, or does its value need to be reconstructed.
|
||||
/// \param root is the split point
|
||||
/// \param branch is the block on which the expression exists and after which is the merge
|
||||
/// \brief Determine if the given expression can be propagated out of the condition
|
||||
///
|
||||
/// If p-code ops contributing to the expression are contained in a conditional branch, they are collected in
|
||||
/// \b ops to later be pulled out of the branch (via duplication).
|
||||
/// \param vn is the root of the given expression
|
||||
/// \param ops will hold the set of ops that need to be duplicated
|
||||
/// \param root is the block that performs the conditional branch
|
||||
/// \param branch is the conditional branch
|
||||
/// \return \b true if the expression can be propagated
|
||||
bool RuleConditionalMove::BoolExpress::evaluatePropagation(FlowBlock *root,FlowBlock *branch)
|
||||
bool RuleConditionalMove::gatherExpression(Varnode *vn,vector<PcodeOp *> &ops,FlowBlock *root,FlowBlock *branch)
|
||||
|
||||
{
|
||||
mustreconstruct = false;
|
||||
if (optype==0) return true; // Constants can always be propagated
|
||||
if (vn->isConstant()) return true; // Constants can always be propagated
|
||||
if (vn->isFree()) return false;
|
||||
if (vn->isAddrTied()) return false;
|
||||
if (root == branch) return true; // Can always propagate if there is no branch
|
||||
if (!vn->isWritten()) return true;
|
||||
PcodeOp *op = vn->getDef();
|
||||
if (op->getParent() != branch) return true; // Can propagate if value formed before branch
|
||||
mustreconstruct = true; // Final op is performed in branch, so it must be reconstructed
|
||||
if (in0->isFree() && !in0->isConstant()) return false;
|
||||
if (in0->isWritten() && (in0->getDef()->getParent()==branch)) return false;
|
||||
if (optype == 2) {
|
||||
if (in1->isFree() && !in1->isConstant()) return false;
|
||||
if (in1->isWritten() && (in1->getDef()->getParent()==branch)) return false;
|
||||
ops.push_back(op);
|
||||
int4 pos = 0;
|
||||
while(pos < ops.size()) {
|
||||
op = ops[pos];
|
||||
pos += 1;
|
||||
if (op->getEvalType() == PcodeOp::special)
|
||||
return false;
|
||||
for(int4 i=0;i<op->numInput();++i) {
|
||||
Varnode *in0 = op->getIn(i);
|
||||
if (in0->isFree() && !in0->isConstant()) return false;
|
||||
if (in0->isWritten() && (in0->getDef()->getParent()==branch)) {
|
||||
if (in0->isAddrTied()) return false; // Don't pull out results that can be indirectly addressed
|
||||
if (in0->loneDescend() != op) return false; // Don't pull out results with more than one use
|
||||
if (ops.size() >= 4) return false;
|
||||
ops.push_back(in0->getDef());
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Produce the boolean Varnode to use after the merge.
|
||||
/// Reproduce the bolean expression resulting in the given Varnode.
|
||||
/// Either reuse the existing Varnode or reconstruct it,
|
||||
/// making sure the expression does not depend on data in the branch.
|
||||
/// \param insertop is point at which any reconstruction should be inserted
|
||||
/// \param data is the function being analyzed
|
||||
/// \return the Varnode representing the boolean expression
|
||||
Varnode *RuleConditionalMove::BoolExpress::constructBool(PcodeOp *insertop,Funcdata &data)
|
||||
Varnode *RuleConditionalMove::constructBool(Varnode *vn,PcodeOp *insertop,vector<PcodeOp *> &ops,Funcdata &data)
|
||||
|
||||
{
|
||||
Varnode *resvn;
|
||||
if (mustreconstruct) {
|
||||
PcodeOp *newop = data.newOp(optype,op->getAddr()); // Keep the original address
|
||||
data.opSetOpcode(newop, opc );
|
||||
resvn = data.newUniqueOut(1,newop);
|
||||
if (in0->isConstant())
|
||||
in0 = data.newConstant(in0->getSize(),in0->getOffset());
|
||||
data.opSetInput(newop,in0,0);
|
||||
if (optype == 2) { // Binary op
|
||||
if (in1->isConstant())
|
||||
in1 = data.newConstant(in1->getSize(),in1->getOffset());
|
||||
data.opSetInput(newop,in1,1);
|
||||
}
|
||||
data.opInsertBefore(newop,insertop);
|
||||
if (!ops.empty()) {
|
||||
sort(ops.begin(),ops.end(),compareOp);
|
||||
CloneBlockOps cloner(data);
|
||||
resvn = cloner.cloneExpression(ops, insertop);
|
||||
}
|
||||
else {
|
||||
if (optype == 0)
|
||||
resvn = data.newConstant(1,val);
|
||||
else
|
||||
resvn = op->getOut();
|
||||
resvn = vn;
|
||||
}
|
||||
return resvn;
|
||||
}
|
||||
|
||||
/// \brief Construct the boolean negation of a given boolean Varnode
|
||||
///
|
||||
/// \param vn is the given Varnode
|
||||
/// \param op is the point at which to insert the BOOL_NEGATE op
|
||||
/// \param data is the function being analyzed
|
||||
/// \return the output of the new op
|
||||
Varnode *RuleConditionalMove::constructNegate(Varnode *vn,PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
PcodeOp *negateop = data.newOp(1,op->getAddr());
|
||||
data.opSetOpcode(negateop,CPUI_BOOL_NEGATE);
|
||||
Varnode *resvn = data.newUniqueOut(1,negateop);
|
||||
data.opSetInput(negateop,vn,0);
|
||||
data.opInsertBefore(negateop,op);
|
||||
return resvn;
|
||||
}
|
||||
|
||||
/// \class RuleConditionalMove
|
||||
/// \brief Simplify various conditional move situations
|
||||
///
|
||||
|
@ -9079,16 +9291,16 @@ void RuleConditionalMove::getOpList(vector<uint4> &oplist) const
|
|||
int4 RuleConditionalMove::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
BoolExpress bool0;
|
||||
BoolExpress bool1;
|
||||
BlockBasic *bb;
|
||||
FlowBlock *inblock0,*inblock1;
|
||||
FlowBlock *rootblock0,*rootblock1;
|
||||
|
||||
if (op->numInput() != 2) return 0; // MULTIEQUAL must have exactly 2 inputs
|
||||
|
||||
if (!bool0.initialize(op->getIn(0))) return 0;
|
||||
if (!bool1.initialize(op->getIn(1))) return 0;
|
||||
Varnode *bool0 = checkBoolean(op->getIn(0));
|
||||
if (bool0 == (Varnode *)0) return 0;
|
||||
Varnode *bool1 = checkBoolean(op->getIn(1));
|
||||
if (bool1 == (Varnode *)0) return 0;
|
||||
|
||||
// Look for the situation
|
||||
// inblock0
|
||||
|
@ -9120,8 +9332,10 @@ int4 RuleConditionalMove::applyOp(PcodeOp *op,Funcdata &data)
|
|||
if (cbranch == (PcodeOp *)0) return 0;
|
||||
if (cbranch->code() != CPUI_CBRANCH) return 0;
|
||||
|
||||
if (!bool0.evaluatePropagation(rootblock0,inblock0)) return 0;
|
||||
if (!bool1.evaluatePropagation(rootblock0,inblock1)) return 0;
|
||||
vector<PcodeOp *> opList0;
|
||||
if (!gatherExpression(bool0,opList0,rootblock0,inblock0)) return 0;
|
||||
vector<PcodeOp *> opList1;
|
||||
if (!gatherExpression(bool1,opList1,rootblock0,inblock1)) return 0;
|
||||
|
||||
bool path0istrue;
|
||||
if (rootblock0 != inblock0)
|
||||
|
@ -9131,7 +9345,7 @@ int4 RuleConditionalMove::applyOp(PcodeOp *op,Funcdata &data)
|
|||
if (cbranch->isBooleanFlip())
|
||||
path0istrue = !path0istrue;
|
||||
|
||||
if (!bool0.isConstant() && !bool1.isConstant()) {
|
||||
if (!bool0->isConstant() && !bool1->isConstant()) {
|
||||
if (inblock0 == rootblock0) {
|
||||
Varnode *boolvn = cbranch->getIn(1);
|
||||
bool andorselect = path0istrue;
|
||||
|
@ -9147,8 +9361,8 @@ int4 RuleConditionalMove::applyOp(PcodeOp *op,Funcdata &data)
|
|||
data.opUninsert( op );
|
||||
data.opSetOpcode(op, opc);
|
||||
data.opInsertBegin(op, bb);
|
||||
Varnode *firstvn = bool0.constructBool(op,data);
|
||||
Varnode *secondvn = bool1.constructBool(op,data);
|
||||
Varnode *firstvn = constructBool(bool0,op,opList0,data);
|
||||
Varnode *secondvn = constructBool(bool1,op,opList1,data);
|
||||
data.opSetInput(op,firstvn,0);
|
||||
data.opSetInput(op,secondvn,1);
|
||||
return 1;
|
||||
|
@ -9168,8 +9382,8 @@ int4 RuleConditionalMove::applyOp(PcodeOp *op,Funcdata &data)
|
|||
OpCode opc = andorselect ? CPUI_BOOL_OR : CPUI_BOOL_AND;
|
||||
data.opSetOpcode(op, opc);
|
||||
data.opInsertBegin(op, bb);
|
||||
Varnode *firstvn = bool1.constructBool(op,data);
|
||||
Varnode *secondvn = bool0.constructBool(op,data);
|
||||
Varnode *firstvn = constructBool(bool1,op,opList1,data);
|
||||
Varnode *secondvn = constructBool(bool0,op,opList0,data);
|
||||
data.opSetInput(op,firstvn,0);
|
||||
data.opSetInput(op,secondvn,1);
|
||||
return 1;
|
||||
|
@ -9180,17 +9394,17 @@ int4 RuleConditionalMove::applyOp(PcodeOp *op,Funcdata &data)
|
|||
// Below here some change is being made
|
||||
data.opUninsert( op ); // Changing from MULTIEQUAL, this should be reinserted
|
||||
int4 sz = op->getOut()->getSize();
|
||||
if (bool0.isConstant() && bool1.isConstant()) {
|
||||
if (bool0.getVal() == bool1.getVal()) {
|
||||
if (bool0->isConstant() && bool1->isConstant()) {
|
||||
if (bool0->getOffset() == bool1->getOffset()) {
|
||||
data.opRemoveInput(op,1);
|
||||
data.opSetOpcode(op,CPUI_COPY);
|
||||
data.opSetInput(op, data.newConstant( sz, bool0.getVal() ), 0 );
|
||||
data.opSetInput(op, data.newConstant( sz, bool0->getOffset() ), 0 );
|
||||
data.opInsertBegin(op,bb);
|
||||
}
|
||||
else {
|
||||
data.opRemoveInput(op,1);
|
||||
Varnode *boolvn = cbranch->getIn(1);
|
||||
bool needcomplement = ( (bool0.getVal()==0) == path0istrue );
|
||||
bool needcomplement = ( (bool0->getOffset()==0) == path0istrue );
|
||||
if (sz == 1) {
|
||||
if (needcomplement)
|
||||
data.opSetOpcode(op,CPUI_BOOL_NEGATE);
|
||||
|
@ -9203,32 +9417,32 @@ int4 RuleConditionalMove::applyOp(PcodeOp *op,Funcdata &data)
|
|||
data.opSetOpcode(op,CPUI_INT_ZEXT);
|
||||
data.opInsertBegin(op,bb);
|
||||
if (needcomplement)
|
||||
boolvn = constructNegate(boolvn,op,data);
|
||||
boolvn = data.opBoolNegate(boolvn,op,false);
|
||||
data.opSetInput(op,boolvn,0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bool0.isConstant()) {
|
||||
bool needcomplement = (path0istrue != (bool0.getVal()!=0));
|
||||
OpCode opc = (bool0.getVal()!=0) ? CPUI_BOOL_OR : CPUI_BOOL_AND;
|
||||
else if (bool0->isConstant()) {
|
||||
bool needcomplement = (path0istrue != (bool0->getOffset()!=0));
|
||||
OpCode opc = (bool0->getOffset()!=0) ? CPUI_BOOL_OR : CPUI_BOOL_AND;
|
||||
data.opSetOpcode(op,opc);
|
||||
data.opInsertBegin(op,bb);
|
||||
Varnode *boolvn = cbranch->getIn(1);
|
||||
if (needcomplement)
|
||||
boolvn = constructNegate(boolvn,op,data);
|
||||
Varnode *body1 = bool1.constructBool(op,data);
|
||||
boolvn = data.opBoolNegate(boolvn,op,false);
|
||||
Varnode *body1 = constructBool(bool1,op,opList1,data);
|
||||
data.opSetInput(op,boolvn,0);
|
||||
data.opSetInput(op,body1,1);
|
||||
}
|
||||
else { // bool1 must be constant
|
||||
bool needcomplement = (path0istrue == (bool1.getVal()!=0));
|
||||
OpCode opc = (bool1.getVal()!=0) ? CPUI_BOOL_OR : CPUI_BOOL_AND;
|
||||
bool needcomplement = (path0istrue == (bool1->getOffset()!=0));
|
||||
OpCode opc = (bool1->getOffset()!=0) ? CPUI_BOOL_OR : CPUI_BOOL_AND;
|
||||
data.opSetOpcode(op,opc);
|
||||
data.opInsertBegin(op,bb);
|
||||
Varnode *boolvn = cbranch->getIn(1);
|
||||
if (needcomplement)
|
||||
boolvn = constructNegate(boolvn,op,data);
|
||||
Varnode *body0 = bool0.constructBool(op,data);
|
||||
boolvn = data.opBoolNegate(boolvn,op,false);
|
||||
Varnode *body0 = constructBool(bool0,op,opList0,data);
|
||||
data.opSetInput(op,boolvn,0);
|
||||
data.opSetInput(op,body0,1);
|
||||
}
|
||||
|
@ -9312,6 +9526,11 @@ bool RuleIgnoreNan::checkBackForCompare(Varnode *floatVar,Varnode *root)
|
|||
if (!root->isWritten()) return false;
|
||||
PcodeOp *def1 = root->getDef();
|
||||
if (!def1->isBoolOutput()) return false;
|
||||
if (def1->code() == CPUI_BOOL_NEGATE) {
|
||||
Varnode *vn = def1->getIn(0);
|
||||
if (!vn->isWritten()) return false;
|
||||
def1 = vn->getDef();
|
||||
}
|
||||
if (def1->getOpcode()->isFloatingPointOp()) {
|
||||
if (def1->numInput() != 2) return false;
|
||||
if (functionalEquality(floatVar, def1->getIn(0)))
|
||||
|
@ -9321,7 +9540,7 @@ bool RuleIgnoreNan::checkBackForCompare(Varnode *floatVar,Varnode *root)
|
|||
return false;
|
||||
}
|
||||
OpCode opc = def1->code();
|
||||
if (opc != CPUI_BOOL_AND || opc != CPUI_BOOL_OR)
|
||||
if (opc != CPUI_BOOL_AND && opc != CPUI_BOOL_OR)
|
||||
return false;
|
||||
for(int4 i=0;i<2;++i) {
|
||||
Varnode *vn = def1->getIn(i);
|
||||
|
|
|
@ -547,6 +547,28 @@ public:
|
|||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
class RuleBooleanUndistribute : public Rule {
|
||||
static bool isMatch(Varnode *leftVn,Varnode *rightVn,bool &rightFlip);
|
||||
public:
|
||||
RuleBooleanUndistribute(const string &g) : Rule(g, 0, "booleanundistribute") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleBooleanUndistribute(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
class RuleBooleanDedup : public Rule {
|
||||
static bool isMatch(Varnode *leftVn,Varnode *rightVn,bool &isFlip);
|
||||
public:
|
||||
RuleBooleanDedup(const string &g) : Rule(g, 0, "booleandedup") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleBooleanDedup(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
class RuleBooleanNegate : public Rule {
|
||||
public:
|
||||
RuleBooleanNegate(const string &g) : Rule(g, 0, "booleannegate") {} ///< Constructor
|
||||
|
@ -1394,23 +1416,10 @@ public:
|
|||
};
|
||||
|
||||
class RuleConditionalMove : public Rule {
|
||||
/// \brief Class for categorizing and rebuilding a boolean expression
|
||||
class BoolExpress {
|
||||
int4 optype; ///< 0=constant 1=unary 2=binary
|
||||
OpCode opc; ///< OpCode constructing the boolean value
|
||||
PcodeOp *op; ///< PcodeOp constructing the boolean value
|
||||
uintb val; ///< Value (if boolean is constant)
|
||||
Varnode *in0; ///< First input
|
||||
Varnode *in1; ///< Second input
|
||||
bool mustreconstruct; ///< Must make a copy of final boolean operation
|
||||
public:
|
||||
bool isConstant(void) const { return (optype==0); } ///< Return \b true if boolean is a constant
|
||||
uintb getVal(void) const { return val; } ///< Get the constant boolean value
|
||||
bool initialize(Varnode *vn); ///< Initialize based on output Varnode
|
||||
bool evaluatePropagation(FlowBlock *root,FlowBlock *branch); ///< Can this expression be propagated
|
||||
Varnode *constructBool(PcodeOp *insertop,Funcdata &data); ///< Construct the expression after the merge
|
||||
};
|
||||
static Varnode *constructNegate(Varnode *vn,PcodeOp *op,Funcdata &data);
|
||||
static Varnode *checkBoolean(Varnode *vn); ///< Check for boolean expression
|
||||
static bool gatherExpression(Varnode *vn,vector<PcodeOp *> &ops,FlowBlock *root,FlowBlock *branch);
|
||||
static Varnode *constructBool(Varnode *vn,PcodeOp *insertop,vector<PcodeOp *> &ops,Funcdata &data); ///< Construct the expression after the merge
|
||||
static bool compareOp(PcodeOp *op0,PcodeOp *op1) { return op0->getSeqNum().getOrder() < op1->getSeqNum().getOrder(); }
|
||||
public:
|
||||
RuleConditionalMove(const string &g) : Rule( g, 0, "conditionalmove") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
|
|
|
@ -1315,7 +1315,7 @@ Datatype *TypeOpIntSub::getOutputToken(const PcodeOp *op,CastStrategy *castStrat
|
|||
TypeOpIntCarry::TypeOpIntCarry(TypeFactory *t)
|
||||
: TypeOpFunc(t,CPUI_INT_CARRY,"CARRY",TYPE_BOOL,TYPE_UINT)
|
||||
{
|
||||
opflags = PcodeOp::binary | PcodeOp::commutative;
|
||||
opflags = PcodeOp::binary | PcodeOp::commutative | PcodeOp::booloutput;
|
||||
addlflags = arithmetic_op;
|
||||
behave = new OpBehaviorIntCarry();
|
||||
}
|
||||
|
@ -1331,7 +1331,8 @@ string TypeOpIntCarry::getOperatorName(const PcodeOp *op) const
|
|||
TypeOpIntScarry::TypeOpIntScarry(TypeFactory *t)
|
||||
: TypeOpFunc(t,CPUI_INT_SCARRY,"SCARRY",TYPE_BOOL,TYPE_INT)
|
||||
{
|
||||
opflags = PcodeOp::binary | PcodeOp::commutative;
|
||||
opflags = PcodeOp::binary | PcodeOp::commutative | PcodeOp::booloutput;
|
||||
addlflags = arithmetic_op;
|
||||
behave = new OpBehaviorIntScarry();
|
||||
}
|
||||
|
||||
|
@ -1346,7 +1347,7 @@ string TypeOpIntScarry::getOperatorName(const PcodeOp *op) const
|
|||
TypeOpIntSborrow::TypeOpIntSborrow(TypeFactory *t)
|
||||
: TypeOpFunc(t,CPUI_INT_SBORROW,"SBORROW",TYPE_BOOL,TYPE_INT)
|
||||
{
|
||||
opflags = PcodeOp::binary;
|
||||
opflags = PcodeOp::binary | PcodeOp::booloutput;
|
||||
addlflags = arithmetic_op;
|
||||
behave = new OpBehaviorIntSborrow();
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -157,6 +157,7 @@ private:
|
|||
friend class VarnodeBank;
|
||||
friend class Merge;
|
||||
friend class Funcdata;
|
||||
friend class CloneBlockOps;
|
||||
void updateCover(void) const; ///< Internal function for update coverage information
|
||||
void calcCover(void) const; ///< Turn on the Cover object for this Varnode
|
||||
void clearCover(void) const; ///< Turn off any coverage information
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue