From 5f927f3208f051418fe3eeaf954db4679e26cb1e Mon Sep 17 00:00:00 2001
From: cmorin6 <cedric.morin6@gmail.com>
Date: Tue, 20 Oct 2020 20:30:39 +0200
Subject: [PATCH] Updated ruleBlockIfNoExit to better select branch if both are
 noexit.

---
 .../Decompiler/src/decompile/cpp/block.cc     | 46 ++++++++++++++++
 .../Decompiler/src/decompile/cpp/block.hh     |  7 +++
 .../src/decompile/cpp/blockaction.cc          | 54 ++++++++++++++++---
 .../src/decompile/cpp/blockaction.hh          |  1 +
 .../Decompiler/src/decompile/cpp/op.hh        |  2 +
 5 files changed, 104 insertions(+), 6 deletions(-)

diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc
index 9b6d8d87e57..6ae384074b7 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc
@@ -1323,6 +1323,20 @@ void BlockGraph::restoreXmlBody(List::const_iterator &iter,List::const_iterator
   }
 }
 
+int4 BlockGraph::getInnerBlockDepth(void)
+
+{
+  int4 depth;
+  int4 maxDepth = 0;
+  for(int4 i=0;i<list.size();++i){
+    depth = list[i]->getBlockDepth();
+    if(depth>maxDepth){
+	maxDepth=depth;
+    }
+  }
+  return maxDepth;
+}
+
 /// This is currently just a wrapper around the FlowBlock::restoreXml()
 /// that sets of the BlockMap resolver
 /// \param el is the root \<block> tag
@@ -2505,6 +2519,12 @@ bool BlockBasic::isDoNothing(void) const
   return hasOnlyMarkers();
 }
 
+int4 BlockBasic::getOpSize(void)
+
+{
+  return op.size();
+}
+
 /// In terms of machine instructions, a basic block always covers a range of addresses,
 /// from its first instruction to its last. This method establishes that range.
 /// \param beg is the address of the first instruction in the block
@@ -2747,6 +2767,13 @@ FlowBlock *BlockList::getSplitPoint(void)
   return getBlock(getSize()-1)->getSplitPoint();
 }
 
+int4 BlockList::getBlockDepth(void)
+
+{
+  // list join block together but don't increase block depth
+  return getInnerBlockDepth();
+}
+
 void BlockList::printHeader(ostream &s) const
 
 {
@@ -2831,6 +2858,13 @@ void BlockCondition::saveXmlHeader(ostream &s) const
   a_v(s,"opcode",nm);
 }
 
+int4 BlockCondition::getBlockDepth(void)
+
+{
+  // conditions join block together but don't increase block depth
+  return getInnerBlockDepth();
+}
+
 void BlockIf::markUnstructured(void)
 
 {
@@ -3185,6 +3219,18 @@ FlowBlock *BlockSwitch::nextFlowAfter(const FlowBlock *bl) const
   return getParent()->nextFlowAfter(this);
 }
 
+int4 BlockSwitch::getBlockDepth(void){
+  int4 i;
+  int4 maxDepth=0;
+  for(i=0;i<caseblocks.size();++i){
+      int4 depth=caseblocks[i].block->getBlockDepth();
+      if(depth>maxDepth){
+	  maxDepth=depth;
+      }
+  }
+  return maxDepth+2; // +1 for switch block and +1 for case/default block
+}
+
 BlockMap::BlockMap(const BlockMap &op2)
 
 {
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/block.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/block.hh
index 30f867a35ef..401c97dfedd 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/block.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/block.hh
@@ -181,6 +181,7 @@ public:
   /// \param enditer marks the end of the XML tags
   /// \param resolver is used to recover FlowBlock objects based on XML references
   virtual void restoreXmlBody(List::const_iterator &iter,List::const_iterator enditer,BlockMap &resolver) {}
+  virtual int4 getBlockDepth(void) {return 0;}		///< Return the depth in code block of \b this
   void saveXmlEdges(ostream &s) const;			///< Save edge information to an XML stream
   void restoreXmlEdges(List::const_iterator &iter,List::const_iterator enditer,BlockMap &resolver);
   void saveXml(ostream &s) const;			///< Write out \b this to an XML stream
@@ -299,6 +300,8 @@ public:
   virtual void finalizePrinting(const Funcdata &data) const;
   virtual void saveXmlBody(ostream &s) const;
   virtual void restoreXmlBody(List::const_iterator &iter,List::const_iterator enditer,BlockMap &resolver);
+  virtual int4 getInnerBlockDepth();					///< Return max depth of child blocks
+  virtual int4 getBlockDepth() {return getInnerBlockDepth()+1;}
   void restoreXml(const Element *el,const AddrSpaceManager *m);	///< Restore \b this BlockGraph from an XML stream
   void addEdge(FlowBlock *begin,FlowBlock *end);		///< Add a directed edge between component FlowBlocks
   void addLoopEdge(FlowBlock *begin,int4 outindex);		///< Mark a given edge as a \e loop edge
@@ -401,6 +404,7 @@ public:
   list<PcodeOp *>::const_iterator beginOp(void) const { return op.begin(); }	///< Return an iterator to the beginning of the PcodeOps
   list<PcodeOp *>::const_iterator endOp(void) const { return op.end(); }	///< Return an iterator to the end of the PcodeOps
   bool emptyOp(void) const { return op.empty(); }		///< Return \b true if \b block contains no operations
+  int4 getOpSize(void);					///< Number of PcodeOps contained in \b this block
   static bool noInterveningStatement(PcodeOp *first,int4 path,PcodeOp *last);
 };
 
@@ -501,6 +505,7 @@ public:
   virtual PcodeOp *lastOp(void) const;
   virtual bool negateCondition(bool toporbottom);
   virtual FlowBlock *getSplitPoint(void);
+  virtual int4 getBlockDepth(void);
 };
 
 /// \brief Two conditional blocks combined into one conditional using BOOL_AND or BOOL_OR
@@ -530,6 +535,7 @@ public:
   virtual bool isComplex(void) const { return getBlock(0)->isComplex(); }
   virtual FlowBlock *nextFlowAfter(const FlowBlock *bl) const;
   virtual void saveXmlHeader(ostream &s) const;
+  virtual int4 getBlockDepth(void);
 };
 
 /// \brief A basic "if" block
@@ -675,6 +681,7 @@ public:
   virtual void emit(PrintLanguage *lng) const { lng->emitBlockSwitch(this); }
   virtual FlowBlock *nextFlowAfter(const FlowBlock *bl) const;
   virtual void finalizePrinting(const Funcdata &data) const;
+  virtual int4 getBlockDepth(void);
 };
 
 /// \brief Helper class for resolving cross-references while deserializing BlockGraph objects
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/blockaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/blockaction.cc
index 86489e4e945..9c3aaa0b43a 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/blockaction.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/blockaction.cc
@@ -1463,6 +1463,7 @@ bool CollapseStructure::ruleBlockIfNoExit(FlowBlock *bl)
 {
   FlowBlock *clauseblock;
   int4 i;
+  int4 bestIndex=-1;
 
   if (bl->sizeOut() != 2) return false; // Must be binary condition
   if (bl->isSwitchOut()) return false;
@@ -1480,15 +1481,56 @@ bool CollapseStructure::ruleBlockIfNoExit(FlowBlock *bl)
     //      bl->setGotoBranch(i);
     //      return true;
     //    }
+    if (bestIndex==-1){
+	bestIndex=i;
+    }else{ // both match
+	bestIndex = selectBestNoExit(bl->getOut(0),bl->getOut(1));
+    }
+  }
+  if(bestIndex==-1) return false; // no match
+  clauseblock = bl->getOut(bestIndex);
+  if (bestIndex==0) {			// clause must be true out of bl
+    if (bl->negateCondition(true))
+      dataflow_changecount += 1;
+  }
+  graph.newBlockIf(bl,clauseblock);
+  return true;
+}
 
-    if (i==0) {			// clause must be true out of bl
-      if (bl->negateCondition(true))
-	dataflow_changecount += 1;
+/// Select the best of two NoExit branch to be collapsed by ruleBlockIfNoExit.
+/// \param clause0 is the first NoExit branch
+/// \param clause1 is the second NoExit branch
+/// \return the index of the selected branch (0 or 1)
+int4 CollapseStructure::selectBestNoExit(FlowBlock *clause0,FlowBlock *clause1)
+
+{
+  // select lowest block depth
+  int4 depth0 = clause0->getBlockDepth();
+  int4 depth1 = clause1->getBlockDepth();
+  if (depth0<depth1)return 0;
+  if (depth1<depth0)return 1;
+
+  // same depth, prefer non return
+  bool isRet0 = clause0->lastOp()!=(PcodeOp *)0 && clause0->lastOp()->isStandardReturn();
+  bool isRet1 = clause1->lastOp()!=(PcodeOp *)0 && clause1->lastOp()->isStandardReturn();
+  if(isRet0 && !isRet1) return 1;
+  if(isRet1 && !isRet0) return 0;
+
+  // prefer block containing only return op
+  if(isRet0){ // both are return
+    FlowBlock* fb;
+    if(clause0->getType()==FlowBlock::t_copy){
+      fb = ((BlockCopy*)clause0)->subBlock(0);
+      if(fb->getType()==FlowBlock::t_basic && ((BlockBasic*)fb)->getOpSize()==1) return 0;
+    }
+    if(clause1->getType()==FlowBlock::t_copy){
+      fb = ((BlockCopy*)clause1)->subBlock(0);
+      if(fb->getType()==FlowBlock::t_basic && ((BlockBasic*)fb)->getOpSize()==1) return 1;
     }
-    graph.newBlockIf(bl,clauseblock);
-    return true;
   }
-  return false;
+
+  // fall back to previous behavior
+  return 0;
 }
 
 /// Try to find a while/do structure, starting with a given FlowBlock.
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/blockaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/blockaction.hh
index abb04b91bb6..2e51503808e 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/blockaction.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/blockaction.hh
@@ -217,6 +217,7 @@ class CollapseStructure {
   bool ruleCaseFallthru(FlowBlock *bl);		///< Attempt to one switch case falling through to another
   int4 collapseInternal(FlowBlock *targetbl);	///< The main collapsing loop
   void collapseConditions(void);		///< Simplify conditionals
+  int4 selectBestNoExit(FlowBlock *clause0,FlowBlock *clause1);
 public:
   CollapseStructure(BlockGraph &g);		///< Construct given a control-flow graph
   int4 getChangeCount(void) const { return dataflow_changecount; }	///< Get number of data-flow changes
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh
index ed915d66c83..3fcbe3dbb9d 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh
@@ -176,6 +176,8 @@ public:
   bool isCallOrBranch(void) const { return ((flags&(PcodeOp::branch|PcodeOp::call))!=0); }
   /// \brief Return \b true if this op breaks fall-thru flow
   bool isFlowBreak(void) const { return ((flags&(PcodeOp::branch|PcodeOp::returns))!=0); }
+  /// \brief Return \b true if this op will be decompiled as "return"
+  bool isStandardReturn(void) const { return ((flags&PcodeOp::returns)!=0 && getHaltType()==0); }
   /// \brief Return \b true if this op flips the true/false meaning of its control-flow branching
   bool isBooleanFlip(void) const { return ((flags&PcodeOp::boolean_flip)!=0); }
   /// \brief Return \b true if the fall-thru branch is taken when the boolean input is true