diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc index 0df7554e03..e5b9d9f6aa 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc @@ -771,103 +771,71 @@ void Merge::mergeOp(PcodeOp *op) } } -/// \brief Collect all instances of the given HighVariable whose Cover intersects a p-code op +/// \brief Collect Varnode instances or pieces from a specific HighVariable that are inputs to a given PcodeOp /// -/// Efficiently test if each instance Varnodes contains the specific p-code op in its Cover -/// and return a list of the instances that do. -/// \param vlist will hold the resulting list of intersecting instances -/// \param high is the given HighVariable -/// \param op is the specific PcodeOp to test intersection with -void Merge::collectCovering(vector &vlist,HighVariable *high,PcodeOp *op) +/// A Varnode is considered an input if it is a \e direct input to the PcodeOp or if it is +/// \e indirectly affected by the PcodeOp. The specific \e read of the Varnode is passed back as +/// a PcodeOp and slot pair (PcodeOpNode). The passed back PcodeOp will either be the given PcodeOp or +/// an INDIRECT caused by the given PcodeOp. +/// \param high is the specific HighVariable through which to search for input instances +/// \param oplist will hold the PcodeOpNodes being passed back +/// \param op is the given PcodeOp +void Merge::collectInputs(HighVariable *high,vector &oplist,PcodeOp *op) { - int4 blk = op->getParent()->getIndex(); - for(int4 i=0;inumInstances();++i) { - Varnode *vn = high->getInstance(i); - if (vn->getCover()->getCoverBlock(blk).contain(op)) - vlist.push_back(vn); - } -} - -/// \brief Check for for p-code op intersections that are correctable -/// -/// Given a list of Varnodes that intersect a specific PcodeOp, check that each intersection is -/// on the boundary, and if so, pass back the \e read op(s) that cause the intersection. -/// \param vlist is the given list of intersecting Varnodes -/// \param oplist will hold the boundary intersecting \e read ops -/// \param slotlist will hold the corresponding input slots of the instance -/// \param op is the specific intersecting PcodeOp -/// \return \b false if any instance in the list intersects the PcodeOp on the interior -bool Merge::collectCorrectable(const vector &vlist,list &oplist, - vector &slotlist,PcodeOp *op) -{ - int4 blk = op->getParent()->getIndex(); - vector::const_iterator viter; - list::const_iterator oiter; - Varnode *vn; - PcodeOp *edgeop; - int4 slot,bound; - uintm opuindex = CoverBlock::getUIndex(op); - - for(viter=vlist.begin();viter!=vlist.end();++viter) { - vn = *viter; - bound = vn->getCover()->getCoverBlock(blk).boundary(op); - if (bound == 0) return false; - if (bound == 2) continue; // Not defined before op (intersects with write op) - for(oiter=vn->beginDescend();oiter!=vn->endDescend();++oiter) { - edgeop = *oiter; - if (CoverBlock::getUIndex(edgeop) == opuindex) { // Correctable - oplist.push_back(edgeop); - slot = edgeop->getSlot(vn); - slotlist.push_back(slot); + VariableGroup *group = (VariableGroup *)0; + if (high->piece != (VariablePiece *)0) + group = high->piece->getGroup(); + for(;;) { + for(int4 i=0;inumInput();++i) { + Varnode *vn = op->getIn(i); + if (vn->isAnnotation()) continue; + HighVariable *testHigh = vn->getHigh(); + if (testHigh == high || (testHigh->piece != (VariablePiece *)0 && testHigh->piece->getGroup() == group)) { + oplist.emplace_back(op, i); } } + op = op->previousOp(); + if (op == (PcodeOp *)0 || op->code() != CPUI_INDIRECT) + break; } - return true; } -/// \brief Snip instances of the input of an INDIRECT op that interfere with its output +/// \brief Snip instances of the output of an INDIRECT that are also inputs to to the underlying PcodeOp /// -/// Examine the input and output HighVariable for the given INDIRECT op. -/// Varnode instances of the input that intersect the output Cover are snipped by creating -/// a new COPY op from the input to a new temporary and then replacing the Varnode reads -/// with the temporary. +/// Examine the output HighVariable for the given INDIRECT op. Varnode instances (or pieces) that are also +/// inputs to the underlying PcodeOp causing the INDIRECT are snipped by creating a new COPY op from the +/// Varnode to a new temporary and then replacing the \e read with the temporary. /// \param indop is the given INDIRECT op -void Merge::snipIndirect(PcodeOp *indop) +/// \return \b true if specific instances are snipped +bool Merge::snipOutputInterference(PcodeOp *indop) { PcodeOp *op = PcodeOp::getOpFromConst(indop->getIn(1)->getAddr()); // Indirect effect op - vector problemvn; - list correctable; - vector correctslot; // Collect instances of output->high that are defined // before (and right up to) op. These need to be snipped. - collectCovering(problemvn,indop->getOut()->getHigh(),op); - if (problemvn.empty()) return; - // Collect vn reads where the snip needs to be. - // If cover properly contains op, report an error. - // This should not be possible as that vn would have - // to intersect with indop->output, which it is merged with. - if (!collectCorrectable(problemvn,correctable,correctslot,op)) - throw LowlevelError("Unable to force indirect merge"); + vector correctable; + collectInputs(indop->getOut()->getHigh(), correctable, op); + if (correctable.empty()) + return false; - if (correctable.empty()) return; - Varnode *refvn = correctable.front()->getIn(correctslot[0]); - PcodeOp *snipop,*insertop; - - // NOTE: the covers for any input to op which is - // an instance of the output high must - // all intersect so the varnodes must all be - // traceable via COPY to the same root - snipop = allocateCopyTrim(refvn, op->getAddr(), correctable.front()); - data.opInsertBefore(snipop,op); - list::iterator oiter; - int4 i,slot; - for(oiter=correctable.begin(),i=0;igetIn(slot); + if (vn->getHigh() != curHigh) { + // NOTE: the covers for any input to op which is an instance of the output high must + // all intersect so the varnodes must all be traceable via COPY to the same root + snipop = allocateCopyTrim(vn, insertop->getAddr(), insertop); + data.opInsertBefore(snipop,insertop); + curHigh = vn->getHigh(); + } data.opSetInput(insertop,snipop->getOut(),slot); } + return true; } /// \brief Force the merge of all input and output Varnodes to a given INDIRECT op @@ -879,24 +847,28 @@ void Merge::mergeIndirect(PcodeOp *indop) { Varnode *outvn = indop->getOut(); - Varnode *invn0 = indop->getIn(0); if (!outvn->isAddrForce()) { // If the output is NOT address forced mergeOp(indop); // We can merge in the same way as a MULTIEQUAL return; } + Varnode *invn0 = indop->getIn(0); if (mergeTestRequired(outvn->getHigh(),invn0->getHigh())) { if (merge(invn0->getHigh(),outvn->getHigh(),false)) return; } - snipIndirect(indop); // If we cannot merge, the only thing that can go - // wrong with an input trim, is if the output of - // indop is involved in the input to the op causing - // the indirect effect. So fix this + // If we cannot merge, the only thing that can go wrong with an input trim, is if the output of + // indop is involved in the input to the op causing the indirect effect. So test for this. + if (snipOutputInterference(indop)) { + // If we found (and snipped) something related to the output, try merging again before snipping the INDIRECT + if (mergeTestRequired(outvn->getHigh(), invn0->getHigh())) { + if (merge(invn0->getHigh(),outvn->getHigh(),false)) + return; + } + } - PcodeOp *newop; - - newop = allocateCopyTrim(invn0, indop->getAddr(), indop); + // Snip the INDIRECT itself + PcodeOp *newop = allocateCopyTrim(invn0, indop->getAddr(), indop); SymbolEntry *entry = outvn->getSymbolEntry(); if (entry != (SymbolEntry *)0 && entry->getSymbol()->getType()->needsResolution()) { data.inheritResolution(entry->getSymbol()->getType(), newop, -1, indop, -1); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.hh index 24dbb69d1b..aeb1a84092 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.hh @@ -95,12 +95,10 @@ class Merge { static bool compareCopyByInVarnode(PcodeOp *op1,PcodeOp *op2); static bool shadowedVarnode(const Varnode *vn); static void findAllIntoCopies(HighVariable *high,vector ©Ins,bool filterTemps); - void collectCovering(vector &vlist,HighVariable *high,PcodeOp *op); - bool collectCorrectable(const vector &vlist,list &oplist,vector &slotlist, - PcodeOp *op); + void collectInputs(HighVariable *high,vector &oplist,PcodeOp *op); PcodeOp *allocateCopyTrim(Varnode *inVn,const Address &addr,PcodeOp *trimOp); void snipReads(Varnode *vn,list &markedop); - void snipIndirect(PcodeOp *indop); + bool snipOutputInterference(PcodeOp *indop); void eliminateIntersect(Varnode *vn,const vector &blocksort); void unifyAddress(VarnodeLocSet::const_iterator startiter,VarnodeLocSet::const_iterator enditer); void trimOpOutput(PcodeOp *op); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh index 8ae074c13e..117fe953be 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh @@ -258,6 +258,7 @@ struct PcodeOpNode { PcodeOpNode(void) { op = (PcodeOp *)0; slot = 0; } ///< Unused constructor PcodeOpNode(PcodeOp *o,int4 s) { op = o; slot = s; } ///< Constructor bool operator<(const PcodeOpNode &op2) const; ///< Simple comparator for putting edges in a sorted container + static bool compareByHigh(const PcodeOpNode &a,const PcodeOpNode &b); ///< Compare Varnodes by their HighVariable }; /// \brief A node in a tree structure of CPUI_PIECE operations @@ -376,5 +377,15 @@ inline bool PcodeOpNode::operator<(const PcodeOpNode &op2) const return false; } +/// Allow a sorting that groups together input Varnodes with the same HighVariable +/// \param a is the first Varnode to compare +/// \param b is the second Varnode to compare +/// \return true is \b a should come before \b b +inline bool PcodeOpNode::compareByHigh(const PcodeOpNode &a, const PcodeOpNode &b) + +{ + return a.op->getIn(a.slot)->getHigh() < b.op->getIn(b.slot)->getHigh(); +} + } // End namespace ghidra #endif