Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions Ghidra/Features/Decompiler/src/decompile/cpp/block.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Comment on lines +1326 to +1338

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The implementation of this loop can be made more concise and readable by using a range-based for loop. Also, the indentation for maxDepth=depth; seems to be off (using a tab instead of spaces), which affects code consistency. Declaring depth inside the loop scope is also better practice.

int4 BlockGraph::getInnerBlockDepth(void)
{
  int4 maxDepth = 0;
  for (const FlowBlock *block : list) {
    const int4 depth = block->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
Expand Down Expand Up @@ -2505,6 +2519,12 @@ bool BlockBasic::isDoNothing(void) const
return hasOnlyMarkers();
}

int4 BlockBasic::getOpSize(void)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The implementation of getOpSize should be marked as const to match the declaration in the header file. This method does not modify the object's state.

int4 BlockBasic::getOpSize(void) const


{
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
Expand Down Expand Up @@ -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

{
Expand Down Expand Up @@ -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)

{
Expand Down Expand Up @@ -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
}
Comment on lines +3222 to +3232

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This function's implementation can be improved for readability and consistency.

  • The opening brace { is on the same line as the function signature, which is inconsistent with the surrounding code style.
  • A range-based for loop would be more modern and readable than a C-style for loop.
  • The indentation for maxDepth=depth; appears to be incorrect (using a tab).
  • The variable i is declared at the top of the function but only used in the loop.
int4 BlockSwitch::getBlockDepth(void)
{
  int4 maxDepth = 0;
  for (const auto &caseblock : caseblocks) {
    const int4 depth = caseblock.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)

{
Expand Down
7 changes: 7 additions & 0 deletions Ghidra/Features/Decompiler/src/decompile/cpp/block.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The getOpSize method does not modify the state of the BlockBasic object. It should be declared as const to enforce this and allow it to be called on const objects. This improves const-correctness and can prevent potential issues elsewhere.

  int4 getOpSize(void) const;                                       ///< Number of PcodeOps contained in \b this block

static bool noInterveningStatement(PcodeOp *first,int4 path,PcodeOp *last);
};

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
54 changes: 48 additions & 6 deletions Ghidra/Features/Decompiler/src/decompile/cpp/blockaction.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Comment on lines +1514 to +1515

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better code clarity and to adhere to modern C++ standards, it's recommended to use nullptr instead of the C-style (PcodeOp *)0 for null pointer checks.

  bool isRet0 = clause0->lastOp() != nullptr && clause0->lastOp()->isStandardReturn();
  bool isRet1 = clause1->lastOp() != nullptr && 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;
}
Comment on lines +1521 to 1529

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This block of code can be improved for safety and clarity:

  1. Use static_cast instead of C-style casts. This is safer and more explicit about the cast's intent.
  2. The fb variable is declared once and reused. It's better practice to declare variables in the narrowest scope possible.

Additionally, BlockBasic::getOpSize() should be a const method, as it doesn't modify the object's state. I've added separate comments for this.

    if(clause0->getType()==FlowBlock::t_copy){
      FlowBlock* fb = static_cast<BlockCopy*>(clause0)->subBlock(0);
      if(fb->getType()==FlowBlock::t_basic && static_cast<BlockBasic*>(fb)->getOpSize()==1) return 0;
    }
    if(clause1->getType()==FlowBlock::t_copy){
      FlowBlock* fb = static_cast<BlockCopy*>(clause1)->subBlock(0);
      if(fb->getType()==FlowBlock::t_basic && static_cast<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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions Ghidra/Features/Decompiler/src/decompile/cpp/op.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down