diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index 5df22c5028..47613d697c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -3579,6 +3579,7 @@ bool ActionCopyMarker::shadowedVarnode(const Varnode *vn) int4 ActionCopyMarker::apply(Funcdata &data) { + vector multiCopy; list::const_iterator iter; PcodeOp *op; HighVariable *h1,*h2,*h3; @@ -3596,6 +3597,12 @@ int4 ActionCopyMarker::apply(Funcdata &data) count += 1; } else { // COPY between different HighVariables + if (!h1->hasCopyIn1()) { // If this is the first COPY we've seen for this high + h1->setCopyIn1(); // Mark it + multiCopy.push_back(h1); + } + else + h1->setCopyIn2(); // This is at least the second COPY we've seen if (v1->hasNoDescend()) { // Don't print shadow assignments if (shadowedVarnode(v1)) { data.opSetFlag(op, PcodeOp::nonprinting); @@ -3635,6 +3642,12 @@ int4 ActionCopyMarker::apply(Funcdata &data) break; } } + for(int4 i=0;ihasCopyIn2()) + data.getMerge().processHighRedundantCopy(high); + high->clearCopyIns(); + } return 0; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc index 4135337144..93a07147ff 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc @@ -959,6 +959,19 @@ bool Merge::checkCopyPair(HighVariable *high,PcodeOp *domOp,PcodeOp *subOp) return true; } +/// \brief Try to replace a set of COPYs from the same Varnode with a single dominant COPY +/// +/// All the COPY outputs must be instances of the same HighVariable (not the same Varnode). +/// Either an existing COPY dominates all the others, or a new dominating COPY is constructed. +/// The read locations of all other COPY outputs are replaced with the output of the dominating +/// COPY, if it does not cause intersections in the HighVariable's Cover. Because of +/// intersections, replacement may fail or partially succeed. Replacement only happens with +/// COPY outputs that are temporary registers. The cover of the HighVariable may be extended +/// because of a new COPY output instance. +/// \param high is the HighVariable being copied to +/// \param copy is the list of COPY ops into the HighVariable +/// \param pos is the index of the first COPY from the specific input Varnode +/// \param size is the number of COPYs (in sequence) from the same specific Varnode void Merge::buildDominantCopy(HighVariable *high,vector ©,int4 pos,int4 size) { @@ -1067,24 +1080,60 @@ void Merge::markRedundantCopies(HighVariable *high,vector ©,int4 } } -void Merge::processCopyTrimsForHigh(HighVariable *high) +/// \brief Find all the COPY ops into the given HighVariable +/// +/// Collect all the COPYs whose output is the given HighVariable but +/// the input is from a different HighVariable. Returned COPYs are sorted +/// first by the input Varnode then by block order. +/// \param high is the given HighVariable +/// \param copyIns will hold the list of COPYs +/// \param filterTemps is \b true if COPYs must have a temporary output +void Merge::findAllIntoCopies(HighVariable *high,vector ©Ins,bool filterTemps) { - vector copyIns; - - // Find all the COPY ops into this HighVariable from a different HighVariable for(int4 i=0;inumInstances();++i) { Varnode *vn = high->getInstance(i); if (!vn->isWritten()) continue; PcodeOp *op = vn->getDef(); if (op->code() != CPUI_COPY) continue; if (op->getIn(0)->getHigh() == high) continue; - if (op->getOut()->getSpace()->getType() != IPTR_INTERNAL) continue; + if (filterTemps && op->getOut()->getSpace()->getType() != IPTR_INTERNAL) continue; copyIns.push_back(op); } - - // Group COPYs based on the incoming Varnode + // Group COPYs based on the incoming Varnode then block order sort(copyIns.begin(),copyIns.end(),compareCopyByInVarnode); +} + +void Merge::processHighDominantCopy(HighVariable *high) + +{ + vector copyIns; + + findAllIntoCopies(high,copyIns,true); // Get all COPYs into this with temporary output + if (copyIns.size() < 2) return; + int4 pos = 0; + while(pos < copyIns.size()) { + // Find a group of COPYs coming from the same Varnode + Varnode *inVn = copyIns[pos]->getIn(0); + int4 sz = 1; + while(pos + sz < copyIns.size()) { + Varnode *nextVn = copyIns[pos+sz]->getIn(0); + if (nextVn != inVn) break; + sz += 1; + } + if (sz > 1) // If there is more than one COPY in a group + buildDominantCopy(high, copyIns, pos, sz); // Try to construct a dominant COPY + pos += sz; + } +} + +void Merge::processHighRedundantCopy(HighVariable *high) + +{ + vector copyIns; + + findAllIntoCopies(high,copyIns,false); + if (copyIns.size() < 2) return; int4 pos = 0; while(pos < copyIns.size()) { // Find a group of COPYs coming from the same Varnode @@ -1096,7 +1145,6 @@ void Merge::processCopyTrimsForHigh(HighVariable *high) sz += 1; } if (sz > 1) { // If there is more than one COPY in a group - buildDominantCopy(high, copyIns, pos, sz); markRedundantCopies(high, copyIns, pos, sz); } pos += sz; @@ -1110,18 +1158,20 @@ void Merge::processCopyTrims(void) for(int4 i=0;igetOut()->getHigh(); - if (high->hasCopyIn()) { // If we've seen COPYs into this high before - if (!high->isCopyProcessed()) { // and we haven't processed it before, - multiCopy.push_back(high); // slate the high for copy trim processing - high->setCopyProcessed(); - } + if (!high->hasCopyIn1()) { + multiCopy.push_back(high); + high->setCopyIn1(); } else - high->setCopyIn(); + high->setCopyIn2(); } copyTrims.clear(); - for(int4 i=0;ihasCopyIn2()) // If the high has at least 2 COPYs into it + processHighDominantCopy(high); // Try to replace with a dominant copy + high->clearCopyIns(); + } } /// \brief Perform low-level details of merging two HighVariables if possible diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.hh index 876aa3feb8..cc00166acb 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.hh @@ -91,6 +91,7 @@ class Merge { static void findSingleCopy(HighVariable *high,vector &singlelist); static bool compareHighByBlock(const HighVariable *a,const HighVariable *b); static bool compareCopyByInVarnode(PcodeOp *op1,PcodeOp *op2); + 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); @@ -109,7 +110,7 @@ class Merge { bool checkCopyPair(HighVariable *high,PcodeOp *domOp,PcodeOp *subOp); void buildDominantCopy(HighVariable *high,vector ©,int4 pos,int4 size); void markRedundantCopies(HighVariable *high,vector ©,int4 pos,int4 size); - void processCopyTrimsForHigh(HighVariable *high); + void processHighDominantCopy(HighVariable *high); public: Merge(Funcdata &fd) : data(fd) {} ///< Construct given a specific function bool intersection(HighVariable *a,HighVariable *b); @@ -124,6 +125,7 @@ public: void mergeAdjacent(void); bool hideShadows(HighVariable *high); void processCopyTrims(void); + void processHighRedundantCopy(HighVariable *high); }; /// \brief Compare HighVariables by the blocks they cover diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.hh index fec0f1f601..a999666a72 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.hh @@ -46,8 +46,8 @@ public: flagsdirty = 1, ///< Boolean properties for the HighVariable are dirty typedirty = 2, ///< The data-type for the HighVariable is dirty coverdirty = 4, ///< The cover for the HighVariable is dirty - copy_in = 8, ///< There exist COPY ops into \b this HighVariable from other HighVariables - copy_processed = 16 ///< COPY ops into \b this HighVariable have been analyzed + copy_in1 = 8, ///< There exists at least 1 COPY into \b this HighVariable from other HighVariables + copy_in2 = 16 ///< There exists at least 2 COPYs into \b this HighVariable from other HighVariables }; private: friend class Merge; @@ -63,10 +63,6 @@ private: void updateFlags(void) const; ///< (Re)derive boolean properties of \b this from the member Varnodes void updateCover(void) const; ///< (Re)derive the cover of \b this from the member Varnodes void updateType(void) const; ///< (Re)derive the data-type for \b this from the member Varnodes - void setCopyIn(void) const { highflags |= copy_in; } ///< Mark the existence of COPY ops into \b this - bool hasCopyIn(void) const { return ((highflags©_in)!=0); } ///< Are there COPY ops into \b this - bool isCopyProcessed(void) const { return ((highflags©_processed)!=0); } ///< Have COPY ops into \b this been processed - void setCopyProcessed(void) const { highflags |= copy_processed; } ///< Mark that \b this has had its COPY ins processed public: HighVariable(Varnode *vn); ///< Construct a HighVariable with a single member Varnode Datatype *getType(void) const { updateType(); return type; } ///< Get the data-type @@ -114,6 +110,11 @@ public: void setMark(void) const { flags |= Varnode::mark; } ///< Set the mark on this variable void clearMark(void) const { flags &= ~Varnode::mark; } ///< Clear the mark on this variable bool isMark(void) const { return ((flags&Varnode::mark)!=0); } ///< Return \b true if \b this is marked + void setCopyIn1(void) const { highflags |= copy_in1; } ///< Mark the existence of one COPY into \b this + void setCopyIn2(void) const { highflags |= copy_in2; } ///< Mark the existence of two COPYs into \b this + void clearCopyIns(void) const { highflags &= ~(copy_in1 | copy_in2); } ///< Clear marks indicating COPYs into \b this + bool hasCopyIn1(void) const { return ((highflags©_in1)!=0); } ///< Is there at least one COPY into \b this + bool hasCopyIn2(void) const { return ((highflags©_in2)!=0); } ///< Is there at least two COPYs into \b this /// \brief Determine if \b this HighVariable has an associated cover. ///