diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/condexe.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/condexe.cc index 163aa10b7d..4042af03fe 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/condexe.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/condexe.cc @@ -17,206 +17,7 @@ namespace ghidra { -ConditionMarker::ConditionMarker(void) - -{ - initop = (PcodeOp *)0; - basevn = (Varnode *)0; - boolvn = (Varnode *)0; - bool2vn = (Varnode *)0; - bool3vn = (Varnode *)0; - binaryop = (PcodeOp *)0; -} - -/// Any marks on Varnodes in the root expression are cleared -ConditionMarker::~ConditionMarker(void) - -{ - basevn->clearMark(); - if (boolvn != (Varnode *)0) - boolvn->clearMark(); - if (bool2vn != (Varnode *)0) - bool2vn->clearMark(); - if (bool3vn != (Varnode *)0) - bool3vn->clearMark(); - if (binaryop != (PcodeOp *)0) { - binaryop->getIn(0)->clearMark(); - binaryop->getIn(1)->clearMark(); - } -} - -/// Starting with the CBRANCH, the key Varnodes in the expression producing -/// the boolean value are marked. BOOL_NEGATE operations are traversed, but -/// otherwise only one level of operator is walked. -/// \param op is the root CBRANCH operation -void ConditionMarker::setupInitOp(PcodeOp *op) - -{ - initop = op; - basevn = op->getIn(1); - Varnode *curvn = basevn; - curvn->setMark(); - if (curvn->isWritten()) { - PcodeOp *tmp = curvn->getDef(); - if (tmp->code() == CPUI_BOOL_NEGATE) { - boolvn = tmp->getIn(0); - curvn = boolvn; - curvn->setMark(); - } - } - if (curvn->isWritten()) { - PcodeOp *tmp = curvn->getDef(); - if (tmp->isBoolOutput()&&(tmp->getEvalType()==PcodeOp::binary)) { - binaryop = tmp; - Varnode *binvn = binaryop->getIn(0); - if (!binvn->isConstant()) { - if (binvn->isWritten()) { - PcodeOp *negop = binvn->getDef(); - if (negop->code() == CPUI_BOOL_NEGATE) { - if (!negop->getIn(0)->isConstant()) { - bool2vn = negop->getIn(0); - bool2vn->setMark(); - } - } - } - binvn->setMark(); - } - binvn = binaryop->getIn(1); - if (!binvn->isConstant()) { - if (binvn->isWritten()) { - PcodeOp *negop = binvn->getDef(); - if (negop->code() == CPUI_BOOL_NEGATE) { - if (!negop->getIn(0)->isConstant()) { - bool3vn = negop->getIn(0); - bool3vn->setMark(); - } - } - } - binvn->setMark(); - } - } - } -} - -/// Walk the tree rooted at the given p-code op, looking for things marked in -/// the tree rooted at \b initop. Trim everything but BOOL_NEGATE operations, -/// one MULTIEQUAL, and one binary boolean operation. If there is a Varnode -/// in common with the root expression, this is returned, and the tree traversal -/// state holds the path from the boolean value to the common Varnode. -/// \param op is the given CBRANCH op to compare -/// \return the Varnode in common with the root expression or NULL -Varnode *ConditionMarker::findMatch(PcodeOp *op) - -{ - PcodeOp *curop; - // FlowBlock *bl = op->getParent(); - state = 0; - Varnode *curvn = op->getIn(1); - multion = false; - binon = false; - - matchflip = op->isBooleanFlip(); - - for(;;) { - if (curvn->isMark()) return curvn; - bool popstate = true; - if (curvn->isWritten()) { - curop = curvn->getDef(); - if (curop->code() == CPUI_BOOL_NEGATE) { - curvn = curop->getIn(0); - if (!binon) // Only flip if we haven't seen binop yet, as binops get compared directly - matchflip = !matchflip; - popstate = false; - } -// else if (curop->code() == CPUI_MULTIEQUAL) { -// if ((curop->getParent()==bl)&&(!multion)) { -// opstate[state] = curop; -// slotstate[state] = 0; -// flipstate[state] = matchflip; -// state += 1; -// curvn = curop->Input(0); -// multion = true; -// popstate = false; -// } -// } - else if (curop->isBoolOutput()&&(curop->getEvalType()==PcodeOp::binary)) { - if (!binon) { - opstate[state] = curop; - slotstate[state] = 0; - flipstate[state] = matchflip; - state += 1; - curvn = curop->getIn(0); - binon = true; - popstate = false; - } - } - } - if (popstate) { - while(state > 0) { - curop = opstate[state-1]; - matchflip = flipstate[state-1]; - slotstate[state-1] += 1; - if (slotstate[state-1] < curop->numInput()) { - curvn = curop->getIn(slotstate[state-1]); - break; - } - state -= 1; - if (opstate[state]->code() == CPUI_MULTIEQUAL) - multion = false; - else - binon = false; - } - if (state==0) break; - } - } - return (Varnode *)0; -} - -/// \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 ConditionMarker::varnodeSame(Varnode *a,Varnode *b) - -{ - if (a == b) return true; - if (a->isConstant() && b->isConstant()) - return (a->getOffset() == b->getOffset()); - return false; -} - -/// \brief Do the given boolean Varnodes always hold complementary values -/// -/// Test if they are constants, 1 and 0, or if one is the direct BOOL_NEGATE of the other. -/// \param a is the first Varnode to compare -/// \param b is the second Varnode to compare -/// \return \b true if the Varnodes (always) hold complementary values -bool ConditionMarker::varnodeComplement(Varnode *a,Varnode *b) - -{ - if (a->isConstant() && b->isConstant()) { - uintb vala = a->getOffset(); - uintb valb = b->getOffset(); - if ((vala==0)&&(valb==1)) return true; - if ((vala==1)&&(valb==0)) return true; - return false; - } - PcodeOp *negop; - if (a->isWritten()) { - negop = a->getDef(); - if (negop->code() == CPUI_BOOL_NEGATE) - if (negop->getIn(0) == b) - return true; - } - if (b->isWritten()) { - negop = b->getDef(); - if (negop->code() == CPUI_BOOL_NEGATE) - if (negop->getIn(0) == a) - return true; - } - return false; -} +const int4 BooleanExpressionMatch::maxDepth = 1; /// \brief Test if two operations with same opcode produce complementary boolean values /// @@ -225,7 +26,7 @@ bool ConditionMarker::varnodeComplement(Varnode *a,Varnode *b) /// \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 ConditionMarker::sameOpComplement(PcodeOp *bin1op,PcodeOp *bin2op) +bool BooleanExpressionMatch::sameOpComplement(PcodeOp *bin1op,PcodeOp *bin2op) { OpCode opcode = bin1op->code(); @@ -256,108 +57,138 @@ bool ConditionMarker::sameOpComplement(PcodeOp *bin1op,PcodeOp *bin2op) return false; } -/// \brief Check if given p-code ops are complements where one is an BOOL_AND and the other is an BOOL_OR +/// \brief Do the given Varnodes hold the same value, possibly as constants /// -/// \param bin1op is the first PcodeOp -/// \param bin2op is the second -/// \return \b true if the p-code ops produce complementary values -bool ConditionMarker::andOrComplement(PcodeOp *bin1op,PcodeOp *bin2op) +/// \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 (bin1op->code() == CPUI_BOOL_AND) { - if (bin2op->code() != CPUI_BOOL_OR) return false; - } - else if (bin1op->code() == CPUI_BOOL_OR) { - if (bin2op->code() != CPUI_BOOL_AND) return false; - } - else - return false; - - // Reaching here, one is AND and one is OR - if (varnodeComplement( bin1op->getIn(0), bin2op->getIn(0))) { - if (varnodeComplement( bin1op->getIn(1), bin2op->getIn(1))) - return true; - } - else if (varnodeComplement( bin1op->getIn(0), bin2op->getIn(1))) { - if (varnodeComplement( bin1op->getIn(1), bin2op->getIn(0))) - return true; - } + if (a == b) return true; + if (a->isConstant() && b->isConstant()) + return (a->getOffset() == b->getOffset()); return false; } -/// \brief Determine if the two boolean expressions always produce the same or complementary values +/// \brief Determine if two boolean Varnodes hold related values /// -/// A common Varnode in the two expressions is given. If the boolean expressions are -/// uncorrelated, \b false is returned, otherwise \b true is returned. If the expressions -/// are correlated but always hold opposite values, the field \b matchflip is set to \b true. -/// \param vn is the common Varnode -/// \return \b true if the expressions are correlated -bool ConditionMarker::finalJudgement(Varnode *vn) +/// 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 (initop->isBooleanFlip()) - matchflip = !matchflip; - if ((vn == basevn)&&(!binon)) // No binary operation involved - return true; - if (boolvn != (Varnode *)0) - matchflip = !matchflip; - if ((vn == boolvn)&&(!binon)) // Negations involved - return true; - if ((binaryop == (PcodeOp *)0)||(!binon)) - return false; // Conditions don't match - - // Both conditions used binary op - PcodeOp *binary2op = (PcodeOp *)0; - for(int4 i=0;iisBoolOutput()) break; - } - // Check if the binary ops are exactly the same - if (binaryop->code() == binary2op->code()) { - if (varnodeSame(binaryop->getIn(0),binary2op->getIn(0)) && - varnodeSame(binaryop->getIn(1),binary2op->getIn(1))) - return true; - if (sameOpComplement(binaryop,binary2op)) { - matchflip = !matchflip; - return true; + 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; } - return false; } - // If not, check if the binary ops are complements of one another - matchflip = !matchflip; - if (andOrComplement(binaryop,binary2op)) - return true; - int4 slot1 = 0; - int4 slot2 = 0; - bool reorder; - if (binaryop->code() != get_booleanflip(binary2op->code(),reorder)) - return false; - if (reorder) slot2 = 1; - if (!varnodeSame(binaryop->getIn(slot1),binary2op->getIn(slot2))) - return false; - if (!varnodeSame(binaryop->getIn(1-slot1),binary2op->getIn(1-slot2))) - return false; - return true; + 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 ConditionMarker::verifyCondition(PcodeOp *op,PcodeOp *iop) +bool BooleanExpressionMatch::verifyCondition(PcodeOp *op, PcodeOp *iop) { - setupInitOp(iop); - Varnode *matchvn = findMatch(op); - if (matchvn == (Varnode *)0) return false; - if (!finalJudgement(matchvn)) return false; - - // Make final determination of what MULTIEQUAL slot is used - if (!multion) - multislot = -1; - else { - for(int4 i=0;icode()==CPUI_MULTIEQUAL) { - multislot = slotstate[i]; - break; - } - } + int4 res = evaluate(op->getIn(1), iop->getIn(1), maxDepth); + if (res == uncorrelated) + return false; + matchflip = (res == complementary); + if (op->isBooleanFlip()) + matchflip = !matchflip; + if (iop->isBooleanFlip()) + matchflip = !matchflip; return true; } @@ -428,7 +259,7 @@ bool ConditionalExecution::verifySameCondition(void) if (init_cbranch == (PcodeOp *)0) return false; if (init_cbranch->code() != CPUI_CBRANCH) return false; - ConditionMarker tester; + BooleanExpressionMatch tester; if (!tester.verifyCondition(cbranch,init_cbranch)) return false; @@ -1057,7 +888,7 @@ int4 RuleOrPredicate::applyOp(PcodeOp *op,Funcdata &data) if (branch0.zeroBlock == branch1.zeroBlock) return 0; // zero sets must be along different paths } else { // Make sure cbranches have shared condition and the different zero sets have complementary paths - ConditionMarker condmarker; + BooleanExpressionMatch condmarker; if (!condmarker.verifyCondition(branch0.cbranch,branch1.cbranch)) return 0; if (condmarker.getMultiSlot() != -1) return 0; branch0.discoverPathIsTrue(); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/condexe.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/condexe.hh index a800756e7c..873eff41f0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/condexe.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/condexe.hh @@ -27,41 +27,24 @@ 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. /// -/// The expression computing the root boolean value for one CBRANCH is marked out -/// by setupInitOp(). For the other CBRANCH, findMatch() tries to find common Varnode -/// in its boolean expression and then maps a critical path from the Varnode to the final boolean. -/// Assuming the common Varnode exists, the method finalJudgement() decides if the two boolean values -/// are the same, uncorrelated, or complements of one another. -class ConditionMarker { - PcodeOp *initop; ///< The root CBRANCH operation to compare against - Varnode *basevn; ///< The boolean Varnode on which the root CBRANCH keys - Varnode *boolvn; ///< If \b basevn is defined by BOOL_NEGATE, this is the unnegated Varnode - Varnode *bool2vn; ///< If the first param to \b binaryop is defined by BOOL_NEGATE, this is the unnegated Varnode - Varnode *bool3vn; ///< If the second param to \b binaryop is defined by BOOL_NEGATE, this is the unnegated Varnode - PcodeOp *binaryop; ///< The binary operator producing the root boolean (if non-null) - +/// 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 - int4 state; ///< Depth of critical path - PcodeOp *opstate[2]; ///< p-code operations along the critical path - bool flipstate[2]; ///< Boolean negation along the critical path - int4 slotstate[2]; ///< Input Varnode to follow to stay on critical path - bool multion; ///< True if MULTIEQUAL used in condition - bool binon; ///< True if a binary operator is used in condition - int4 multislot; ///< Input slot of MULTIEQUAL on critical path, -1 if no MULTIEQUAL - - void setupInitOp(PcodeOp *op); ///< Map out the root boolean expression - Varnode *findMatch(PcodeOp *op); ///< Find a matching Varnode in the root expression producing the given CBRANCH boolean - bool sameOpComplement(PcodeOp *bin1op, PcodeOp *bin2op); - bool andOrComplement(PcodeOp *bin1op, PcodeOp *bin2op); - bool finalJudgement(Varnode *vn); -public: - ConditionMarker(void); ///< Constructor - ~ConditionMarker(void); ///< Destructor - bool verifyCondition(PcodeOp *op, PcodeOp *iop); ///< Perform the correlation test on two CBRANCH operations - int4 getMultiSlot(void) const { return multislot; } ///< Get the MULTIEQUAL slot in the critical path - bool getFlip(void) const { return matchflip; } ///< Return \b true is the expressions are anti-correlated + static bool sameOpComplement(PcodeOp *bin1op, PcodeOp *bin2op); static bool varnodeSame(Varnode *a,Varnode *b); - static bool varnodeComplement(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 + bool getFlip(void) const { return matchflip; } ///< Return \b true if the expressions are anti-correlated }; /// \brief A class for simplifying a series of conditionally executed statements.