ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc
2024-08-30 18:47:34 +00:00

3648 lines
109 KiB
C++

/* ###
* IP: GHIDRA
* NOTE: Cooper, Harvey, Kennedy dominance algorithm
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "block.hh"
#include "funcdata.hh"
namespace ghidra {
AttributeId ATTRIB_ALTINDEX = AttributeId("altindex",75);
AttributeId ATTRIB_DEPTH = AttributeId("depth",76);
AttributeId ATTRIB_END = AttributeId("end",77);
AttributeId ATTRIB_OPCODE = AttributeId("opcode",78);
AttributeId ATTRIB_REV = AttributeId("rev",79);
ElementId ELEM_BHEAD = ElementId("bhead",102);
ElementId ELEM_BLOCK = ElementId("block",103);
ElementId ELEM_BLOCKEDGE = ElementId("blockedge",104);
ElementId ELEM_EDGE = ElementId("edge",105);
/// The edge is saved assuming we already know what block we are in.
/// \param encoder is the stream encoder
void BlockEdge::encode(Encoder &encoder) const
{
encoder.openElement(ELEM_EDGE);
// We are not saving label currently
encoder.writeSignedInteger(ATTRIB_END, point->getIndex()); // Reference to other end of edge
encoder.writeSignedInteger(ATTRIB_REV, reverse_index); // Position within other blocks edgelist
encoder.closeElement(ELEM_EDGE);
}
/// Parse an \<edge> element
/// \param decoder is the stream decoder
/// \param resolver is used to cross-reference the edge's FlowBlock endpoints
void BlockEdge::decode(Decoder &decoder,BlockMap &resolver)
{
uint4 elemId = decoder.openElement(ELEM_EDGE);
label = 0; // Tag does not currently contain info about label
int4 endIndex = decoder.readSignedInteger(ATTRIB_END);
point = resolver.findLevelBlock(endIndex);
if (point == (FlowBlock *)0)
throw LowlevelError("Bad serialized edge in block graph");
reverse_index = decoder.readSignedInteger(ATTRIB_REV);
decoder.closeElement(elemId);
}
FlowBlock::FlowBlock(void)
{
flags = 0;
index = 0;
visitcount = 0;
parent = (FlowBlock *)0;
immed_dom = (FlowBlock *)0;
}
/// \param b is the FlowBlock coming in
/// \param lab is a label for the edge
void FlowBlock::addInEdge(FlowBlock *b,uint4 lab)
{
int4 ourrev = b->outofthis.size();
int4 brev = intothis.size();
intothis.push_back(BlockEdge(b,lab,ourrev));
b->outofthis.push_back(BlockEdge(this,lab,brev));
}
/// Parse the next \<edge> element in the stream
/// \param decoder is the stream decoder
/// \param resolver is used to resolve block references
void FlowBlock::decodeNextInEdge(Decoder &decoder,BlockMap &resolver)
{
intothis.emplace_back();
BlockEdge &inedge(intothis.back());
inedge.decode(decoder,resolver);
while(inedge.point->outofthis.size() <= inedge.reverse_index)
inedge.point->outofthis.emplace_back();
BlockEdge &outedge(inedge.point->outofthis[inedge.reverse_index]);
outedge.label = 0;
outedge.point = this;
outedge.reverse_index = intothis.size()-1;
}
/// \param slot is the index of the incoming edge being altered
void FlowBlock::halfDeleteInEdge(int4 slot)
{
while(slot < intothis.size()-1) {
BlockEdge &edge( intothis[slot] );
edge = intothis[slot+1]; // Slide the edge entry over
// Correct the index coming the other way
BlockEdge &edger( edge.point->outofthis[edge.reverse_index] );
edger.reverse_index -= 1;
slot += 1;
}
intothis.pop_back();
}
/// \param slot is the index of the outgoing edge being altered
void FlowBlock::halfDeleteOutEdge(int4 slot)
{
while(slot < outofthis.size()-1) {
BlockEdge &edge( outofthis[slot] );
edge = outofthis[slot+1]; // Slide the edge
// Correct the index coming the other way
BlockEdge &edger( edge.point->intothis[edge.reverse_index] );
edger.reverse_index -= 1;
slot += 1;
}
outofthis.pop_back();
}
/// \param slot is the index of the incoming edge to remove
void FlowBlock::removeInEdge(int4 slot)
{
FlowBlock *b = intothis[slot].point;
int4 rev = intothis[slot].reverse_index;
halfDeleteInEdge(slot);
b->halfDeleteOutEdge(rev);
#ifdef BLOCKCONSISTENT_DEBUG
checkEdges();
b->checkEdges();
#endif
}
/// \param slot is the index of the outgoing edge to remove
void FlowBlock::removeOutEdge(int4 slot)
{
FlowBlock *b = outofthis[slot].point;
int4 rev = outofthis[slot].reverse_index;
halfDeleteOutEdge(slot);
b->halfDeleteInEdge(rev);
#ifdef BLOCKCONSISTENT_DEBUG
checkEdges();
b->checkEdges();
#endif
}
/// The original edge, which must exist, is replaced.
/// \param num is the index of the incoming edge
/// \param b is the new incoming block
void FlowBlock::replaceInEdge(int4 num,FlowBlock *b)
{
FlowBlock *oldb = intothis[num].point;
oldb->halfDeleteOutEdge(intothis[num].reverse_index);
intothis[num].point = b;
intothis[num].reverse_index = b->outofthis.size();
b->outofthis.push_back(BlockEdge(this,intothis[num].label,num));
#ifdef BLOCKCONSISTENT_DEBUG
checkEdges();
b->checkEdges();
oldb->checkEdges();
#endif
}
/// The original edge, which must exist is replaced.
/// \param num is the index of the outgoing edge
/// \param b is the new outgoing block
void FlowBlock::replaceOutEdge(int4 num,FlowBlock *b)
{
FlowBlock *oldb = outofthis[num].point;
oldb->halfDeleteInEdge(outofthis[num].reverse_index);
outofthis[num].point = b;
outofthis[num].reverse_index = b->intothis.size();
b->intothis.push_back(BlockEdge(this,outofthis[num].label,num));
#ifdef BLOCKCONSISTENT_DEBUG
checkEdges();
b->checkEdges();
oldb->checkEdges();
#endif
}
/// Remove edge \b in and \b out from \b this block, but create
/// a new edge between the in-block and the out-block, preserving
/// position in the in/out edge lists.
/// \param in is the index of the incoming block
/// \param out is the index of the outgoing block
void FlowBlock::replaceEdgesThru(int4 in,int4 out)
{
FlowBlock *inb = intothis[in].point;
int4 inblock_outslot = intothis[in].reverse_index;
FlowBlock *outb = outofthis[out].point;
int4 outblock_inslot = outofthis[out].reverse_index;
inb->outofthis[inblock_outslot].point = outb;
inb->outofthis[inblock_outslot].reverse_index = outblock_inslot;
outb->intothis[outblock_inslot].point = inb;
outb->intothis[outblock_inslot].reverse_index = inblock_outslot;
halfDeleteInEdge(in);
halfDeleteOutEdge(out);
#ifdef BLOCKCONSISTENT_DEBUG
checkEdges();
inb->checkEdges();
outb->checkEdges();
#endif
}
void FlowBlock::swapEdges(void)
{
#ifdef BLOCKCONSISTENT_DEBUG
if (outofthis.size() != 2)
throw LowlevelError("Swapping edges for block that doesn't have two edges");
#endif
BlockEdge tmp = outofthis[0];
outofthis[0] = outofthis[1];
outofthis[1] = tmp;
FlowBlock *bl = outofthis[0].point;
bl->intothis[ outofthis[0].reverse_index ].reverse_index = 0;
bl = outofthis[1].point;
bl->intothis[ outofthis[1].reverse_index ].reverse_index = 1;
flags ^= f_flip_path;
#ifdef BLOCKCONSISTENT_DEBUG
checkEdges();
#endif
}
/// \param i is the index of the outgoing edge
/// \param lab is the new edge label
void FlowBlock::setOutEdgeFlag(int4 i,uint4 lab)
{
FlowBlock *bbout = outofthis[i].point;
outofthis[i].label |= lab;
bbout->intothis[ outofthis[i].reverse_index ].label |= lab;
}
/// \param i is the index of the outgoing edge
/// \param lab is the edge label to remove
void FlowBlock::clearOutEdgeFlag(int4 i,uint4 lab)
{
FlowBlock *bbout = outofthis[i].point;
outofthis[i].label &= ~lab;
bbout->intothis[ outofthis[i].reverse_index ].label &= ~lab;
}
/// \param bump if \b true, mark that labels for this block are printed by somebody higher in hierarchy
void FlowBlock::markLabelBumpUp(bool bump)
{
if (bump)
flags |= f_label_bumpup;
}
/// Block references are updated using the getCopyMap() reference on the original block
/// \param vec is the list of edges whose block references should be updated
void FlowBlock::replaceEdgeMap(vector<BlockEdge> &vec)
{
vector<BlockEdge>::iterator iter;
for(iter=vec.begin();iter!=vec.end();++iter)
(*iter).point = (*iter).point->getCopyMap();
}
/// Run through incoming and outgoing edges and replace FlowBlock references with
/// the FlowBlock accessed via the getCopyMap() method.
void FlowBlock::replaceUsingMap(void)
{
replaceEdgeMap(intothis);
replaceEdgeMap(outofthis);
if (immed_dom != (FlowBlock *)0)
immed_dom = immed_dom->getCopyMap();
}
/// Flip the order of outgoing edges (at least).
/// This should also affect the original op causing the condition.
/// Note: we don't have to flip at all levels of the hierarchy
/// only at the top and at the bottom
/// \param toporbottom is \b true if \b this is the top outermost block of the hierarchy getting negated
/// \return \b true if a change was made to data-flow
bool FlowBlock::negateCondition(bool toporbottom)
{
if (!toporbottom) return false; // No change was made to data-flow
swapEdges();
return false;
}
/// This is the main entry point for marking a branch
/// from one block to another as unstructured.
/// \param i is the index of the outgoing edge to mark
void FlowBlock::setGotoBranch(int4 i)
{ if ((i>=0)&&(i < outofthis.size()))
setOutEdgeFlag(i,f_goto_edge);
else
throw LowlevelError("Could not find block edge to mark unstructured");
flags |= f_interior_gotoout; // Mark that there is a goto out of this block
outofthis[i].point->flags |= f_interior_gotoin;
}
/// The switch can have exactly 1 default edge, so we make sure other edges are not marked.
/// \param pos is the index of the \e out edge that should be the default
void FlowBlock::setDefaultSwitch(int4 pos)
{
for(int4 i=0;i<outofthis.size();++i) {
if (isDefaultBranch(i))
clearOutEdgeFlag(i, f_defaultswitch_edge); // Clear any previous flag
}
setOutEdgeFlag(pos,f_defaultswitch_edge);
}
/// \b return \b true if block is the target of a jump
bool FlowBlock::isJumpTarget(void) const
{
for(int4 i=0;i<intothis.size();++i)
if (intothis[i].point->index != index-1) return true;
return false;
}
/// Keep descending tree hierarchy, taking the front block,
/// until we get to the bottom copy block
/// \return the first leaf FlowBlock to execute
const FlowBlock *FlowBlock::getFrontLeaf(void) const
{
const FlowBlock *bl = this;
while(bl->getType() != t_copy) {
bl = bl->subBlock(0);
if (bl == (const FlowBlock *)0) return bl;
}
return bl;
}
/// Keep descending tree hierarchy, taking the front block,
/// until we get to the bottom copy block
/// \return the first leaf FlowBlock to execute
FlowBlock *FlowBlock::getFrontLeaf(void)
{
FlowBlock *bl = this;
while(bl->getType() != t_copy) {
bl = bl->subBlock(0);
if (bl == (FlowBlock *)0) return bl;
}
return bl;
}
/// How many getParent() calls from the leaf to \b this
/// \param leaf is the component FlowBlock
/// \return the depth count
int4 FlowBlock::calcDepth(const FlowBlock *leaf) const
{
int4 depth = 0;
while(leaf != this) {
if (leaf == (const FlowBlock *)0)
return -1;
leaf = leaf->getParent();
depth += 1;
}
return depth;
}
/// Return \b true if \b this block \e dominates the given block (or is equal to it).
/// This assumes that block indices have been set with a reverse post order so that having a
/// smaller index is a necessary condition for dominance.
/// \param subBlock is the given block to test against \b this for dominance
/// \return \b true if \b this dominates
bool FlowBlock::dominates(const FlowBlock *subBlock) const
{
while(subBlock != (const FlowBlock *)0 && index <= subBlock->index) {
if (subBlock == this) return true;
subBlock = subBlock->getImmedDom();
}
return false;
}
/// \brief Check if the condition from the given block holds for \b this block
///
/// We assume the given block has 2 out-edges and that \b this block is immediately reached by
/// one of these two edges. Some condition holds when traversing the out-edge to \b this, and the complement
/// of the condition holds for traversing the other out-edge. We verify that the condition holds for
/// this entire block. More specifically, we check that that there is no path to \b this through the
/// sibling edge, where the complement of the condition holds (unless we loop back through the conditional block).
/// \param cond is the conditional block with 2 out-edges
/// \return \b true if the condition holds for this block
bool FlowBlock::restrictedByConditional(const FlowBlock *cond) const
{
if (sizeIn() == 1) return true; // Its impossible for any path to come through sibling to this
if (getImmedDom() != cond) return false; // This is not dominated by conditional block at all
bool seenCond = false;
for(int4 i=0;i<sizeIn();++i) {
const FlowBlock *inBlock = getIn(i);
if (inBlock == cond) {
if (seenCond)
return false; // Coming in from cond block on multiple direct edges
seenCond = true;
continue;
}
while(inBlock != this) {
if (inBlock == cond) return false; // Must have come through sibling
inBlock = inBlock->getImmedDom();
}
}
return true;
}
/// \return \b true if \b this is the top of a loop
bool FlowBlock::hasLoopIn(void) const
{
for(int4 i=0;i<intothis.size();++i)
if ((intothis[i].label & f_loop_edge)!=0) return true;
return false;
}
/// \return \b true if \b this is the bottom of a loop
bool FlowBlock::hasLoopOut(void) const
{
for(int4 i=0;i<outofthis.size();++i)
if ((outofthis[i].label & f_loop_edge)!=0) return true;
return false;
}
/// \param bl is the given block
void FlowBlock::eliminateInDups(FlowBlock *bl)
{
int4 indval = -1;
int4 i=0;
while(i < intothis.size()) {
if (intothis[i].point == bl) {
if (indval == -1) { // The first instance of bl
indval = i; // We keep it
i += 1;
}
else {
intothis[indval].label |= intothis[i].label;
int4 rev = intothis[i].reverse_index;
halfDeleteInEdge(i);
bl->halfDeleteOutEdge(rev);
}
}
else
i += 1;
}
#ifdef BLOCKCONSISTENT_DEBUG
checkEdges();
bl->checkEdges();
#endif
}
/// \param bl is the given block
void FlowBlock::eliminateOutDups(FlowBlock *bl)
{
int4 indval = -1;
int4 i=0;
while(i < outofthis.size()) {
if (outofthis[i].point == bl) {
if (indval == -1) { // The first instance of bl
indval = i; // We keep it
i += 1;
}
else {
outofthis[indval].label |= outofthis[i].label;
int4 rev = outofthis[i].reverse_index;
halfDeleteOutEdge(i);
bl->halfDeleteInEdge(rev);
}
}
else
i += 1;
}
#ifdef BLOCKCONSISTENT_DEBUG
checkEdges();
bl->checkEdges();
#endif
}
/// \brief Find blocks that are at the end of multiple edges
///
/// \param ref is the list of BlockEdges to search
/// \param duplist will contain the list of blocks with duplicate edges
void FlowBlock::findDups(const vector<BlockEdge> &ref,vector<FlowBlock *> &duplist)
{
vector<BlockEdge>::const_iterator iter;
for(iter=ref.begin();iter!=ref.end();++iter) {
if (((*iter).point->flags&f_mark2)!=0) continue; // Already marked as a duplicate
if (((*iter).point->flags&f_mark)!=0) { // We have a duplicate
duplist.push_back((*iter).point);
(*iter).point->flags |= f_mark2;
}
else
(*iter).point->flags |= f_mark;
}
for(iter=ref.begin();iter!=ref.end();++iter) // Erase our marks
(*iter).point->flags &= ~(f_mark | f_mark2);
}
void FlowBlock::dedup(void)
{
vector<FlowBlock *> duplist;
vector<FlowBlock *>::iterator iter;
findDups(intothis,duplist);
for(iter=duplist.begin();iter!=duplist.end();++iter)
eliminateInDups(*iter);
duplist.clear();
findDups(outofthis,duplist);
for(iter=duplist.begin();iter!=duplist.end();++iter)
eliminateOutDups(*iter);
}
#ifdef BLOCKCONSISTENT_DEBUG
/// Make sure block references in the BlockEdge objects owned
/// by \b this block, and any other block at the other end of these edges,
/// are consistent.
void FlowBlock::checkEdges(void)
{
for(int4 i=0;i<intothis.size();++i) {
BlockEdge &edge( intothis[i] );
int4 rev = edge.reverse_index;
FlowBlock *bl = edge.point;
if (bl->outofthis.size() <= rev)
throw LowlevelError("Not enough outofthis blocks");
BlockEdge &edger( bl->outofthis[rev] );
if (edger.point != this)
throw LowlevelError("Intothis edge mismatch");
if (edger.reverse_index != i)
throw LowlevelError("Intothis index mismatch");
}
for(int4 i=0;i<outofthis.size();++i) {
BlockEdge &edge( outofthis[i] );
int4 rev = edge.reverse_index;
FlowBlock *bl = edge.point;
if (bl->intothis.size() <= rev)
throw LowlevelError("Not enough intothis blocks");
BlockEdge &edger( bl->intothis[rev] );
if (edger.point != this)
throw LowlevelError("Outofthis edge mismatch");
if (edger.reverse_index != i)
throw LowlevelError("Outofthis index mismatch");
}
}
#endif
/// Search through incoming blocks in edge order for the given block.
/// \param bl is the given FlowBlock
/// \return the matching edge index or -1 if \b bl doesn't flow into \b this
int4 FlowBlock::getInIndex(const FlowBlock *bl) const
{
int4 blocknum;
for(blocknum=0;blocknum<intothis.size();++blocknum)
if (intothis[blocknum].point==bl) return blocknum;
return -1; // That block not found
}
/// Search through outgoing blocks in edge order for the given block.
/// \param bl is the given FlowBlock
/// \return the matching edge index or -1 if \b bl doesn't flow out of \b this
int4 FlowBlock::getOutIndex(const FlowBlock *bl) const
{
int4 blocknum;
for(blocknum=0;blocknum<outofthis.size();++blocknum)
if (outofthis[blocknum].point==bl) return blocknum;
return -1;
}
/// Only print a header for \b this single block
/// \param s is the output stream
void FlowBlock::printHeader(ostream &s) const
{
s << dec << index;
if (!getStart().isInvalid() && !getStop().isInvalid()) {
s << ' ' << getStart() << '-' << getStop();
}
}
/// Recursively print out the hierarchical structure of \b this FlowBlock.
/// \param s is the output stream
/// \param level is the current level of indentation
void FlowBlock::printTree(ostream &s,int4 level) const
{
int4 i;
for(i=0;i<level;++i)
s << " ";
printHeader(s);
s << endl;
}
/// If \b this FlowBlock was ends with a computed jump, retrieve
/// the associated JumpTable object
/// \return the JumpTable object or NULL
JumpTable *FlowBlock::getJumptable(void) const
{
JumpTable *jt = (JumpTable *)0;
if (!isSwitchOut()) return jt;
PcodeOp *indop = lastOp();
if (indop != (PcodeOp *)0)
jt = indop->getParent()->getFuncdata()->findJumpTable(indop);
return jt;
}
/// Given a string describing a FlowBlock type, return the block_type.
/// This is currently only used by the decode() process.
/// TODO: Fill in the remaining names and types
/// \param nm is the name string
/// \return the corresponding block_type
FlowBlock::block_type FlowBlock::nameToType(const string &nm)
{
FlowBlock::block_type bt = FlowBlock::t_plain;
if (nm == "graph")
bt = FlowBlock::t_graph;
else if (nm == "copy")
bt = FlowBlock::t_copy;
return bt;
}
/// For use in serializng FlowBlocks to XML.
/// \param bt is the block_type
/// \return the corresponding name string
string FlowBlock::typeToName(FlowBlock::block_type bt)
{
switch(bt) {
case t_plain:
return "plain";
case t_basic:
return "basic";
case t_graph:
return "graph";
case t_copy:
return "copy";
case t_goto:
return "goto";
case t_multigoto:
return "multigoto";
case t_ls:
return "list";
case t_condition:
return "condition";
case t_if:
return "properif";
case t_whiledo:
return "whiledo";
case t_dowhile:
return "dowhile";
case t_switch:
return "switch";
case t_infloop:
return "infloop";
}
return "";
}
/// Comparator for ordering the final 0-exit blocks
/// \param bl1 is the first FlowBlock to compare
/// \param bl2 is the second FlowBlock
/// \return true if the first comes before the second
bool FlowBlock::compareFinalOrder(const FlowBlock *bl1,const FlowBlock *bl2)
{
if (bl1->getIndex() == 0) return true; // Make sure the entry point comes first
if (bl2->getIndex() == 0) return false;
PcodeOp *op1 = bl1->lastOp();
PcodeOp *op2 = bl2->lastOp();
if (op1 != (PcodeOp *)0) { // Make sure return blocks come last
if (op2 != (PcodeOp *)0) {
if ((op1->code() == CPUI_RETURN)&&(op2->code() != CPUI_RETURN))
return false;
else if ((op1->code() != CPUI_RETURN)&&(op2->code() == CPUI_RETURN))
return true;
}
if (op1->code() == CPUI_RETURN) return false;
}
else if (op2 != (PcodeOp *)0) {
if (op2->code() == CPUI_RETURN) return true;
}
return (bl1->getIndex() < bl2->getIndex()); // Otherwise use index
}
/// Within the dominator tree, find the earliest common ancestor of two FlowBlocks
/// \param bl1 is the first FlowBlock
/// \param bl2 is the second
/// \return the common ancestor which dominates both
FlowBlock *FlowBlock::findCommonBlock(FlowBlock *bl1,FlowBlock *bl2)
{
FlowBlock *b1,*b2,*common;
common = (FlowBlock *)0;
b1 = bl1;
b2 = bl2;
for(;;) {
if (b2 == (FlowBlock *)0) {
while(b1 != (FlowBlock *)0) {
if (b1->isMark()) {
common = b1;
break;
}
b1 = b1->getImmedDom();
}
break;
}
if (b1 == (FlowBlock *)0) {
while(b2 != (FlowBlock *)0) {
if (b2->isMark()) {
common = b2;
break;
}
b2 = b2->getImmedDom();
}
break;
}
if (b1->isMark()) {
common = b1;
break;
}
b1->setMark();
if (b2->isMark()) {
common = b2;
break;
}
b2->setMark();
b1 = b1->getImmedDom();
b2 = b2->getImmedDom();
}
// Clear our marks
while(bl1!=(FlowBlock *)0) {
if (!bl1->isMark()) break;
bl1->clearMark();
bl1 = bl1->getImmedDom();
}
while(bl2!=(FlowBlock *)0) {
if (!bl2->isMark()) break;
bl2->clearMark();
bl2 = bl2->getImmedDom();
}
return common;
}
/// Find the most immediate dominating FlowBlock of all blocks in the given set.
/// The container must not be empty.
/// \param blockSet is the given set of blocks
/// \return the most immediate dominating FlowBlock
FlowBlock *FlowBlock::findCommonBlock(const vector<FlowBlock *> &blockSet)
{
vector<FlowBlock *> markedSet;
FlowBlock *bl;
FlowBlock *res = blockSet[0];
int4 bestIndex = res->getIndex();
bl = res;
do {
bl->setMark();
markedSet.push_back(bl);
bl = bl->getImmedDom();
} while (bl != (FlowBlock *)0);
for(int4 i=1;i<blockSet.size();++i) {
if (bestIndex == 0)
break;
bl = blockSet[i];
while(!bl->isMark()) {
bl->setMark();
markedSet.push_back(bl);
bl = bl->getImmedDom();
}
if (bl->getIndex() < bestIndex) { // If first meeting with old paths is higher than ever before
res = bl; // we have a new best
bestIndex = res->getIndex();
}
}
for(int4 i=0;i<markedSet.size();++i)
markedSet[i]->clearMark();
return res;
}
/// \brief Find conditional block that decides between the given control-flow edges
///
/// There must be a unique path from the conditional block through the first edge, and
/// a second unique path through the second edge. Otherwise null is returned. The index of the
/// output block from the conditional that flows to the first edge is passed back.
/// \param bl1 is the destination block for the first given control-flow edge
/// \param edge1 is the input slot for the first edge
/// \param bl2 is the destination block for the second given control-flow edge
/// \param edge2 is the input slot for the second edge
/// \param slot1 will hold the output slot leading to the first control-flow edge
/// \return the conditional FlowBlock if it exists or null
FlowBlock *FlowBlock::findCondition(FlowBlock *bl1,int4 edge1,FlowBlock *bl2,int4 edge2,int4 &slot1)
{
FlowBlock *cond = bl1->getIn(edge1);
while (cond->sizeOut() != 2) {
if (cond->sizeOut() != 1) return (FlowBlock *)0;
bl1 = cond;
edge1 = 0;
cond = bl1->getIn(0);
}
while (cond != bl2->getIn(edge2)) {
bl2 = bl2->getIn(edge2);
if (bl2->sizeOut() != 1) return (FlowBlock *)0;
edge2 = 0;
}
slot1 = bl1->getInRevIndex(edge1);
return cond;
}
/// Add the given FlowBlock to the list and make \b this the parent
/// Update \b index so that it has the minimum over all components
/// \param bl is the given FlowBlock
void BlockGraph::addBlock(FlowBlock *bl)
{
int4 min = bl->index;
if (list.empty()) {
index = min;
}
else {
if (min < index) index = min;
}
bl->parent = this;
list.push_back(bl);
}
/// Force \b this FlowBlock to have the indicated number of outputs.
/// Create edges back into itself if necessary.
/// \param i is the number of out edges to force
void BlockGraph::forceOutputNum(int4 i)
{
#ifdef BLOCKCONSISTENT_DEBUG
if (sizeOut() > i)
throw LowlevelError("Bad block output force");
#endif
while(sizeOut() < i)
addInEdge(this,f_loop_edge|f_back_edge);
}
/// Examine the set of components and their incoming and outgoing edges. If both
/// ends of the edge are not within the set, then \b this block inherits the edge.
/// A formal BlockEdge is added between \b this and the FlowBlock outside the set.
/// The edges are deduplicated.
void BlockGraph::selfIdentify(void)
{
vector<FlowBlock *>::iterator iter;
FlowBlock *mybl,*otherbl;
if (list.empty()) return;
for(iter=list.begin();iter!=list.end();++iter) {
mybl = *iter;
int4 i = 0;
while(i<mybl->intothis.size()) {
otherbl = mybl->intothis[i].point;
if (otherbl->parent == this)
i += 1;
else {
for(int4 j=0;j<otherbl->outofthis.size();++j)
if (otherbl->outofthis[j].point == mybl)
otherbl->replaceOutEdge(j,this);
// Dont increment i
}
}
i = 0;
while(i<mybl->outofthis.size()) {
otherbl = mybl->outofthis[i].point;
if (otherbl->parent == this)
i += 1;
else {
for(int4 j=0;j<otherbl->intothis.size();++j)
if (otherbl->intothis[j].point == mybl)
otherbl->replaceInEdge(j,this);
if (mybl->isSwitchOut()) // Check for indirect branch out
setFlag(f_switch_out);
}
}
}
dedup();
}
/// \brief Move nodes from \b this into a new BlockGraph
///
/// This does most of the work of collapsing a set of components in \b this
/// into a single node. The components are removed from \b this, put in the new FlowBlock
/// and adjusts edges. The new FlowBlock must be added back into \b this.
/// \param ident is the new FlowBlock
/// \param nodes is the list component FlowBlocks to move
void BlockGraph::identifyInternal(BlockGraph *ident,const vector<FlowBlock *> &nodes)
{
vector<FlowBlock *>::const_iterator iter;
for(iter=nodes.begin();iter!=nodes.end();++iter) {
#ifdef BLOCKCONSISTENT_DEBUG
if ((*iter)->parent != this)
throw LowlevelError("Bad block identify");
#endif
(*iter)->setMark();
ident->addBlock(*iter); // Maintain order of blocks
ident->flags |= ((*iter)->flags & (f_interior_gotoout | f_interior_gotoin));
}
vector<FlowBlock *> newlist;
for(iter=list.begin();iter!=list.end();++iter) { // Remove -nodes- from our list
if (!(*iter)->isMark())
newlist.push_back(*iter);
else
(*iter)->clearMark();
}
list = newlist;
ident->selfIdentify();
}
/// \param fl is the set of boolean properties
void BlockGraph::clearEdgeFlags(uint4 fl)
{
fl = ~fl;
int4 size = list.size();
for(int4 j=0;j<size;++j) {
FlowBlock *bl = list[j];
for(int4 i=0;i<bl->intothis.size();++i)
bl->intothis[i].label &= fl;
for(int4 i=0;i<bl->outofthis.size();++i)
bl->outofthis[i].label &= fl;
}
}
/// \brief Create a single root block
///
/// Some algorithms need a graph with a single entry node. Given multiple entry points,
/// this routine creates an artificial root with no \e in edges and an \e out
/// edge to each of the real entry points. The resulting root FlowBlock isn't
/// owned by any BlockGraph, and the caller is responsible for freeing it.
/// \param rootlist is the given set of entry point FlowBlocks
/// \return the new artificial root FlowBlock
FlowBlock *BlockGraph::createVirtualRoot(const vector<FlowBlock *> &rootlist)
{
FlowBlock *newroot = new FlowBlock();
for(int4 i=0;i<rootlist.size();++i)
rootlist[i]->addInEdge(newroot,0);
return newroot;
}
/// \brief Find a spanning tree (skipping irreducible edges).
///
/// - Label pre and reverse-post orderings, tree, forward, cross, and back edges.
/// - Calculate number of descendants.
/// - Put the blocks of the graph in reverse post order.
/// - Return an array of all nodes in pre-order.
/// - If the graph does not have a real root, create one and return it, otherwise return null.
///
/// Algorithm originally due to Tarjan.
/// The first block is the entry block, and should remain the first block
/// \param preorder will hold the list of FlowBlock components in pre-order
/// \param rootlist will hold the list of entry points
void BlockGraph::findSpanningTree(vector<FlowBlock *> &preorder,vector<FlowBlock *> &rootlist)
{
if (list.size()==0) return;
vector<FlowBlock *> rpostorder;
vector<FlowBlock *> state;
vector<int4> istate;
FlowBlock *tmpbl;
int4 origrootpos;
preorder.reserve(list.size());
rpostorder.resize(list.size());
state.reserve(list.size());
istate.reserve(list.size());
for(int4 i=0;i<list.size();++i) {
tmpbl = list[i];
tmpbl->index = -1; // reverse post-order starts at 0
tmpbl->visitcount = -1;
tmpbl->copymap = tmpbl;
if (tmpbl->sizeIn()==0) // Keep track of all potential roots of the tree
rootlist.push_back(tmpbl);
}
if (rootlist.size() > 1) { // Make sure orighead is visited last, (so it is first in the reverse post order)
tmpbl = rootlist[rootlist.size()-1];
rootlist[rootlist.size()-1] = rootlist[0];
rootlist[0] = tmpbl;
}
else if (rootlist.size() == 0) { // If there's no obvious starting block
rootlist.push_back(list[0]); // Assume first block is entry point
}
origrootpos = rootlist.size()-1; // Position of original head in rootlist
for(int4 repeat=0;repeat<2;++repeat) {
bool extraroots = false;
int4 rpostcount = list.size();
int4 rootindex = 0;
clearEdgeFlags(~((uint4)0)); // Clear all edge flags
while(preorder.size() < list.size()) {
FlowBlock *startbl = (FlowBlock *)0;
while(rootindex<rootlist.size()) { // Go thru blocks with no in edges
startbl = rootlist[rootindex];
rootindex += 1;
if (startbl->visitcount == -1) break;
// If we reach here, startbl isn't really a root (root from previous pass)
for(int4 i=rootindex;i<rootlist.size();++i)
rootlist[i-1] = rootlist[i];
rootlist.pop_back(); // Remove it
rootindex -= 1;
startbl = (FlowBlock *)0;
}
if (startbl == (FlowBlock *)0) { // If we didn't find one, just take next unvisited
extraroots = true;
for(int4 i=0;i<list.size();++i) {
startbl = list[i];
if (startbl->visitcount == -1) break;
}
rootlist.push_back(startbl); // We have to treat this block as another root
rootindex += 1; // Update root traversal state
}
state.push_back(startbl);
istate.push_back(0);
startbl->visitcount = preorder.size();
preorder.push_back(startbl);
startbl->numdesc = 1;
while(!state.empty()) {
FlowBlock *curbl = state.back();
if (curbl->sizeOut() <= istate.back()) { // We've visited all children of this node
state.pop_back();
istate.pop_back();
rpostcount -= 1;
curbl->index = rpostcount;
rpostorder[rpostcount] = curbl;
if (!state.empty())
state.back()->numdesc += curbl->numdesc;
}
else {
int4 edgenum = istate.back();
istate.back() += 1; // Next visit to this state should try next child
if (curbl->isIrreducibleOut(edgenum)) continue; // Pretend irreducible edges don't exist
FlowBlock *childbl = curbl->getOut(edgenum); // New child to try
if (childbl->visitcount == -1) { // If we haven't visited this node before
curbl->setOutEdgeFlag(edgenum,f_tree_edge);
state.push_back(childbl);
istate.push_back(0);
childbl->visitcount = preorder.size();
preorder.push_back(childbl);
childbl->numdesc = 1;
}
else if (childbl->index == -1) // childbl is already on stack
curbl->setOutEdgeFlag(edgenum,f_back_edge|f_loop_edge);
else if (curbl->visitcount < childbl->visitcount) // childbl processing is already done
curbl->setOutEdgeFlag(edgenum,f_forward_edge);
else
curbl->setOutEdgeFlag(edgenum,f_cross_edge);
}
}
}
if (!extraroots) break;
if (repeat==1)
throw LowlevelError("Could not generate spanning tree");
// We had extra roots we did not know about so we have to regenerate the post order so entry block comes first
tmpbl = rootlist[rootlist.size()-1];
rootlist[rootlist.size()-1] = rootlist[origrootpos]; // Move entry block to last position in rootlist
rootlist[origrootpos] = tmpbl;
for(int4 i=0;i<list.size();++i) {
tmpbl = list[i];
tmpbl->index = -1; // reverse post-order starts at 0
tmpbl->visitcount = -1;
tmpbl->copymap = tmpbl;
}
preorder.clear();
state.clear();
istate.clear();
}
if (rootlist.size() > 1) { // Make sure orighead is at the front of the rootlist as well
tmpbl = rootlist[rootlist.size()-1];
rootlist[rootlist.size()-1] = rootlist[0];
rootlist[0] = tmpbl;
}
list = rpostorder;
}
/// \brief Identify irreducible edges
///
/// Assuming the spanning tree has been properly labeled using findSpanningTree(),
/// test for and label and irreducible edges (the test ignores any edges already labeled as irreducible).
/// Return \b true if the spanning tree needs to be rebuilt, because one of the tree edges is irreducible.
/// Original algorithm due to Tarjan.
/// \param preorder is the list of FlowBlocks in pre-order
/// \param irreduciblecount will hold the number of irreducible edges
/// \return true if the spanning tree needs to be rebuilt
bool BlockGraph::findIrreducible(const vector<FlowBlock *> &preorder,int4 &irreduciblecount)
{
vector<FlowBlock *> reachunder; // The current reachunder set being built (also with mark set on each block)
bool needrebuild = false;
int4 xi = preorder.size()-1;
while(xi >= 0) { // For each vertex in reverse pre-order
FlowBlock *x = preorder[xi];
xi -= 1;
int4 sizein = x->sizeIn();
for(int4 i=0;i<sizein;++i) {
if (!x->isBackEdgeIn(i)) continue; // For each back-edge into x
FlowBlock *y = x->getIn(i);
if (y==x) continue; // Reachunder set does not include the loop head
reachunder.push_back(y->copymap); // Add FIND(y) to reachunder
y->copymap->setMark();
}
int4 q = 0;
while(q < reachunder.size()) {
FlowBlock *t = reachunder[q];
q += 1;
int4 sizein_t = t->sizeIn();
for(int4 i=0;i<sizein_t;++i) {
if (t->isIrreducibleIn(i)) continue; // Pretend irreducible edges don't exist
// All back-edges into t have already been collapsed, so this is
FlowBlock *y = t->getIn(i); // For each forward, tree, or cross edge
FlowBlock *yprime = y->copymap; // y' = FIND(y)
if ((x->visitcount > yprime->visitcount)||( x->visitcount + x->numdesc <= yprime->visitcount)) {
// The original Tarjan algorithm reports reducibility failure at this point
irreduciblecount += 1;
int4 edgeout = t->getInRevIndex(i);
y->setOutEdgeFlag(edgeout,f_irreducible);
if (t->isTreeEdgeIn(i))
needrebuild = true; // If a tree edge is irreducible, we need to rebuild the spanning tree
else // Otherwise we can pretend the edge was already marked irreducible
y->clearOutEdgeFlag(edgeout,f_cross_edge|f_forward_edge);
}
else if ((!yprime->isMark())&&(yprime != x)) { // if yprime is not in reachunder and not equal to x
reachunder.push_back(yprime);
yprime->setMark();
}
}
}
// Collapse reachunder into a single node, labeled as x
for(int4 i=0;i<reachunder.size();++i) {
FlowBlock *s = reachunder[i];
s->clearMark();
s->copymap = x;
}
reachunder.clear();
}
return needrebuild;
}
/// Make sure \b this has exactly 2 out edges and the first edge flows to the given FlowBlock.
/// Swap the edges if necessary. Throw an exception if this is not possible.
/// \param out0 is the given FlowBlock
void BlockGraph::forceFalseEdge(const FlowBlock *out0)
{
if (sizeOut() != 2)
throw LowlevelError("Can only preserve binary condition");
if (out0->getParent() == this) // Allow for loops to self
out0 = this;
if (outofthis[0].point != out0)
swapEdges();
if (outofthis[0].point != out0)
throw LowlevelError("Unable to preserve condition");
}
/// \param i is the position of the first FlowBlock to swap
/// \param j is the position of the second
void BlockGraph::swapBlocks(int4 i,int4 j)
{
FlowBlock *bl = list[i];
list[i] = list[j];
list[j] = bl;
}
/// For the given BlockGraph find the first component leaf FlowBlock and
/// set its properties
/// \param bl is the given BlockGraph
/// \param fl is the property to set
void BlockGraph::markCopyBlock(FlowBlock *bl,uint4 fl)
{
bl->getFrontLeaf()->flags |= fl;
}
void BlockGraph::clear(void)
{
vector<FlowBlock *>::iterator iter;
for(iter=list.begin();iter!=list.end();++iter)
delete *iter;
list.clear();
}
void BlockGraph::markUnstructured(void)
{
vector<FlowBlock *>::iterator iter;
for(iter=list.begin();iter!=list.end();++iter)
(*iter)->markUnstructured(); // Recurse
}
void BlockGraph::markLabelBumpUp(bool bump)
{
FlowBlock::markLabelBumpUp(bump); // Mark ourselves if true
if (list.empty()) return;
vector<FlowBlock *>::iterator iter = list.begin();
(*iter)->markLabelBumpUp(bump); // Only pass true down to first subblock
++iter;
for(;iter!=list.end();++iter)
(*iter)->markLabelBumpUp(false);
}
void BlockGraph::scopeBreak(int4 curexit,int4 curloopexit)
{
vector<FlowBlock *>::iterator iter;
FlowBlock *curbl;
int4 ind;
iter = list.begin();
while(iter != list.end()) {
curbl = *iter;
++iter;
if (iter == list.end())
ind = curexit;
else
ind = (*iter)->getIndex();
// Recurse the scopeBreak call, making sure we pass the appropriate exit index.
curbl->scopeBreak(ind,curloopexit);
}
}
void BlockGraph::printTree(ostream &s,int4 level) const
{
vector<FlowBlock *>::const_iterator iter;
FlowBlock::printTree(s,level);
for(iter=list.begin();iter!=list.end();++iter)
(*iter)->printTree(s,level+1);
}
void BlockGraph::printRaw(ostream &s) const
{
vector<FlowBlock *>::const_iterator iter;
printHeader(s);
s << endl;
for(iter=list.begin();iter!=list.end();++iter)
(*iter)->printRaw(s);
}
PcodeOp *BlockGraph::firstOp(void) const
{
if (getSize() == 0)
return (PcodeOp *)0;
return getBlock(0)->firstOp();
}
FlowBlock *BlockGraph::nextFlowAfter(const FlowBlock *bl) const
{
FlowBlock *nextbl;
vector<FlowBlock *>::const_iterator iter;
for(iter=list.begin();iter!=list.end();++iter)
if ((*iter)==bl)
break;
++iter; // Find the first block after bl
if (iter == list.end()) {
if (getParent() == (FlowBlock *)0)
return (FlowBlock *)0;
return getParent()->nextFlowAfter(this);
}
nextbl = *iter; // The next block after bl (to be emitted)
if (nextbl != (FlowBlock *)0)
nextbl = nextbl->getFrontLeaf();
return nextbl;
}
void BlockGraph::finalTransform(Funcdata &data)
{
// Recurse into all the substructures
vector<FlowBlock *>::const_iterator iter;
for(iter=list.begin();iter!=list.end();++iter)
(*iter)->finalTransform(data);
}
void BlockGraph::finalizePrinting(Funcdata &data) const
{
// Recurse into all the substructures
vector<FlowBlock *>::const_iterator iter;
for(iter=list.begin();iter!=list.end();++iter)
(*iter)->finalizePrinting(data);
}
void BlockGraph::encodeBody(Encoder &encoder) const
{
FlowBlock::encodeBody(encoder);
for(int4 i=0;i<list.size();++i) {
FlowBlock *bl = list[i];
encoder.openElement(ELEM_BHEAD);
encoder.writeSignedInteger(ATTRIB_INDEX, bl->getIndex());
FlowBlock::block_type bt = bl->getType();
string nm;
if (bt == FlowBlock::t_if) {
int4 sz = ((BlockGraph *)bl)->getSize();
if (sz == 1)
nm = "ifgoto";
else if (sz == 2)
nm = "properif";
else
nm = "ifelse";
}
else
nm = FlowBlock::typeToName(bt);
encoder.writeString(ATTRIB_TYPE, nm);
encoder.closeElement(ELEM_BHEAD);
}
for(int4 i=0;i<list.size();++i)
list[i]->encode(encoder);
}
void BlockGraph::decodeBody(Decoder &decoder)
{
BlockMap newresolver;
vector<FlowBlock *> tmplist;
for(;;) {
uint4 subId = decoder.peekElement();
if (subId != ELEM_BHEAD) break;
decoder.openElement();
int4 newindex = decoder.readSignedInteger(ATTRIB_INDEX);
FlowBlock *bl = newresolver.createBlock(decoder.readString(ATTRIB_TYPE));
bl->index = newindex; // Need to set index here for sort
tmplist.push_back(bl);
decoder.closeElement(subId);
}
newresolver.sortList();
for(int4 i=0;i<tmplist.size();++i) {
FlowBlock *bl = tmplist[i];
bl->decode(decoder,newresolver);
addBlock(bl);
}
}
/// Parse a \<block> element. This is currently just a wrapper around the
/// FlowBlock::decode() that sets of the BlockMap resolver
/// \param decoder is the stream decoder
void BlockGraph::decode(Decoder &decoder)
{
BlockMap resolver;
FlowBlock::decode(decoder,resolver);
// Restore goto references here
}
/// \param begin is the start FlowBlock
/// \param end is the stop FlowBlock
void BlockGraph::addEdge(FlowBlock *begin,FlowBlock *end)
{
#ifdef BLOCKCONSISTENT_DEBUG
if ((begin->parent != this)||(end->parent != this))
throw LowlevelError("Bad edge create");
#endif
end->addInEdge(begin,0);
}
/// \param begin is a given component FlowBlock
/// \param outindex is the index of the \e out edge to mark as a loop
void BlockGraph::addLoopEdge(FlowBlock *begin,int4 outindex)
{
#ifdef BLOCKCONSISTENT_DEBUG
//if ((begin->parent != this)||(end->parent != this))
if ((begin->parent != this))
throw LowlevelError("Bad loopedge create");
#endif
// int4 i;
// i = begin->OutIndex(end);
// using OutIndex did not necessarily get the right edge
// if there were multiple outedges to the same block
begin->setOutEdgeFlag(outindex,f_loop_edge);
}
/// The edge must already exist
/// \param begin is the incoming FlowBlock of the edge
/// \param end is the outgoing FlowBlock
void BlockGraph::removeEdge(FlowBlock *begin,FlowBlock *end)
{
#ifdef BLOCKCONSISTENT_DEBUG
if ((begin->parent != this)||(end->parent != this))
throw LowlevelError("Bad edge remove");
#endif
int4 i;
for(i=0;i<end->intothis.size();++i)
if (end->intothis[i].point == begin)
break;
end->removeInEdge(i);
}
/// The edge from \b in to \b outbefore must already exist. It will get removed
/// and replaced with an edge from \b in to \b outafter. The new edge index
/// will be the same as the removed edge, and all other edge ordering will be preserved.
/// \param in is the input FlowBlock
/// \param outbefore is the initial output FlowBlock
/// \param outafter is the new output FlowBlock
void BlockGraph::switchEdge(FlowBlock *in,FlowBlock *outbefore,FlowBlock *outafter)
{
for(int4 i=0;i<in->outofthis.size();++i)
if (in->outofthis[i].point == outbefore)
in->replaceOutEdge(i,outafter);
}
/// Given an edge specified by its input FlowBlock, replace that
/// input with new FlowBlock.
/// \param blold is the original input FlowBlock
/// \param slot is the index of the \e out edge of \b blold
/// \param blnew is the FlowBlock that will become the input to the edge
void BlockGraph::moveOutEdge(FlowBlock *blold,int4 slot,FlowBlock *blnew)
{
#ifdef BLOCKCONSISTENT_DEBUG
if ((blold->parent != this)||(blnew->parent != this))
throw LowlevelError("Bad edge move");
#endif
FlowBlock *outbl = blold->getOut(slot);
int4 i = blold->getOutRevIndex(slot);
outbl->replaceInEdge(i,blnew);
}
/// The indicated block is pulled out of the component list and deleted.
/// Any edges between it and the rest of the BlockGraph are simply removed.
/// \param bl is the indicated block
void BlockGraph::removeBlock(FlowBlock *bl)
{
#ifdef BLOCKCONSISTENT_DEBUG
if (bl->parent != this)
throw LowlevelError("Bad block remove");
#endif
vector<FlowBlock *>::iterator iter;
while(bl->sizeIn()>0) // Rip the block out of the graph
removeEdge(bl->getIn(0),bl);
while(bl->sizeOut()>0)
removeEdge(bl,bl->getOut(0));
for(iter=list.begin();iter!=list.end();++iter)
if (*iter == bl) {
list.erase(iter);
break;
}
delete bl; // Free up memory
}
/// This should be applied only if the given FlowBlock has 0 or 1 outputs.
/// If there is an output FlowBlock, all incoming edges to the given FlowBlock
/// are moved so they flow into the output FlowBlock, then all remaining edges
/// into or out of the given FlowBlock are removed. The given FlowBlock is \b not
/// removed from \b this.
/// This routine doesn't preserve loopedge information
/// \param bl is the given FlowBlock component
void BlockGraph::removeFromFlow(FlowBlock *bl)
{
#ifdef BLOCKCONSISTENT_DEBUG
if (bl->parent != this)
throw LowlevelError("Bad remove from flow");
if ((bl->sizeIn()>0)&&(bl->sizeOut()>1))
throw LowlevelError("Illegal remove from flow");
#endif
FlowBlock *bbout,*bbin;
while(bl->sizeOut()>0) {
bbout = bl->getOut(bl->sizeOut()-1);
bl->removeOutEdge(bl->sizeOut()-1);
while(bl->sizeIn()>0) {
bbin = bl->getIn(0);
bbin->replaceOutEdge(bl->intothis[0].reverse_index,bbout);
}
}
}
/// Remove the given FlowBlock from the flow of the graph. It must have
/// 2 inputs, and 2 outputs. The edges will be remapped so that
/// - In(0) -> Out(0) and
/// - In(1) -> Out(1)
///
/// Or if \b flipflow is true:
/// - In(0) -> Out(1)
/// - In(1) -> Out(0)
/// \param bl is the given FlowBlock
/// \param flipflow indicates how the edges are remapped
void BlockGraph::removeFromFlowSplit(FlowBlock *bl,bool flipflow)
{
#ifdef BLOCKCONSISTENT_DEBUG
if (bl->parent != this)
throw LowlevelError("Bad remove from flow split");
if ((bl->sizeIn()!=2)&&(bl->sizeOut()!=2))
throw LowlevelError("Illegal remove from flow split");
#endif
if (flipflow)
bl->replaceEdgesThru(0,1); // Replace edge slot from 0 -> 1
else
bl->replaceEdgesThru(1,1); // Replace edge slot from 1 -> 1
// Removing the first edge
bl->replaceEdgesThru(0,0); // Replace remaining edge
}
/// The given FlowBlock must have exactly one output. That output must have
/// exactly one input. The output FlowBlock is removed and any outgoing edges
/// it has become outgoing edge of the given FlowBlock. The output FlowBlock
/// is permanently removed. It is viewed as being \e spliced together with the given FlowBlock.
/// \param bl is the given FlowBlock
void BlockGraph::spliceBlock(FlowBlock *bl)
{
FlowBlock *outbl = (FlowBlock *)0;
if (bl->sizeOut() == 1) {
outbl = bl->getOut(0);
if (outbl->sizeIn() != 1)
outbl = (FlowBlock *)0;
}
if (outbl == (FlowBlock *)0)
throw LowlevelError("Can only splice a block with 1 output to a block with 1 input");
// Flags from the input block that we keep
uint4 fl1 = bl->flags & (f_unstructured_targ|f_entry_point);
// Flags from the output block that we keep
uint4 fl2 = outbl->flags & f_switch_out;
bl->removeOutEdge(0);
// Move every out edge of -outbl- to -bl-
int4 szout = outbl->sizeOut();
for(int4 i=0;i<szout;++i)
moveOutEdge(outbl,0,bl);
removeBlock(outbl);
bl->flags = fl1 | fl2;
}
/// The component list is reordered to make the given FlowBlock first.
/// The \e f_entry_point property is updated.
/// \param bl is the given FlowBlock to make the entry point
void BlockGraph::setStartBlock(FlowBlock *bl)
{
#ifdef BLOCKCONSISTENT_DEBUG
if (bl->parent != this)
throw LowlevelError("Bad set start");
#endif
if ((list[0]->flags&f_entry_point)!=0) {
if (bl == list[0]) return; // Already set as start block
list[0]->flags &= ~f_entry_point; // Remove old entry point
}
int4 i;
for(i=0;i<list.size();++i)
if (list[i] == bl) break;
for(int4 j=i;j>0;--j) // Slide everybody down
list[j] = list[j-1];
list[0] = bl;
bl->flags |= f_entry_point;
}
/// Throw an exception if no entry point is registered
/// \return the entry point FlowBlock
FlowBlock *BlockGraph::getStartBlock(void) const
{
if (list.empty() || ((list[0]->flags&f_entry_point)==0))
throw LowlevelError("No start block registered");
return list[0];
}
/// Add the new FlowBlock to \b this
/// \return the new FlowBlock
FlowBlock *BlockGraph::newBlock(void)
{
FlowBlock *ret = new FlowBlock();
addBlock(ret);
return ret;
}
/// Add the new BlockBasic to \b this
/// \param fd is the function underlying the basic block
/// \return the new BlockBasic
BlockBasic *BlockGraph::newBlockBasic(Funcdata *fd)
{
BlockBasic *ret = new BlockBasic(fd);
addBlock(ret);
return ret;
}
/// Add the new BlockCopy to \b this
/// \param bl is the FlowBlock underlying the copy
/// \return the new BlockCopy
BlockCopy *BlockGraph::newBlockCopy(FlowBlock *bl)
{
BlockCopy *ret = new BlockCopy(bl);
ret->intothis = bl->intothis;
ret->outofthis = bl->outofthis;
ret->immed_dom = bl->immed_dom;
ret->index = bl->index;
// visitcount needs to be initialized to zero via FlowBlock constructure
// ret->visitcount = bl->visitcount;
ret->numdesc = bl->numdesc;
ret->flags |= bl->flags;
if (ret->outofthis.size() > 2)
ret->flags |= f_switch_out; // Make sure switch is marked (even if not produced by INDIRECT) as it is needed for structuring
addBlock(ret);
return ret;
}
/// Add the new BlockGoto to \b this, incorporating the given FlowBlock
/// \param bl is the given FlowBlock whose outgoing edge is to be marked as a \e goto
/// \return the new BlockGoto
BlockGoto *BlockGraph::newBlockGoto(FlowBlock *bl)
{
BlockGoto *ret = new BlockGoto(bl->getOut(0));
vector<FlowBlock *> nodes;
nodes.push_back(bl);
identifyInternal(ret,nodes);
addBlock(ret);
ret->forceOutputNum(1);
removeEdge(ret,ret->getOut(0)); // Treat out edge as if it didn't exist
return ret;
}
/// The given FlowBlock may already be a BlockMultiGoto, otherwise we
/// add the new BlockMultiGoto to \b this.
/// \param bl is the given FlowBlock with the new \e goto edge
/// \param outedge is the index of the outgoing edge to make into a \e goto
/// \return the (possibly new) BlockMultiGoto
BlockMultiGoto *BlockGraph::newBlockMultiGoto(FlowBlock *bl,int4 outedge)
{
BlockMultiGoto *ret;
FlowBlock *targetbl = bl->getOut(outedge);
bool isdefaultedge = bl->isDefaultBranch(outedge);
if (bl->getType() == t_multigoto) { // Already one goto edge from this same block, we add to existing structure
ret = (BlockMultiGoto *)bl;
ret->addEdge(targetbl);
removeEdge(ret,targetbl);
if (isdefaultedge)
ret->setDefaultGoto();
}
else {
ret = new BlockMultiGoto(bl);
int4 origSizeOut = bl->sizeOut();
vector<FlowBlock *> nodes;
nodes.push_back(bl);
identifyInternal(ret,nodes);
addBlock(ret);
ret->addEdge(targetbl);
if (targetbl != bl) {
if (ret->sizeOut() != origSizeOut) { // If there are less out edges after identifyInternal
// it must have collapsed a self edge (switch out edges are already deduped)
ret->forceOutputNum(ret->sizeOut()+1); // preserve the self edge (it is not the goto edge)
}
removeEdge(ret,targetbl); // Remove the edge to the goto target
}
// else -- the goto edge is a self edge and will get removed by identifyInternal
if (isdefaultedge)
ret->setDefaultGoto();
}
return ret;
}
/// Add the new BlockList to \b this, collapsing the given FlowBlock components into it.
/// \param nodes is the given set of FlowBlocks components
/// \return the new BlockList
BlockList *BlockGraph::newBlockList(const vector<FlowBlock *> &nodes)
{
const FlowBlock *out0 = (const FlowBlock *)0;
int4 outforce = nodes.back()->sizeOut();
if (outforce==2)
out0 = nodes.back()->getOut(0);
BlockList *ret = new BlockList();
identifyInternal(ret,nodes);
addBlock(ret);
ret->forceOutputNum(outforce);
if (ret->sizeOut()==2)
ret->forceFalseEdge(out0); // Preserve the condition
// if ((ret->OutSize() == 2)&&(nodes.back()->Out(0) == nodes.front()))
// ret->FlowBlock::negateCondition(); // Preserve out ordering of last block
return ret;
}
/// Add the new BlockCondition to \b this, collapsing its pieces into it.
/// \param b1 is the first FlowBlock piece
/// \param b2 is the second FlowBlock piece
/// \return the new BlockCondition
BlockCondition *BlockGraph::newBlockCondition(FlowBlock *b1,FlowBlock *b2)
{
const FlowBlock *out0 = b2->getOut(0);
vector<FlowBlock *> nodes;
OpCode opc = (b1->getFalseOut() == b2) ? CPUI_INT_OR : CPUI_INT_AND;
BlockCondition *ret = new BlockCondition(opc);
nodes.push_back(b1);
nodes.push_back(b2);
identifyInternal(ret,nodes);
addBlock(ret);
ret->forceOutputNum(2); // All conditions must have two outputs
ret->forceFalseEdge(out0); // Preserve the condition
return ret;
}
/// Add the new BlockIfGoto to \b this, collapsing the given condition FlowBlock into it.
/// \param cond is the given condition FlowBlock
/// \return the new BlockIfGoto
BlockIf *BlockGraph::newBlockIfGoto(FlowBlock *cond)
{
if (!cond->isGotoOut(1)) // True branch must be a goto branch
throw LowlevelError("Building ifgoto where true branch is not the goto");
const FlowBlock *out0 = cond->getOut(0);
vector<FlowBlock *> nodes;
BlockIf *ret = new BlockIf();
ret->setGotoTarget(cond->getOut(1)); // Store the target
nodes.push_back(cond);
identifyInternal(ret,nodes);
addBlock(ret);
ret->forceOutputNum(2);
ret->forceFalseEdge(out0); // Preserve the condition
removeEdge(ret,ret->getTrueOut()); // Treat the true edge as if it didn't exist
return ret;
}
/// Add the new BlockIf to \b this, collapsing the condition and body FlowBlocks into it.
/// \param cond is the condition FlowBlock
/// \param tc is the body FlowBlock
/// \return the new BlockIf
BlockIf *BlockGraph::newBlockIf(FlowBlock *cond,FlowBlock *tc)
{
vector<FlowBlock *> nodes;
BlockIf *ret = new BlockIf();
nodes.push_back(cond);
nodes.push_back(tc);
identifyInternal(ret,nodes);
addBlock(ret);
ret->forceOutputNum(1);
return ret;
}
/// Add the new BlockIfElse to \b this, collapsing the condition, true clause, and false clause into it.
/// \param cond is the condition FlowBlock
/// \param tc is the true clause FlowBlock
/// \param fc is the false clause FlowBlock
/// \return the new BlockIf
BlockIf *BlockGraph::newBlockIfElse(FlowBlock *cond,FlowBlock *tc,FlowBlock *fc)
{
vector<FlowBlock *> nodes;
BlockIf *ret = new BlockIf();
nodes.push_back(cond);
nodes.push_back(tc);
nodes.push_back(fc);
identifyInternal(ret,nodes);
addBlock(ret);
ret->forceOutputNum(1);
return ret;
}
/// Add the new BlockWhileDo to \b this, collapsing the condition and clause into it.
/// \param cond is the condition FlowBlock
/// \param cl is the clause FlowBlock
/// \return the new BlockWhileDo
BlockWhileDo *BlockGraph::newBlockWhileDo(FlowBlock *cond,FlowBlock *cl)
{
vector<FlowBlock *> nodes;
BlockWhileDo *ret = new BlockWhileDo();
nodes.push_back(cond);
nodes.push_back(cl);
identifyInternal(ret,nodes);
addBlock(ret);
ret->forceOutputNum(1);
return ret;
}
/// Add the new BlockDoWhile to \b this, collapsing the condition clause FlowBlock into it.
/// \param condcl is the condition clause FlowBlock
/// \return the new BlockDoWhile
BlockDoWhile *BlockGraph::newBlockDoWhile(FlowBlock *condcl)
{
vector<FlowBlock *> nodes;
BlockDoWhile *ret = new BlockDoWhile();
nodes.push_back(condcl);
identifyInternal(ret,nodes);
addBlock(ret);
ret->forceOutputNum(1);
return ret;
}
/// Add the new BlockInfLoop to \b this, collapsing the body FlowBlock into it.
/// \param body is the body FlowBlock
/// \return the new BlockInfLoop
BlockInfLoop *BlockGraph::newBlockInfLoop(FlowBlock *body)
{
vector<FlowBlock *> nodes;
BlockInfLoop *ret = new BlockInfLoop();
nodes.push_back(body);
identifyInternal(ret,nodes);
addBlock(ret);
return ret;
}
/// Add the new BlockSwitch to \b this, collapsing all the case FlowBlocks into it.
/// \param cs is the list of case FlowBlocks
/// \param hasExit is \b true if the switch has a formal exit
/// \return the new BlockSwitch
BlockSwitch *BlockGraph::newBlockSwitch(const vector<FlowBlock *> &cs,bool hasExit)
{
FlowBlock *rootbl = cs[0];
BlockSwitch *ret = new BlockSwitch(rootbl);
const FlowBlock *leafbl = rootbl->getExitLeaf();
if ((leafbl == (const FlowBlock *)0)||(leafbl->getType() != FlowBlock::t_copy))
throw LowlevelError("Could not get switch leaf");
ret->grabCaseBasic(leafbl->subBlock(0),cs); // Must be called before the identifyInternal
identifyInternal(ret,cs);
addBlock(ret);
if (hasExit)
ret->forceOutputNum(1); // If there is an exit, there should be exactly 1 out edge
ret->clearFlag(f_switch_out); // Don't consider this as being a switch "out"
return ret;
}
/// Construct a copy of the given BlockGraph in \b this. The nodes of the copy
/// will be official BlockCopy objects which will contain a reference to their
/// corresponding FlowBlock in the given graph. All edges will be duplicated.
/// \param graph is the given BlockGraph to copy
void BlockGraph::buildCopy(const BlockGraph &graph)
{
BlockCopy *copyblock;
int4 startsize = list.size();
vector<FlowBlock *>::const_iterator iter;
for(iter=graph.list.begin();iter!=graph.list.end();++iter) {
copyblock = newBlockCopy(*iter);
(*iter)->copymap = copyblock; // Store map basic->copy
}
for(iter=list.begin()+startsize;iter!=list.end();++iter)
(*iter)->replaceUsingMap();
}
void BlockGraph::clearVisitCount(void)
{
for(int4 i=0;i<list.size();++i)
list[i]->visitcount = 0;
}
/// Calculate the immediate dominator for each FlowBlock node in \b this BlockGraph,
/// for forward control-flow.
/// The algorithm must be provided a list of entry points for the graph.
/// We assume the blocks are in reverse post-order and this is reflected in the index field.
/// Using an algorithm by Cooper, Harvey, and Kennedy.
/// Softw. Pract. Exper. 2001; 4: 1-10
/// \param rootlist is the list of entry point FlowBlocks
void BlockGraph::calcForwardDominator(const vector<FlowBlock *> &rootlist)
{
vector<FlowBlock *> postorder;
FlowBlock *virtualroot;
FlowBlock *b,*new_idom,*rho;
bool changed;
int4 i,j,finger1,finger2;
if (list.empty()) return;
int4 numnodes = list.size()-1;
postorder.resize(list.size());
for(i=0;i<list.size();++i) {
list[i]->immed_dom = (FlowBlock *)0; // Clear the dominator field
postorder[ numnodes-i ] = list[i]; // Construct a forward post order list
}
if (rootlist.size() > 1) {
virtualroot = createVirtualRoot(rootlist);
postorder.push_back(virtualroot);
}
else
virtualroot = (FlowBlock *)0;
b = postorder.back(); // The official start node
if (b->sizeIn() != 0) { // Root node must have no in edges
if ((rootlist.size() != 1)||(rootlist[0] != b))
throw LowlevelError("Problems finding root node of graph");
virtualroot = createVirtualRoot(rootlist); // Create virtual root with no in edges
postorder.push_back(virtualroot);
b = virtualroot;
}
b->immed_dom = b;
for(i=0;i<b->sizeOut();++i) // Fill in dom of nodes which start immediately
b->getOut(i)->immed_dom = b; // connects to (to deal with possible artificial edge)
changed = true;
new_idom = (FlowBlock *)0;
while(changed) {
changed = false;
for(i=postorder.size()-2;i>=0;--i) { // For all nodes, in reverse post-order, except root
b = postorder[i];
if (b->immed_dom != postorder.back()) {
for(j=0;j<b->sizeIn();++j) { // Find first processed node
new_idom = b->getIn(j);
if (new_idom->immed_dom != (FlowBlock *)0)
break;
}
j += 1;
for(;j<b->sizeIn();++j) {
rho = b->getIn(j);
if (rho->immed_dom != (FlowBlock *)0) { // Here is the intersection routine
finger1 = numnodes - rho->index;
finger2 = numnodes - new_idom->index;
while(finger1 != finger2) {
while(finger1 < finger2)
finger1 = numnodes - postorder[finger1]->immed_dom->index;
while(finger2 < finger1)
finger2 = numnodes - postorder[finger2]->immed_dom->index;
}
new_idom = postorder[finger1];
}
}
if (b->immed_dom != new_idom) {
b->immed_dom = new_idom;
changed = true;
}
}
}
}
if (virtualroot != (FlowBlock *)0) { // If there was a virtual root, excise it from the dominator tree
for(i=0;i<list.size();++i)
if (postorder[i]->immed_dom == virtualroot)
postorder[i]->immed_dom = (FlowBlock *)0; // Remove the dominator link to virtualroot
while(virtualroot->sizeOut() > 0)
virtualroot->removeOutEdge(virtualroot->sizeOut()-1); // Remove any edges from virtualroot
delete virtualroot;
}
else
postorder.back()->immed_dom = (FlowBlock *)0;
}
/// Associate dominator children with each node via a list (of lists) indexed by the FlowBlock index.
/// \param child is the initially empty list of lists
void BlockGraph::buildDomTree(vector<vector<FlowBlock *> > &child) const
{
FlowBlock *bl;
child.clear();
child.resize(list.size()+1);
for(int4 i=0;i<list.size();++i) {
bl = list[i];
if (bl->immed_dom != (FlowBlock *)0)
child[bl->immed_dom->index].push_back(bl);
else
child[list.size()].push_back(bl);
}
}
/// Associate every FlowBlock node in \b this graph with its depth in the dominator tree.
/// The dominator root has depth 1, the nodes it immediately dominates have depth 2, etc.
/// \param depth is array that will be populated with depths
/// \return the maximum depth across all nodes
int4 BlockGraph::buildDomDepth(vector<int4> &depth) const
{
FlowBlock *bl;
int4 max = 0;
depth.resize(list.size()+1);
for(int4 i=0;i<list.size();++i) {
bl = list[i]->immed_dom;
if (bl != (FlowBlock *)0)
depth[i] = depth[bl->getIndex()] + 1;
else
depth[i] = 1;
if (max<depth[i])
max = depth[i];
}
depth[list.size()] = 0;
return max;
}
/// Collect all nodes in the dominator sub-tree starting at a given root FlowBlock.
/// We assume blocks in are reverse post order.
/// \param res will hold the list of nodes in the sub-tree
/// \param root is the given root FlowBlock
void BlockGraph::buildDomSubTree(vector<FlowBlock *> &res,FlowBlock *root) const
{
FlowBlock *bl,*dombl;
int4 rootindex = root->getIndex();
res.push_back(root);
for(int4 i=rootindex+1;i<list.size();++i) {
bl = list[i];
dombl = bl->getImmedDom();
if (dombl == (FlowBlock *)0) break;
if (dombl->getIndex() > rootindex) break;
res.push_back(bl);
}
}
/// This algorithm identifies a set of edges such that,
/// if the edges are removed, the remaining graph has NO directed cycles
/// The algorithm works as follows:
/// Starting from the start block, do a depth first search through the "out" edges
/// of the block. If the outblock is already on the current path from root to node,
/// we have found a cycle, we add the last edge to the list and continue pretending
/// that edge didn't exist. If the outblock is not on the current path but has
/// been visited before, we can truncate the search.
/// This is now only applied as a failsafe if the graph has irreducible edges.
void BlockGraph::calcLoop(void)
{ // Look for directed cycles in graph
// Mark edges (loopedges) that can be removed
// to prevent looping
vector<FlowBlock *>::iterator iter;
FlowBlock *bl,*nextbl;
int4 i;
if (list.empty()) return; // Nothing to do
vector<FlowBlock *> path; // Current depth first path
vector<int4> state;
path.push_back(list.front());
state.push_back(0); // No children visited yet
list.front()->setFlag(f_mark|f_mark2); // Mark this node as visited and on path
while(!path.empty()) {
bl = path.back();
i = state.back();
if (i >= bl->sizeOut()) { // Visited everything below this node, POP
bl->clearFlag(f_mark2); // Mark this node as no longer on the path
path.pop_back();
state.pop_back();
}
else {
state.back() += 1;
if (bl->isLoopOut(i)) continue; // Previously marked loop-edge (act as if it doesn't exist)
nextbl = bl->getOut(i);
if ((nextbl->flags&f_mark2) != 0) { // We found a cycle!
// Technically we should never reach here if the reducibility algorithms work
addLoopEdge(bl,i);
// throw LowlevelError("Found a new loop despite irreducibility");
}
else if ((nextbl->flags&f_mark)==0) { // Fresh node
nextbl->setFlag(f_mark|f_mark2);
path.push_back(nextbl);
state.push_back(0);
}
}
}
for(iter=list.begin();iter!=list.end();++iter)
(*iter)->clearFlag(f_mark|f_mark2); // Clear our marks
}
/// If the boolean \b un is \b true, collect unreachable blocks. Otherwise
/// collect reachable blocks.
/// \param res will hold the reachable or unreachable FlowBlocks
/// \param bl is the starting FlowBlock
/// \param un toggles reachable,unreachable
void BlockGraph::collectReachable(vector<FlowBlock *> &res,FlowBlock *bl,bool un) const
{
FlowBlock *blk,*blk2;
bl->setMark();
res.push_back(bl);
int4 total = 0;
// Propagate forward to find all reach blocks from entry point
while(total < res.size()) {
blk = res[total++];
for(int4 j=0;j<blk->sizeOut();++j) {
blk2 = blk->getOut(j);
if (blk2->isMark()) continue;
blk2->setMark();
res.push_back(blk2);
}
}
if (un) {
res.clear(); // Anything not marked is unreachable
for(int4 i=0;i<list.size();++i) {
blk = list[i];
if (blk->isMark())
blk->clearMark();
else
res.push_back(blk);
}
}
else {
for(int4 i=0;i<res.size();++i)
res[i]->clearMark();
}
}
/// - Find irreducible edges
/// - Find a spanning tree
/// - Set FlowBlock indices in reverse-post order
/// - Label tree-edges, forward-edges, cross-edges, and back-edges
/// \param rootlist will contain the entry points for the graph
void BlockGraph::structureLoops(vector<FlowBlock *> &rootlist)
{
vector<FlowBlock *> preorder;
bool needrebuild;
int4 irreduciblecount = 0;
do {
needrebuild = false;
findSpanningTree(preorder,rootlist);
needrebuild = findIrreducible(preorder,irreduciblecount);
if (needrebuild) {
clearEdgeFlags(f_tree_edge|f_forward_edge|f_cross_edge|f_back_edge|f_loop_edge); // Clear the spanning tree
preorder.clear();
rootlist.clear();
}
} while(needrebuild);
if (irreduciblecount > 0) {
// We need some kind of check here, like calcLoop, to make absolutely sure removing the loop edges makes a DAG
calcLoop();
}
}
#ifdef BLOCKCONSISTENT_DEBUG
bool BlockGraph::isConsistent(void) const
{
FlowBlock *bl1,*bl2;
int4 i,j,k;
int4 count1,count2;
for(i=0;i<list.size();++i) {
bl1 = list[i];
for(j=0;j<bl1->sizeIn();++j) {
bl2 = bl1->getIn(j); // For each in edge
count1 = 0;
for(k=0;k<bl1->sizeIn();++k)
if (bl1->getIn(k)==bl2) count1 += 1;
count2 = 0;
for(k=0;k<bl2->sizeOut();++k)
if (bl2->getOut(k)==bl1) count2 += 1;
if (count1 != count2)
return false;
}
for(j=0;j<bl1->sizeOut();++j) {
bl2 = bl1->getOut(j); // Similarly for each out edge
count1 = 0;
for(k=0;k<bl1->sizeOut();++k)
if (bl1->getOut(k)==bl2) count1 += 1;
count2 = 0;
for(k=0;k<bl2->sizeIn();++k)
if (bl2->getIn(k)==bl1) count2 += 1;
if (count1 != count2)
return false;
}
}
return true;
}
#endif
/// The operation is inserted \e before the PcodeOp pointed at by the iterator.
/// This method also assigns the ordering index for the PcodeOp, getSeqNum().getOrder()
/// \param iter points at the PcodeOp to insert before
/// \param inst is the PcodeOp to insert
void BlockBasic::insert(list<PcodeOp *>::iterator iter,PcodeOp *inst)
{
uintm ordbefore,ordafter;
list<PcodeOp *>::iterator newiter;
inst->setParent( this );
newiter = op.insert(iter,inst);
inst->setBasicIter(newiter);
if (newiter == op.begin())
ordbefore = 2; // This is minimum possible order val
else {
--newiter;
ordbefore = (*newiter)->getSeqNum().getOrder();
}
if (iter==op.end()) {
ordafter = ordbefore+0x1000000;
if (ordafter <= ordbefore)
ordafter = ~((uintm)0);
}
else
ordafter = (*iter)->getSeqNum().getOrder();
if (ordafter-ordbefore<=1)
setOrder();
else
inst->setOrder( ordafter/2 + ordbefore/2 ); // Beware overflow
if (inst->isBranch()) {
if (inst->code()==CPUI_BRANCHIND)
setFlag(f_switch_out);
}
}
/// \param inst is the PcodeOp to remove, which \e must be in the block
void BlockBasic::removeOp(PcodeOp *inst)
{
inst->setParent( (BlockBasic *)0 );
op.erase(inst->basiciter);
}
/// This relies slightly on \e normal semantics: when instructions \e fall-thru during execution,
/// the associated address increases.
/// \return the address of the original entry point instruction for \b this block
Address BlockBasic::getEntryAddr(void) const
{
const Range *range;
if (cover.numRanges() == 1) // If block consists of 1 range
range = cover.getFirstRange(); // return the start of range
else {
if (op.empty())
return Address();
const Address &addr(op.front()->getAddr()); // Find range of first op
range = cover.getRange(addr.getSpace(),addr.getOffset());
if (range == (const Range *)0)
return op.front()->getAddr();
}
return range->getFirstAddr();
}
Address BlockBasic::getStart(void) const
{
const Range *range = cover.getFirstRange();
if (range == (const Range *)0)
return Address();
return range->getFirstAddr();
}
Address BlockBasic::getStop(void) const
{
const Range *range = cover.getLastRange();
if (range == (const Range *)0)
return Address();
return range->getLastAddr();
}
PcodeOp *BlockBasic::firstOp(void) const
{
if (op.empty()) return (PcodeOp *)0;
return (PcodeOp *)op.front();
}
PcodeOp *BlockBasic::lastOp(void) const
{
if (op.empty()) return (PcodeOp *)0;
return (PcodeOp *) op.back();
}
bool BlockBasic::negateCondition(bool toporbottom)
{
PcodeOp *lastop = op.back();
lastop->flipFlag(PcodeOp::boolean_flip); // Flip the meaning of condition
lastop->flipFlag(PcodeOp::fallthru_true); // Flip whether fall-thru block is true/false
FlowBlock::negateCondition(true); // Flip the order of outgoing edges
return true; // Return -true- to indicate a change was made to data-flow
}
FlowBlock *BlockBasic::getSplitPoint(void)
{
if (sizeOut() != 2) return (FlowBlock *)0;
return this;
}
int4 BlockBasic::flipInPlaceTest(vector<PcodeOp *> &fliplist) const
{
if (op.empty()) return 2;
PcodeOp *lastop = op.back();
if (lastop->code() != CPUI_CBRANCH)
return 2;
return opFlipInPlaceTest(lastop,fliplist);
}
void BlockBasic::flipInPlaceExecute(void)
{
PcodeOp *lastop = op.back();
// This is similar to negateCondition but we don't need to set the boolean_flip flag on lastop
// because it is getting explicitly changed
lastop->flipFlag(PcodeOp::fallthru_true); // Flip whether the fallthru block is true/false
FlowBlock::negateCondition(true); // Flip the order of outof this
}
bool BlockBasic::isComplex(void) const
{
list<PcodeOp *>::const_iterator iter,iter2;
PcodeOp *inst,*d_op;
Varnode *vn;
int4 statement,maxref;
// Is this block too complicated for a condition.
// We count the number of statements in the block
statement = 0;
if (sizeOut()>=2)
statement = 1; // Consider the branch as a statement
maxref = data->getArch()->max_implied_ref; // Max number of uses a varnode can have
// before it must be considered an explicit variable
for(iter=op.begin();iter!=op.end();++iter) {
inst = *iter;
if (inst->isMarker()) continue;
vn = inst->getOut();
if (inst->isCall())
statement += 1;
else if (vn==(Varnode *)0) {
if (inst->isFlowBreak()) continue;
statement += 1;
}
else { // If the operation is a calculation with output
// This is a conservative version of
// Varnode::calc_explicit
bool yesstatement = false;
if (vn->hasNoDescend())
yesstatement = true;
else if (vn->isAddrTied()) // Being conservative
yesstatement = true;
else {
int4 totalref = 0; // Number of references to this variable
for(iter2=vn->beginDescend();iter2!=vn->endDescend();++iter2) {
d_op = *iter2;
if (d_op->isMarker()||(d_op->getParent() != this)) { // Variable used outside of block
yesstatement = true;
break;
}
totalref += 1;
if (totalref > maxref) { // If used too many times
yesstatement = true; // consider defining op as a statement
break;
}
}
}
if (yesstatement)
statement += 1;
}
if (statement >2) return true;
}
return false;
}
/// \param encoder is the stream encoder
void FlowBlock::encodeHeader(Encoder &encoder) const
{
encoder.writeSignedInteger(ATTRIB_INDEX, index);
}
/// \param decoder is the stream decoder to pull attributes from
void FlowBlock::decodeHeader(Decoder &decoder)
{
index = decoder.readSignedInteger(ATTRIB_INDEX);
}
/// Write \<edge> element to a stream
/// \param encoder is the stream encoder
void FlowBlock::encodeEdges(Encoder &encoder) const
{
for(int4 i=0;i<intothis.size();++i) {
intothis[i].encode(encoder);
}
}
/// \brief Restore edges from an encoded stream
///
/// \param decoder is the stream decoder
/// \param resolver is used to recover FlowBlock cross-references
void FlowBlock::decodeEdges(Decoder &decoder,BlockMap &resolver)
{
for(;;) {
uint4 subId = decoder.peekElement();
if (subId != ELEM_EDGE)
break;
decodeNextInEdge(decoder,resolver);
}
}
/// Encode \b this and all its sub-components as a \<block> element.
/// \param encoder is the stream encoder
void FlowBlock::encode(Encoder &encoder) const
{
encoder.openElement(ELEM_BLOCK);
encodeHeader(encoder);
encodeBody(encoder);
encodeEdges(encoder);
encoder.closeElement(ELEM_BLOCK);
}
/// Recover \b this and all it sub-components from a \<block> element.
///
/// This will construct all the sub-components using \b resolver as a factory.
/// \param decoder is the stream decoder
/// \param resolver acts as a factory and resolves cross-references
void FlowBlock::decode(Decoder &decoder,BlockMap &resolver)
{
uint4 elemId = decoder.openElement(ELEM_BLOCK);
decodeHeader(decoder);
decodeBody(decoder);
decodeEdges(decoder,resolver);
decoder.closeElement(elemId);
}
/// If there are two branches, pick the fall-thru branch
/// \return the next block in flow, or NULL otherwise
const FlowBlock *FlowBlock::nextInFlow(void) const
{
const PcodeOp *op;
if (sizeOut()==1) return getOut(0);
if (sizeOut()==2) {
op = lastOp();
if (op == (const PcodeOp *)0) return (const FlowBlock *)0;
if (op->code() != CPUI_CBRANCH) return (const FlowBlock *)0;
return op->isFallthruTrue() ? getOut(1) : getOut(0);
}
return (const FlowBlock *)0;
}
/// Does removing this block leads to redundant MULTIEQUAL entries which are inconsistent.
/// A MULTIEQUAL can hide an implied copy, in which case \b this block is actually doing something
/// and shouldn't be removed.
/// \param outslot is the index of the outblock that \b this is getting collapsed to
/// \return true if there is no implied COPY
bool BlockBasic::unblockedMulti(int4 outslot) const
{
const BlockBasic *blout = (const BlockBasic *)getOut(outslot);
const FlowBlock *bl;
PcodeOp *multiop,*othermulti;
list<PcodeOp *>::const_iterator iter;
Varnode *vnremove,*vnredund;
// First we build list of blocks which would have
// redundant branches into blout
vector<const FlowBlock *> redundlist;
for(int4 i=0;i<sizeIn();++i) {
bl = getIn(i);
for(int4 j=0;j<bl->sizeOut();++j)
if (bl->getOut(j)==blout)
redundlist.push_back(bl);
// We assume blout appears only once in bl's and this's
// outlists
}
if (redundlist.empty()) return true;
for(iter=blout->op.begin();iter!=blout->op.end();++iter) {
multiop = *iter;
if (multiop->code() != CPUI_MULTIEQUAL) continue;
for(vector<const FlowBlock *>::iterator biter=redundlist.begin();biter!=redundlist.end();++biter) {
bl = *biter;
vnredund = multiop->getIn(blout->getInIndex(bl)); // One of the redundant varnodes
vnremove = multiop->getIn(blout->getInIndex(this));
if (vnremove->isWritten()) {
othermulti = vnremove->getDef();
if ((othermulti->code()==CPUI_MULTIEQUAL)&&(othermulti->getParent()==this))
vnremove = othermulti->getIn(getInIndex(bl));
}
if (vnremove != vnredund) return false; // Redundant branches must be identical
}
}
return true;
}
/// This is a crucial test for whether \b this block is doing anything substantial
/// or is a candidate for removal. Even blocks that "do nothing" have some kind of branch
/// and placeholder operations (MULTIEQUAL and INDIRECT) for data flowing through the block.
/// This tests if there is any other operation going on.
/// \return \b true if there only MULTIEQUAL, INDIRECT, and branch operations in \b this
bool BlockBasic::hasOnlyMarkers(void) const
{
// (and a branch)
list<PcodeOp *>::const_iterator iter;
const PcodeOp *bop;
for(iter=op.begin();iter!=op.end();++iter) {
bop = *iter;
if (bop->isMarker()) continue;
if (bop->isBranch()) continue;
return false;
}
return true;
}
/// Check if \b this block is doing anything useful.
/// \return \b true if the block does nothing and should be removed
bool BlockBasic::isDoNothing(void) const
{
if (sizeOut() != 1) return false; // A block that does nothing useful
// has exactly one out, (no return or cbranch)
if (sizeIn() == 0) return false; // A block that does nothing but
// is a starting block, may need to be a
// placeholder for global(persistent) vars
if ((sizeIn()==1)&&(getIn(0)->isSwitchOut())) {
if (getOut(0)->sizeIn() > 1)
return false; // Don't remove switch targets
}
PcodeOp *lastop = lastOp();
if ((lastop != (PcodeOp *)0)&&(lastop->code()==CPUI_BRANCHIND))
return false; // Don't remove single-out indirect jumps
return hasOnlyMarkers();
}
/// 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
/// \param end is the address of the last instruction in the block
void BlockBasic::setInitialRange(const Address &beg,const Address &end)
{
cover.clear();
// TODO: We could check that -beg- and -end- are in the same address space
cover.insertRange(beg.getSpace(),beg.getOffset(),end.getOffset());
}
/// The SeqNum::order field for each PcodeOp must mirror the ordering of that PcodeOp within
/// \b this block. Insertions are usually handled by calculating an appropriate SeqNum::order field
/// for the new PcodeOp, but sometime there isn't enough room between existing ops. This method is
/// then called to recalculate the SeqNum::order field for all PcodeOp objects in \b this block,
/// reestablishing space between the field values.
void BlockBasic::setOrder(void)
{
list<PcodeOp *>::iterator iter;
uintm count,step;
step = ~((uintm)0);
step = ( step / op.size() ) -1;
count = 0;
for(iter=op.begin();iter!=op.end();++iter) {
count += step;
(*iter)->setOrder(count);
}
}
void BlockBasic::encodeBody(Encoder &encoder) const
{
cover.encode(encoder);
}
void BlockBasic::decodeBody(Decoder &decoder)
{
cover.decode(decoder);
}
void BlockBasic::printHeader(ostream &s) const
{
s << "Basic Block ";
FlowBlock::printHeader(s);
}
void BlockBasic::printRaw(ostream &s) const
{
list<PcodeOp *>::const_iterator iter;
PcodeOp *inst;
printHeader(s);
s << endl;
for(iter=op.begin();iter!=op.end();++iter) {
inst = *iter;
s << inst->getSeqNum() << ":\t";
inst->printRaw(s);
s << endl;
}
}
/// \brief Check for values created in \b this block that flow outside the block.
///
/// The block can calculate a value for a BRANCHIND or CBRANCH and can copy values and this method will still
/// return \b true. But calculating any value used outside the block, writing to an addressable location,
/// or performing a CALL or STORE causes the method to return \b false.
/// \return \b true if no value is created that can be used outside of the block
bool BlockBasic::noInterveningStatement(void) const
{
list<PcodeOp *>::const_iterator iter;
const PcodeOp *bop;
OpCode opc;
for(iter=op.begin();iter!=op.end();++iter) {
bop = *iter;
if (bop->isMarker()) continue;
if (bop->isBranch()) continue;
if (bop->getEvalType() == PcodeOp::special) {
if (bop->isCall())
return false;
opc = bop->code();
if (opc == CPUI_STORE || opc == CPUI_NEW)
return false;
}
else {
opc = bop->code();
if (opc == CPUI_COPY || opc == CPUI_SUBPIECE)
continue;
}
const Varnode *outvn = bop->getOut();
if (outvn->isAddrTied())
return false;
list<PcodeOp *>::const_iterator iter = outvn->beginDescend();
while(iter!=outvn->endDescend()) {
PcodeOp *op = *iter;
if (op->getParent() != this)
return false;
++iter;
}
}
return true;
}
/// If there exists a CPUI_MULTIEQUAL PcodeOp in \b this basic block that takes the given exact list of Varnodes
/// as its inputs, return that PcodeOp. Otherwise return null.
/// \param varArray is the exact list of Varnodes
/// \return the MULTIEQUAL or null
PcodeOp *BlockBasic::findMultiequal(const vector<Varnode *> &varArray)
{
Varnode *vn = varArray[0];
PcodeOp *op;
list<PcodeOp *>::const_iterator iter = vn->beginDescend();
for(;;) {
op = *iter;
if (op->code() == CPUI_MULTIEQUAL && op->getParent() == this)
break;
++iter;
if (iter == vn->endDescend())
return (PcodeOp *)0;
}
for(int4 i=0;i<op->numInput();++i) {
if (op->getIn(i) != varArray[i])
return (PcodeOp *)0;
}
return op;
}
/// Each Varnode must be defined by a PcodeOp with the same OpCode. The Varnode, within the array, is replaced
/// with the input Varnode in the indicated slot.
/// \param varArray is the given array of Varnodes
/// \param slot is the indicated slot
/// \return \b true if all the Varnodes are defined in the same way
bool BlockBasic::liftVerifyUnroll(vector<Varnode *> &varArray,int4 slot)
{
OpCode opc;
Varnode *cvn;
Varnode *vn = varArray[0];
if (!vn->isWritten()) return false;
PcodeOp *op = vn->getDef();
opc = op->code();
if (op->numInput() == 2) {
cvn = op->getIn(1-slot);
if (!cvn->isConstant()) return false;
}
else
cvn = (Varnode *)0;
varArray[0] = op->getIn(slot);
for(int4 i=1;i<varArray.size();++i) {
vn = varArray[i];
if (!vn->isWritten()) return false;
op = vn->getDef();
if (op->code() != opc) return false;
if (cvn != (Varnode *)0) {
Varnode *cvn2 = op->getIn(1-slot);
if (!cvn2->isConstant()) return false;
if (cvn->getSize() != cvn2->getSize()) return false;
if (cvn->getOffset() != cvn2->getOffset()) return false;
}
varArray[i] = op->getIn(slot);
}
return true;
}
void BlockCopy::printHeader(ostream &s) const
{
s << "Basic(copy) block ";
FlowBlock::printHeader(s);
}
void BlockCopy::printTree(ostream &s,int4 level) const
{
copy->printTree(s,level);
}
void BlockCopy::encodeHeader(Encoder &encoder) const
{
FlowBlock::encodeHeader(encoder);
int4 altindex = copy->getIndex();
encoder.writeSignedInteger(ATTRIB_ALTINDEX, altindex);
}
void BlockGoto::markUnstructured(void)
{
BlockGraph::markUnstructured(); // Recurse
if (gototype == f_goto_goto) {
if (gotoPrints())
markCopyBlock(gototarget,f_unstructured_targ);
}
}
void BlockGoto::scopeBreak(int4 curexit,int4 curloopexit)
{
getBlock(0)->scopeBreak(gototarget->getIndex(),curloopexit); // Recurse
// Check if our goto hits the current loop exit
if (curloopexit == gototarget->getIndex())
gototype = f_break_goto; // If so, our goto is a break
}
/// Under rare circumstances, the emitter can place the target block of the goto immediately
/// after this goto block. In this case, because the control-flow is essentially a fall-thru,
/// there should not be a formal goto statement emitted.
/// Check if the goto is to the next block in flow in which case the goto should not be printed.
/// \return \b true if the goto should be printed formally
bool BlockGoto::gotoPrints(void) const
{
if (getParent() != (FlowBlock *)0) {
FlowBlock *nextbl = getParent()->nextFlowAfter(this);
FlowBlock *gotobl = getGotoTarget()->getFrontLeaf();
return (gotobl != nextbl);
}
return false;
}
void BlockGoto::printHeader(ostream &s) const
{
s << "Plain goto block ";
FlowBlock::printHeader(s);
}
FlowBlock *BlockGoto::nextFlowAfter(const FlowBlock *bl) const
{ // Return the block containing the next statement in flow
return getGotoTarget()->getFrontLeaf();
}
void BlockGoto::encodeBody(Encoder &encoder) const
{
BlockGraph::encodeBody(encoder);
encoder.openElement(ELEM_TARGET);
const FlowBlock *leaf = gototarget->getFrontLeaf();
int4 depth = gototarget->calcDepth(leaf);
encoder.writeSignedInteger(ATTRIB_INDEX, leaf->getIndex());
encoder.writeSignedInteger(ATTRIB_DEPTH, depth);
encoder.writeUnsignedInteger(ATTRIB_TYPE, gototype);
encoder.closeElement(ELEM_TARGET);
}
void BlockMultiGoto::scopeBreak(int4 curexit,int4 curloopexit)
{
getBlock(0)->scopeBreak(-1,curloopexit); // Recurse
}
void BlockMultiGoto::printHeader(ostream &s) const
{
s << "Multi goto block ";
FlowBlock::printHeader(s);
}
FlowBlock *BlockMultiGoto::nextFlowAfter(const FlowBlock *bl) const
{
// The child of this can never be a BlockGoto
return (FlowBlock *)0;
}
void BlockMultiGoto::encodeBody(Encoder &encoder) const
{
BlockGraph::encodeBody(encoder);
for(int4 i=0;i<gotoedges.size();++i) {
FlowBlock *gototarget = gotoedges[i];
const FlowBlock *leaf = gototarget->getFrontLeaf();
int4 depth = gototarget->calcDepth(leaf);
encoder.openElement(ELEM_TARGET);
encoder.writeSignedInteger(ATTRIB_INDEX, leaf->getIndex());
encoder.writeSignedInteger(ATTRIB_DEPTH, depth);
encoder.closeElement(ELEM_TARGET);
}
}
const FlowBlock *BlockList::getExitLeaf(void) const
{
if (getSize()==0) return (FlowBlock *)0;
return getBlock(getSize()-1)->getExitLeaf();
}
PcodeOp *BlockList::lastOp(void) const
{
if (getSize()==0) return (PcodeOp *)0;
return getBlock(getSize()-1)->lastOp(); // Get last instruction of last block
}
bool BlockList::negateCondition(bool toporbottom)
{
FlowBlock *bl = getBlock(getSize()-1);
bool res = bl->negateCondition(false); // Negate condition of last block
FlowBlock::negateCondition(toporbottom); // Flip order of outgoing edges
return res;
}
FlowBlock *BlockList::getSplitPoint(void)
{
if (getSize()==0) return (FlowBlock *)0;
return getBlock(getSize()-1)->getSplitPoint();
}
void BlockList::printHeader(ostream &s) const
{
s << "List block ";
FlowBlock::printHeader(s);
}
int4 BlockCondition::flipInPlaceTest(vector<PcodeOp *> &fliplist) const
{
FlowBlock *split1 = getBlock(0)->getSplitPoint();
if (split1 == (FlowBlock *)0)
return 2;
FlowBlock *split2 = getBlock(1)->getSplitPoint();
if (split2 == (FlowBlock *)0)
return 2;
int4 subtest1 = split1->flipInPlaceTest(fliplist);
if (subtest1 == 2)
return 2;
int4 subtest2 = split2->flipInPlaceTest(fliplist);
if (subtest2 == 2)
return 2;
return subtest1;
}
void BlockCondition::flipInPlaceExecute(void)
{
opc = (opc==CPUI_BOOL_AND) ? CPUI_BOOL_OR : CPUI_BOOL_AND;
getBlock(0)->getSplitPoint()->flipInPlaceExecute();
getBlock(1)->getSplitPoint()->flipInPlaceExecute();
}
PcodeOp *BlockCondition::lastOp(void) const
{ // Is destination of condition reached
// by an unstructured goto
return getBlock(1)->lastOp();
}
bool BlockCondition::negateCondition(bool toporbottom)
{
bool res1,res2;
res1 = getBlock(0)->negateCondition(false); // Distribute the NOT
res2 = getBlock(1)->negateCondition(false); // to each side of condition
opc = (opc==CPUI_BOOL_AND) ? CPUI_BOOL_OR : CPUI_BOOL_AND;
FlowBlock::negateCondition(toporbottom); // Flip order of outofthis
return (res1 || res2);
}
void BlockCondition::scopeBreak(int4 curexit,int4 curloopexit)
{
getBlock(0)->scopeBreak(-1,curloopexit); // No fixed exit
getBlock(1)->scopeBreak(-1,curloopexit);
}
void BlockCondition::printHeader(ostream &s) const
{
s << "Condition block(";
if (opc==CPUI_BOOL_AND)
s << "&&";
else
s << "||";
s << ") ";
FlowBlock::printHeader(s);
}
FlowBlock *BlockCondition::nextFlowAfter(const FlowBlock *bl) const
{
return (FlowBlock *)0; // Do not know where flow goes
}
void BlockCondition::encodeHeader(Encoder &encoder) const
{
BlockGraph::encodeHeader(encoder);
string nm(get_opname(opc));
encoder.writeString(ATTRIB_OPCODE, nm);
}
void BlockIf::markUnstructured(void)
{
BlockGraph::markUnstructured(); // Recurse
if ((gototarget != (FlowBlock *)0)&&(gototype==f_goto_goto))
markCopyBlock(gototarget,f_unstructured_targ);
}
void BlockIf::scopeBreak(int4 curexit,int4 curloopexit)
{
getBlock(0)->scopeBreak(-1,curloopexit); // Condition block has multiple exits
// Blocks don't flow into one another, but share same exit block
for(int4 i=1;i<getSize();++i)
getBlock(i)->scopeBreak(curexit,curloopexit);
if ((gototarget != (FlowBlock *)0)&&(gototarget->getIndex() == curloopexit))
gototype = f_break_goto;
}
void BlockIf::printHeader(ostream &s) const
{
s << "If block ";
FlowBlock::printHeader(s);
}
bool BlockIf::preferComplement(Funcdata &data)
{
if (getSize()!=3) // If we are an if/else
return false;
FlowBlock *split = getBlock(0)->getSplitPoint();
if (split == (FlowBlock *)0)
return false;
vector<PcodeOp *> fliplist;
if (0 != split->flipInPlaceTest(fliplist))
return false;
split->flipInPlaceExecute();
opFlipInPlaceExecute(data,fliplist);
swapBlocks(1,2);
return true;
}
const FlowBlock *BlockIf::getExitLeaf(void) const
{ // In the special case of an ifgoto block, we do have an exit leaf
if (getSize() == 1)
return getBlock(0)->getExitLeaf();
return (FlowBlock *)0;
}
PcodeOp *BlockIf::lastOp(void) const
{ // In the special case of an ifgoto block, we do have a last op, otherwise we don't
if (getSize() == 1)
return getBlock(0)->lastOp();
return (PcodeOp *)0;
}
FlowBlock *BlockIf::nextFlowAfter(const FlowBlock *bl) const
{
if (getBlock(0)==bl)
return (FlowBlock *)0; // Do not know where flow goes
if (getParent() == (FlowBlock *)0)
return (FlowBlock *)0;
return getParent()->nextFlowAfter(this);
}
void BlockIf::encodeBody(Encoder &encoder) const
{
BlockGraph::encodeBody(encoder);
if (getSize() == 1) { // If this is a if GOTO block
const FlowBlock *leaf = gototarget->getFrontLeaf();
int4 depth = gototarget->calcDepth(leaf);
encoder.openElement(ELEM_TARGET);
encoder.writeSignedInteger(ATTRIB_INDEX, leaf->getIndex());
encoder.writeSignedInteger(ATTRIB_DEPTH, depth);
encoder.writeUnsignedInteger(ATTRIB_TYPE, gototype);
encoder.closeElement(ELEM_TARGET);
}
}
/// Try to find a Varnode that represents the controlling \e loop \e variable for \b this loop.
/// The Varnode must be:
/// - tested by the exit condition
/// - have a MULTIEQUAL in the head block
/// - have a modification coming in from the tail block
/// - the modification must be the last op or moveable to the last op
///
/// If the loop variable is found, this routine sets the \e iterateOp and the \e loopDef.
/// \param cbranch is the CBRANCH implementing the loop exit
/// \param head is the head basic-block of the loop
/// \param tail is the tail basic-block of the loop
/// \param lastOp is the precomputed last PcodeOp of tail that isn't a BRANCH
void BlockWhileDo::findLoopVariable(PcodeOp *cbranch,BlockBasic *head,BlockBasic *tail,PcodeOp *lastOp)
{
Varnode *vn = cbranch->getIn(1);
if (!vn->isWritten()) return; // No loop variable found
PcodeOp *op = vn->getDef();
int4 slot = tail->getOutRevIndex(0);
PcodeOpNode path[4];
int4 count = 0;
if (op->isCall() || op->isMarker()) {
return;
}
path[0].op = op;
path[0].slot = 0;
while(count>=0) {
PcodeOp *curOp = path[count].op;
int4 ind = path[count].slot++;
if (ind >= curOp->numInput()) {
count -= 1;
continue;
}
Varnode *nextVn = curOp->getIn(ind);
if (!nextVn->isWritten()) continue;
PcodeOp *defOp = nextVn->getDef();
if (defOp->code() == CPUI_MULTIEQUAL) {
if (defOp->getParent() != head) continue;
Varnode *itvn = defOp->getIn(slot);
if (!itvn->isWritten()) continue;
PcodeOp *possibleIterate = itvn->getDef();
if (possibleIterate->getParent() == tail) { // Found proper head/tail configuration
if (possibleIterate->isMarker())
continue; // No iteration in tail
if (!possibleIterate->isMoveable(lastOp))
continue; // Not the final statement
loopDef = defOp;
iterateOp = possibleIterate;
return; // Found the loop variable
}
}
else {
if (count == 3) continue;
if (defOp->isCall() || defOp->isMarker()) continue;
count += 1;
path[count].op = defOp;
path[count].slot = 0;
}
}
return; // No loop variable found
}
/// Given a control flow loop, try to find a putative initializer PcodeOp for the loop variable.
/// The initializer must be read by read by \e loopDef and by in a block that
/// flows only into the loop. If an initializer is found, then
/// \e initializeOp is set and the lastOp (not including a branch) in the initializer
/// block is returned. Otherwise null is returned.
/// \param head is the head block of the loop
/// \param slot is the block input coming from the loop tail
/// \return the last PcodeOp in the initializer's block
PcodeOp *BlockWhileDo::findInitializer(BlockBasic *head,int4 slot) const
{
if (head->sizeIn() != 2) return (PcodeOp *)0;
slot = 1 - slot;
Varnode *initVn = loopDef->getIn(slot);
if (!initVn->isWritten()) return (PcodeOp *)0;
PcodeOp *res = initVn->getDef();
if (res->isMarker()) return (PcodeOp *)0;
FlowBlock *initialBlock = res->getParent();
if (initialBlock != head->getIn(slot))
return (PcodeOp *)0; // Statement must terminate in block flowing to head
PcodeOp *lastOp = initialBlock->lastOp();
if (lastOp == (PcodeOp *)0) return (PcodeOp *)0;
if (initialBlock->sizeOut() != 1) return (PcodeOp *)0; // Initializer block must flow only to for loop
if (lastOp->isBranch()) {
lastOp = lastOp->previousOp();
if (lastOp == (PcodeOp *)0) return (PcodeOp *)0;
}
initializeOp = res;
return lastOp;
}
/// For-loop initializer or iterator statements must be the final statement in
/// their respective basic block. This method tests that iterateOp/initializeOp (specified
/// by \e slot) is the root of or can be turned into the root of a terminal statement.
/// The root output must be an explicit variable being read by the
/// \e loopDef MULTIEQUAL at the top of the loop. If the root is not the last
/// PcodeOp in the block, an attempt is made to move it.
/// Return the root PcodeOp if all these conditions are met, otherwise return null.
/// \param data is the function containing the while loop
/// \param slot is the slot read by \e loopDef from the output of the statement
/// \return an explicit statement or null
PcodeOp *BlockWhileDo::testTerminal(Funcdata &data,int4 slot) const
{
Varnode *vn = loopDef->getIn(slot);
if (!vn->isWritten()) return (PcodeOp *)0;
PcodeOp *finalOp = vn->getDef();
BlockBasic *parentBlock = (BlockBasic *)loopDef->getParent()->getIn(slot);
PcodeOp *resOp = finalOp;
if (finalOp->code() == CPUI_COPY && finalOp->notPrinted()) {
vn = finalOp->getIn(0);
if (!vn->isWritten()) return (PcodeOp *)0;
resOp = vn->getDef();
if (resOp->getParent() != parentBlock) return (PcodeOp *)0;
}
if (!vn->isExplicit()) return (PcodeOp *)0;
if (resOp->notPrinted())
return (PcodeOp *)0; // Statement MUST be printed
// finalOp MUST be the last op in the basic block (except for the branch)
PcodeOp *lastOp = finalOp->getParent()->lastOp();
if (lastOp->isBranch())
lastOp = lastOp->previousOp();
if (!data.moveRespectingCover(finalOp, lastOp))
return (PcodeOp *)0;
return resOp;
}
/// Make sure the loop variable is involved as input in the iterator statement.
/// \return \b true if the loop variable is an input to the iterator statement
bool BlockWhileDo::testIterateForm(void) const
{
Varnode *targetVn = loopDef->getOut();
HighVariable *high = targetVn->getHigh();
vector<PcodeOpNode> path;
PcodeOp *op = iterateOp;
path.push_back(PcodeOpNode(op,0));
while(!path.empty()) {
PcodeOpNode &node(path.back());
if (node.op->numInput() <= node.slot) {
path.pop_back();
continue;
}
Varnode *vn = node.op->getIn(node.slot);
node.slot += 1;
if (vn->isAnnotation()) continue;
if (vn->getHigh() == high) {
return true;
}
if (vn->isExplicit()) continue; // Truncate at explicit
if (!vn->isWritten()) continue;
op = vn->getDef();
path.push_back(PcodeOpNode(vn->getDef(),0));
}
return false;
}
void BlockWhileDo::markLabelBumpUp(bool bump)
{
BlockGraph::markLabelBumpUp(true); // whiledos steal lower blocks labels
if (!bump)
clearFlag(f_label_bumpup);
}
void BlockWhileDo::scopeBreak(int4 curexit,int4 curloopexit)
{
// A new loop scope (current loop exit becomes curexit)
getBlock(0)->scopeBreak(-1,curexit); // Top block has multiple exits
getBlock(1)->scopeBreak(getBlock(0)->getIndex(),curexit); // Exits into topblock
}
void BlockWhileDo::printHeader(ostream &s) const
{
s << "Whiledo block ";
if (hasOverflowSyntax())
s << "(overflow) ";
FlowBlock::printHeader(s);
}
FlowBlock *BlockWhileDo::nextFlowAfter(const FlowBlock *bl) const
{
if (getBlock(0) == bl)
return (FlowBlock *)0; // Don't know what will execute next
FlowBlock *nextbl = getBlock(0); // Will execute first block of while
if (nextbl != (FlowBlock *)0)
nextbl = nextbl->getFrontLeaf();
return nextbl;
}
/// Determine if \b this block can be printed as a \e for loop, with an \e initializer statement
/// extracted from the previous block, and an \e iterator statement extracted from the body.
/// \param data is the function containing \b this loop
void BlockWhileDo::finalTransform(Funcdata &data)
{
BlockGraph::finalTransform(data);
if (!data.getArch()->analyze_for_loops) return;
if (hasOverflowSyntax()) return;
FlowBlock *copyBl = getFrontLeaf();
if (copyBl == (FlowBlock *)0) return;
BlockBasic *head = (BlockBasic *)copyBl->subBlock(0);
if (head->getType() != t_basic) return;
PcodeOp *lastOp = getBlock(1)->lastOp(); // There must be a last op in body, for there to be an iterator statement
if (lastOp == (PcodeOp *)0) return;
BlockBasic *tail = lastOp->getParent();
if (tail->sizeOut() != 1) return;
if (tail->getOut(0) != head) return;
PcodeOp *cbranch = getBlock(0)->lastOp();
if (cbranch == (PcodeOp *)0 || cbranch->code() != CPUI_CBRANCH) return;
if (lastOp->isBranch()) { // Convert lastOp to -point- iterateOp must appear after
lastOp = lastOp->previousOp();
if (lastOp == (PcodeOp *)0) return;
}
findLoopVariable(cbranch, head, tail, lastOp);
if (iterateOp == (PcodeOp *)0) return;
if (iterateOp != lastOp) {
data.opUninsert(iterateOp);
data.opInsertAfter(iterateOp, lastOp);
}
// Try to set up initializer statement
lastOp = findInitializer(head, tail->getOutRevIndex(0));
if (lastOp == (PcodeOp *)0) return;
if (!initializeOp->isMoveable(lastOp)) {
initializeOp = (PcodeOp *)0; // Turn it off
return;
}
if (initializeOp != lastOp) {
data.opUninsert(initializeOp);
data.opInsertAfter(initializeOp, lastOp);
}
}
/// Assume that finalTransform() has run and that all HighVariable merging has occurred.
/// Do any final tests checking that the initialization and iteration statements are good.
/// Extract initialization and iteration statements from their basic blocks.
/// \param data is the function containing the loop
void BlockWhileDo::finalizePrinting(Funcdata &data) const
{
BlockGraph::finalizePrinting(data); // Continue recursing
if (iterateOp == (PcodeOp *)0) return; // For-loop printing not enabled
// TODO: We can check that iterate statement is not too complex
int4 slot = iterateOp->getParent()->getOutRevIndex(0);
iterateOp = testTerminal(data,slot); // Make sure iterator statement is explicit
if (iterateOp == (PcodeOp *)0) return;
if (!testIterateForm()) {
iterateOp = (PcodeOp *)0;
return;
}
if (initializeOp == (PcodeOp *)0)
findInitializer(loopDef->getParent(), slot); // Last chance initializer
if (initializeOp != (PcodeOp *)0)
initializeOp = testTerminal(data,1-slot); // Make sure initializer statement is explicit
data.opMarkNonPrinting(iterateOp);
if (initializeOp != (PcodeOp *)0)
data.opMarkNonPrinting(initializeOp);
}
void BlockDoWhile::markLabelBumpUp(bool bump)
{
BlockGraph::markLabelBumpUp(true); // dowhiles steal lower blocks labels
if (!bump)
clearFlag(f_label_bumpup);
}
void BlockDoWhile::scopeBreak(int4 curexit,int4 curloopexit)
{
// A new loop scope, current loop exit becomes curexit
getBlock(0)->scopeBreak(-1,curexit); // Multiple exits
}
void BlockDoWhile::printHeader(ostream &s) const
{
s << "Dowhile block ";
FlowBlock::printHeader(s);
}
FlowBlock *BlockDoWhile::nextFlowAfter(const FlowBlock *bl) const
{
return (FlowBlock *)0; // Don't know what will execute next
}
void BlockInfLoop::markLabelBumpUp(bool bump)
{
BlockGraph::markLabelBumpUp(true); // infloops steal lower blocks labels
if (!bump)
clearFlag(f_label_bumpup);
}
void BlockInfLoop::scopeBreak(int4 curexit,int4 curloopexit)
{
// A new loop scope, current loop exit becomes curexit
getBlock(0)->scopeBreak(getBlock(0)->getIndex(),curexit); // Exits into itself
}
void BlockInfLoop::printHeader(ostream &s) const
{
s << "Infinite loop block ";
FlowBlock::printHeader(s);
}
FlowBlock *BlockInfLoop::nextFlowAfter(const FlowBlock *bl) const
{
FlowBlock *nextbl = getBlock(0); // Will execute first block of infloop
if (nextbl != (FlowBlock *)0)
nextbl = nextbl->getFrontLeaf();
return nextbl;
}
BlockSwitch::BlockSwitch(FlowBlock *ind)
{
jump = ind->getJumptable();
}
/// Associate a structured block as a full \e case of \b this switch.
/// \param switchbl is the underlying switch statement block
/// \param bl is the new block to make into a case
/// \param gt gives the unstructured branch type if the switch edge to the new case was unstructured (zero otherwise)
void BlockSwitch::addCase(FlowBlock *switchbl,FlowBlock *bl,uint4 gt)
{
caseblocks.emplace_back();
CaseOrder &curcase( caseblocks.back() );
const FlowBlock *basicbl = bl->getFrontLeaf()->subBlock(0);
curcase.block = bl;
curcase.basicblock = basicbl;
curcase.label = 0;
curcase.depth = 0;
curcase.chain = -1;
int4 inindex = basicbl->getInIndex(switchbl);
if (inindex==-1)
throw LowlevelError("Case block has become detached from switch");
curcase.outindex = basicbl->getInRevIndex(inindex);
curcase.gototype = gt;
if (gt != 0)
curcase.isexit = false;
else
curcase.isexit = (bl->sizeOut() == 1);
curcase.isdefault = switchbl->isDefaultBranch( curcase.outindex );
}
/// Given the list of components for the switch structure, build the annotated descriptions
/// of the cases. Work out flow between cases and if there are any unstructured cases.
/// The first FlowBlock in the component list is the switch component itself. All other
/// FlowBlocks in the list are the \e case components.
/// \param switchbl is the underlying basic block, with multiple outgoing edges, for the switch
/// \param cs is the list of switch and case components
void BlockSwitch::grabCaseBasic(FlowBlock *switchbl,const vector<FlowBlock *> &cs)
{
vector<int4> casemap(switchbl->sizeOut(),-1); // Map from from switchtarget's outindex to position in caseblocks
caseblocks.clear();
for(int4 i=1;i<cs.size();++i) {
FlowBlock *casebl = cs[i];
addCase(switchbl,casebl,0);
casemap[caseblocks[i-1].outindex] = i-1; // Build map from outindex to caseblocks index
}
// Fillin fallthru chaining
for(int4 i=0;i<caseblocks.size();++i) {
CaseOrder &curcase( caseblocks[i] );
FlowBlock *casebl = curcase.block;
if (casebl->getType() == t_goto) { // All fall-thru blocks are plain gotos
FlowBlock *targetbl = ((BlockGoto *)casebl)->getGotoTarget();
const FlowBlock *basicbl = targetbl->getFrontLeaf()->subBlock(0);
int4 inindex = basicbl->getInIndex(switchbl);
if (inindex == -1) continue; // Goto target is not another switch case
curcase.chain = casemap[ basicbl->getInRevIndex(inindex) ];
}
}
if (cs[0]->getType() == t_multigoto) { // Check if some of the main switch edges were marked as goto
BlockMultiGoto *gotoedgeblock = (BlockMultiGoto *)cs[0];
int4 numgoto = gotoedgeblock->numGotos();
for(int4 i=0;i<numgoto;++i)
addCase(switchbl,gotoedgeblock->getGoto(i),f_goto_goto);
}
}
void BlockSwitch::finalizePrinting(Funcdata &data) const
{
BlockGraph::finalizePrinting(data); // Make sure to still recurse
// We need to order the cases based on the label
// First populate the label and depth fields of the CaseOrder objects
for(int4 i=0;i<caseblocks.size();++i) { // Construct the depth parameter, to sort fall-thru cases
CaseOrder &curcase( caseblocks[i] );
int4 j = curcase.chain;
while(j != -1) { // Run through the fall-thru chain
if (caseblocks[j].depth != 0) break; // Break any possible loops (already visited this node)
caseblocks[j].depth = -1; // Mark non-roots of chains
j = caseblocks[j].chain;
}
}
for(int4 i=0;i<caseblocks.size();++i) {
CaseOrder &curcase( caseblocks[i] );
if (jump->numIndicesByBlock(curcase.basicblock) > 0) {
if (curcase.depth == 0) { // Only set label on chain roots
int4 ind = jump->getIndexByBlock(curcase.basicblock,0);
curcase.label = jump->getLabelByIndex(ind);
int4 j = curcase.chain;
int4 depthcount = 1;
while(j != -1) {
if (caseblocks[j].depth > 0) break; // Has this node had its depth set. Break any possible loops.
caseblocks[j].depth = depthcount++;
caseblocks[j].label = curcase.label;
j = caseblocks[j].chain;
}
}
}
else
curcase.label = 0; // Should never happen
}
// Do actual sort of the cases based on label
stable_sort(caseblocks.begin(),caseblocks.end(),CaseOrder::compare);
}
/// Drill down to the variable associated with the BRANCHIND itself, and return its data-type
/// \return the Datatype associated with the switch variable
const Datatype *BlockSwitch::getSwitchType(void) const
{
PcodeOp *op = jump->getIndirectOp();
return op->getIn(0)->getHighTypeReadFacing(op);
}
void BlockSwitch::markUnstructured(void)
{
BlockGraph::markUnstructured(); // Recurse
for(int4 i=0;i<caseblocks.size();++i) {
if (caseblocks[i].gototype == f_goto_goto)
markCopyBlock(caseblocks[i].block,f_unstructured_targ);
}
}
void BlockSwitch::scopeBreak(int4 curexit,int4 curloopexit)
{
// New scope, current loop exit = curexit
getBlock(0)->scopeBreak(-1,curexit); // Top block has multiple exits
for(int4 i=0;i<caseblocks.size();++i) {
FlowBlock *bl = caseblocks[i].block;
if (caseblocks[i].gototype != 0) {
if (bl->getIndex() == curexit) // A goto that goes straight to exit, print is (empty) break
caseblocks[i].gototype = f_break_goto;
}
else {
// All case blocks are either plaingotos (curexit doesn't matter)
// exitpoints (exit to switches exit curexit = curexit)
bl->scopeBreak(curexit,curexit);
}
}
}
void BlockSwitch::printHeader(ostream &s) const
{
s << "Switch block ";
FlowBlock::printHeader(s);
}
FlowBlock *BlockSwitch::nextFlowAfter(const FlowBlock *bl) const
{
if (getBlock(0) == bl)
return (FlowBlock *)0; // Don't know what will execute
// Can only evaluate this if bl is a case block that falls through to another case block.
// Otherwise there is a break statement in the flow
if (bl->getType() != t_goto) // Fallthru must be a goto block
return (FlowBlock *)0;
int4 i;
// Look for block to find flow after
for(i=0;i<caseblocks.size();++i)
if (caseblocks[i].block == bl) break;
if (i==caseblocks.size()) return (FlowBlock *)0; // Didn't find block
i = i + 1; // Blocks are printed in fallthru order, "flow" is to next block in this order
if (i < caseblocks.size())
return caseblocks[i].block->getFrontLeaf();
// Otherwise we are at last block of switch, flow is to exit of switch
if (getParent() == (const FlowBlock *)0) return (FlowBlock *)0;
return getParent()->nextFlowAfter(this);
}
/// \param bt is the block_type
/// \return a new instance of the specialized FlowBlock
FlowBlock *BlockMap::resolveBlock(FlowBlock::block_type bt)
{
switch(bt) {
case FlowBlock::t_plain:
return new FlowBlock();
case FlowBlock::t_copy:
return new BlockCopy((FlowBlock *)0);
case FlowBlock::t_graph:
return new BlockGraph();
default:
break;
}
return (FlowBlock *)0;
}
/// Given a list of FlowBlock objects sorted by index, use binary search to find the FlowBlock with matching index
/// \param list is the sorted list of FlowBlock objects
/// \param ind is the FlowBlock index to match
/// \return the matching FlowBlock or NULL
FlowBlock *BlockMap::findBlock(const vector<FlowBlock *> &list,int4 ind)
{
int4 min = 0;
int4 max = list.size();
max -= 1;
while(min <= max) {
int4 mid = (min + max)/2;
FlowBlock *block = list[mid];
if (block->getIndex() == ind)
return block;
if (block->getIndex() < ind)
min = mid + 1;
else
max = mid -1;
}
return (FlowBlock *)0;
}
void BlockMap::sortList(void)
{
sort(sortlist.begin(),sortlist.end(),FlowBlock::compareBlockIndex);
}
/// Given the name of a block (deserialized from a \<bhead> tag), build the corresponding type of block.
/// \param name is the name of the block type
/// \return a new instance of the named FlowBlock type
FlowBlock *BlockMap::createBlock(const string &name)
{
FlowBlock::block_type bt = FlowBlock::nameToType(name);
FlowBlock *bl = resolveBlock(bt);
sortlist.push_back(bl);
return bl;
}
} // End namespace ghidra