mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
2366 lines
77 KiB
C++
2366 lines
77 KiB
C++
/* ###
|
|
* IP: GHIDRA
|
|
*
|
|
* 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 "blockaction.hh"
|
|
#include "funcdata.hh"
|
|
|
|
namespace ghidra {
|
|
|
|
/// Retrieve the current edge (as a \e top FlowBlock and the index of the outgoing edge).
|
|
/// If the end-points have been collapsed together, this returns NULL.
|
|
/// The top and bottom nodes of the edge are updated to FlowBlocks in the current collapsed graph.
|
|
/// \param outedge will hold the index of the edge (outgoing relative to returned FlowBlock)
|
|
/// \param graph is the containing BlockGraph
|
|
/// \return the current \e top of the edge or NULL
|
|
FlowBlock *FloatingEdge::getCurrentEdge(int4 &outedge,FlowBlock *graph)
|
|
|
|
{
|
|
while(top->getParent() != graph)
|
|
top = top->getParent(); // Move up through collapse hierarchy to current graph
|
|
while(bottom->getParent() != graph)
|
|
bottom = bottom->getParent();
|
|
outedge = top->getOutIndex(bottom);
|
|
if (outedge < 0)
|
|
return (FlowBlock *)0; // Edge does not exist (any longer)
|
|
return top;
|
|
}
|
|
|
|
/// \brief Find blocks in containing loop that aren't in \b this
|
|
///
|
|
/// Assuming \b this has all of its nodes marked, find all additional nodes that create the
|
|
/// body of the \b container loop. Mark these and put them in \b body list.
|
|
/// \param container is a loop that contains \b this
|
|
/// \param body will hold blocks in the body of the container that aren't in \b this
|
|
void LoopBody::extendToContainer(const LoopBody &container,vector<FlowBlock *> &body) const
|
|
|
|
{
|
|
int4 i = 0;
|
|
if (!container.head->isMark()) { // container head may already be in subloop, if not
|
|
container.head->setMark(); // add it to new body
|
|
body.push_back(container.head);
|
|
i = 1; // make sure we don't traverse back from it
|
|
}
|
|
for(int4 j=0;j<container.tails.size();++j) {
|
|
FlowBlock *tail = container.tails[j];
|
|
if (!tail->isMark()) { // container tail may already be in subloop, if not
|
|
tail->setMark();
|
|
body.push_back(tail); // add to body, make sure we DO traverse back from it
|
|
}
|
|
}
|
|
// -this- head is already marked, but hasn't been traversed
|
|
if (head != container.head) { // Unless the container has the same head, traverse the contained head
|
|
int4 sizein = head->sizeIn();
|
|
for(int4 k=0;k<sizein;++k) {
|
|
if (head->isGotoIn(k)) continue; // Don't trace back through irreducible edges
|
|
FlowBlock *bl = head->getIn(k);
|
|
if (bl->isMark()) continue; // Already in list
|
|
bl->setMark();
|
|
body.push_back(bl);
|
|
}
|
|
}
|
|
|
|
while(i < body.size()) {
|
|
FlowBlock *curblock = body[i++];
|
|
int4 sizein = curblock->sizeIn();
|
|
for(int4 k=0;k<sizein;++k) {
|
|
if (curblock->isGotoIn(k)) continue; // Don't trace back through irreducible edges
|
|
FlowBlock *bl = curblock->getIn(k);
|
|
if (bl->isMark()) continue; // Already in list
|
|
bl->setMark();
|
|
body.push_back(bl);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// This updates the \b head node to the FlowBlock in the current collapsed graph.
|
|
/// The \b tail nodes are also updated until one is found that has not collapsed into \b head.
|
|
/// This first updated \b tail is returned. The loop may still exist as a \b head node with an
|
|
/// out edge back into itself, in which case \b head is returned as the active \b tail.
|
|
/// If the loop has been completely collapsed, null is returned.
|
|
/// \param graph is the containing control-flow structure
|
|
/// \return the current loop \b tail or null
|
|
FlowBlock *LoopBody::update(FlowBlock *graph)
|
|
|
|
{
|
|
while(head->getParent() != graph)
|
|
head = head->getParent(); // Move up through collapse hierarchy to current graph
|
|
FlowBlock *bottom;
|
|
for(int4 i=0;i<tails.size();++i) {
|
|
bottom = tails[i];
|
|
while(bottom->getParent() != graph)
|
|
bottom = bottom->getParent();
|
|
tails[i] = bottom;
|
|
if (bottom != head) { // If the loop hasn't been fully collapsed yet
|
|
return bottom;
|
|
}
|
|
}
|
|
for(int4 i=head->sizeOut()-1;i>=0;--i) {
|
|
if (head->getOut(i) == head) // Check for head looping with itself
|
|
return head;
|
|
}
|
|
return (FlowBlock *)0;
|
|
}
|
|
|
|
/// Collect all FlowBlock nodes that reach a \b tail of the loop without going through \b head.
|
|
/// Put them in a list and mark them.
|
|
/// \param body will contain the body nodes
|
|
void LoopBody::findBase(vector<FlowBlock *> &body)
|
|
|
|
{
|
|
head->setMark();
|
|
body.push_back(head);
|
|
for(int4 j=0;j<tails.size();++j) {
|
|
FlowBlock *tail = tails[j];
|
|
if (!tail->isMark()) {
|
|
tail->setMark();
|
|
body.push_back(tail);
|
|
}
|
|
}
|
|
uniquecount = body.size(); // Number of nodes that either head or tail
|
|
int4 i=1;
|
|
while(i < body.size()) {
|
|
FlowBlock *curblock = body[i++];
|
|
int4 sizein = curblock->sizeIn();
|
|
for(int4 k=0;k<sizein;++k) {
|
|
if (curblock->isGotoIn(k)) continue; // Don't trace back through irreducible edges
|
|
FlowBlock *bl = curblock->getIn(k);
|
|
if (bl->isMark()) continue; // Already in list
|
|
bl->setMark();
|
|
body.push_back(bl);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Extend the \b body of this loop to every FlowBlock that can be reached
|
|
/// \b only from \b head without hitting the \b exitblock.
|
|
/// Assume \b body has been filled out by findBase() and that all these blocks have their mark set.
|
|
/// \param body contains the current loop body and will be extended
|
|
void LoopBody::extend(vector<FlowBlock *> &body) const
|
|
|
|
{
|
|
vector<FlowBlock *> trial;
|
|
int4 i=0;
|
|
while(i<body.size()) {
|
|
FlowBlock *bl = body[i++];
|
|
int4 sizeout = bl->sizeOut();
|
|
for(int4 j=0;j<sizeout;++j) {
|
|
if (bl->isGotoOut(j)) continue; // Don't extend through goto edge
|
|
FlowBlock *curbl = bl->getOut(j);
|
|
if (curbl->isMark()) continue;
|
|
if (curbl == exitblock) continue;
|
|
int4 count = curbl->getVisitCount();
|
|
if (count == 0)
|
|
trial.push_back(curbl); // New possible extension
|
|
count += 1;
|
|
curbl->setVisitCount(count);
|
|
if (count == curbl->sizeIn()) {
|
|
curbl->setMark();
|
|
body.push_back(curbl);
|
|
}
|
|
}
|
|
}
|
|
for(i=0;i<trial.size();++i)
|
|
trial[i]->setVisitCount(0); // Make sure to clear the count
|
|
}
|
|
|
|
/// A structured loop is allowed at most one exit block: pick this block.
|
|
/// First build a set of trial exits, preferring from a \b tail, then from \b head,
|
|
/// then from the middle. If there is no containing loop, just return the first such exit we find.
|
|
/// \param body is the list FlowBlock objects in the loop body, which we assume are marked.
|
|
void LoopBody::findExit(const vector<FlowBlock *> &body)
|
|
|
|
{
|
|
vector<FlowBlock *> trialexit;
|
|
FlowBlock *tail;
|
|
|
|
for(int4 j=0;j<tails.size();++j) {
|
|
tail = tails[j];
|
|
int4 sizeout = tail->sizeOut();
|
|
|
|
for(int4 i=0;i<sizeout;++i) {
|
|
if (tail->isGotoOut(i)) continue; // Don't use goto as exit edge
|
|
FlowBlock *curbl = tail->getOut(i);
|
|
if (!curbl->isMark()) {
|
|
if (immed_container == (LoopBody *)0) {
|
|
exitblock = curbl;
|
|
return;
|
|
}
|
|
trialexit.push_back(curbl);
|
|
}
|
|
}
|
|
}
|
|
|
|
for(int4 i=0;i<body.size();++i) {
|
|
FlowBlock *bl = body[i];
|
|
if ((i>0)&&(i<uniquecount)) continue; // Filter out tails (processed previously)
|
|
int4 sizeout = bl->sizeOut();
|
|
for(int4 j=0;j<sizeout;++j) {
|
|
if (bl->isGotoOut(j)) continue; // Don't use goto as exit edge
|
|
FlowBlock *curbl = bl->getOut(j);
|
|
if (!curbl->isMark()) {
|
|
if (immed_container == (LoopBody *)0) {
|
|
exitblock = curbl;
|
|
return;
|
|
}
|
|
trialexit.push_back(curbl);
|
|
}
|
|
}
|
|
}
|
|
|
|
exitblock = (FlowBlock *)0; // Default exit is null, if no block meeting condition can be found
|
|
if (trialexit.empty())
|
|
return;
|
|
|
|
// If there is a containing loop, force exitblock to be in the containing loop
|
|
if (immed_container != (LoopBody *)0) {
|
|
vector<FlowBlock *> extension;
|
|
extendToContainer(*immed_container,extension);
|
|
for(int4 i=0;i<trialexit.size();++i) {
|
|
FlowBlock *bl = trialexit[i];
|
|
if (bl->isMark()) {
|
|
exitblock = bl;
|
|
break;
|
|
}
|
|
}
|
|
clearMarks(extension);
|
|
}
|
|
}
|
|
|
|
/// The idea is if there is more than one \b tail for a loop, some tails are more "preferred" than others
|
|
/// and should have their exit edges preserved longer and be the target of the DAG path.
|
|
/// Currently we look for a single \b tail that has an outgoing edge to the \b exitblock and
|
|
/// make sure it is the first tail.
|
|
void LoopBody::orderTails(void)
|
|
|
|
{
|
|
if (tails.size() <= 1) return;
|
|
if (exitblock == (FlowBlock *)0) return;
|
|
int4 prefindex;
|
|
FlowBlock *trial;
|
|
for(prefindex=0;prefindex < tails.size(); ++prefindex) {
|
|
trial = tails[prefindex];
|
|
int4 sizeout = trial->sizeOut();
|
|
int4 j;
|
|
for(j=0;j<sizeout;++j)
|
|
if (trial->getOut(j) == exitblock) break;
|
|
if (j<sizeout) break;
|
|
}
|
|
if (prefindex >= tails.size()) return;
|
|
if (prefindex == 0) return;
|
|
tails[prefindex] = tails[0]; // Swap preferred tail into the first position
|
|
tails[0] = trial;
|
|
}
|
|
|
|
/// Label any edge that leaves the set of nodes in \b body.
|
|
/// Put the edges in priority for removal, middle exit at front, \e head exit, then \e tail exit.
|
|
/// We assume all the FlowBlock nodes in \b body have been marked.
|
|
/// \param body is list of nodes in \b this loop body
|
|
void LoopBody::labelExitEdges(const vector<FlowBlock *> &body)
|
|
|
|
{
|
|
vector<FlowBlock *> toexitblock;
|
|
for(int4 i=uniquecount;i<body.size();++i) { // For non-head/tail nodes of graph
|
|
FlowBlock *curblock = body[i];
|
|
int4 sizeout = curblock->sizeOut();
|
|
for(int4 k=0;k<sizeout;++k) {
|
|
if (curblock->isGotoOut(k)) continue; // Don't exit through goto edges
|
|
FlowBlock *bl = curblock->getOut(k);
|
|
if (bl == exitblock) {
|
|
toexitblock.push_back(curblock);
|
|
continue; // Postpone exit to exitblock
|
|
}
|
|
if (!bl->isMark())
|
|
exitedges.push_back(FloatingEdge(curblock,bl));
|
|
}
|
|
}
|
|
if (head != (FlowBlock *)0) {
|
|
int4 sizeout = head->sizeOut();
|
|
for(int4 k=0;k<sizeout;++k) {
|
|
if (head->isGotoOut(k)) continue; // Don't exit through goto edges
|
|
FlowBlock *bl = head->getOut(k);
|
|
if (bl == exitblock) {
|
|
toexitblock.push_back(head);
|
|
continue; // Postpone exit to exitblock
|
|
}
|
|
if (!bl->isMark())
|
|
exitedges.push_back(FloatingEdge(head,bl));
|
|
}
|
|
}
|
|
for(int4 i=tails.size()-1;i>=0;--i) { // Put exits from more preferred tails later
|
|
FlowBlock *curblock = tails[i];
|
|
if (curblock == head) continue;
|
|
int4 sizeout = curblock->sizeOut();
|
|
for(int4 k=0;k<sizeout;++k) {
|
|
if (curblock->isGotoOut(k)) continue; // Don't exit through goto edges
|
|
FlowBlock *bl = curblock->getOut(k);
|
|
if (bl == exitblock) {
|
|
toexitblock.push_back(curblock);
|
|
continue; // Postpone exit to exitblock
|
|
}
|
|
if (!bl->isMark())
|
|
exitedges.push_back(FloatingEdge(curblock,bl));
|
|
}
|
|
}
|
|
for(int4 i=0;i<toexitblock.size();++i) { // Now we do exits to exitblock
|
|
FlowBlock *bl = toexitblock[i];
|
|
exitedges.push_back(FloatingEdge(bl,exitblock));
|
|
}
|
|
}
|
|
|
|
/// \brief Record any loops that \b body contains.
|
|
///
|
|
/// Search for any loop contained by \b this and update is \b depth and \b immed_container field.
|
|
/// \param body is the set of FlowBlock nodes making up this loop
|
|
/// \param looporder is the list of known loops
|
|
void LoopBody::labelContainments(const vector<FlowBlock *> &body,const vector<LoopBody *> &looporder)
|
|
|
|
{
|
|
vector<LoopBody *> containlist;
|
|
|
|
for(int4 i=0;i<body.size();++i) {
|
|
FlowBlock *curblock = body[i];
|
|
if (curblock != head) {
|
|
LoopBody *subloop = LoopBody::find(curblock,looporder);
|
|
if (subloop != (LoopBody *)0) {
|
|
containlist.push_back(subloop);
|
|
subloop->depth += 1;
|
|
}
|
|
}
|
|
}
|
|
// Note the following code works even though the depth fields may shift during subsequent calls to this routine
|
|
// Once a LoopBody calls this routine
|
|
// the depth of any contained loop will permanently be bigger than this LoopBody
|
|
// because any other loop will either
|
|
// increment the depth of both this LoopBody and any loop that it contains OR
|
|
// increment neither the LoopBody nor a loop it contains OR
|
|
// NOT increment the LoopBody but DO increment a contained loop
|
|
// So when the immediate container a of loop b calls this routine
|
|
// a has a depth greater than any containing LoopBody that has already run
|
|
// => therefore b->immed_container->depth < a->depth and a claims the immed_container position
|
|
// Subsequent containers c of a and b, will have c->depth < a->depth because c contains a
|
|
for(int4 i=0;i<containlist.size();++i) { // Keep track of the most immediate container
|
|
LoopBody *lb = containlist[i];
|
|
if ((lb->immed_container == (LoopBody *)0)||(lb->immed_container->depth < depth))
|
|
lb->immed_container = this;
|
|
}
|
|
}
|
|
|
|
/// Add edges that exit from \b this loop body to the list of likely \e gotos,
|
|
/// giving them the proper priority.
|
|
/// \param likely will hold the exit edges in (reverse) priority order
|
|
/// \param graph is the containing control-flow graph
|
|
void LoopBody::emitLikelyEdges(list<FloatingEdge> &likely,FlowBlock *graph)
|
|
|
|
{
|
|
while(head->getParent() != graph)
|
|
head = head->getParent();
|
|
if (exitblock != (FlowBlock *)0) {
|
|
while(exitblock->getParent() != graph)
|
|
exitblock = exitblock->getParent();
|
|
}
|
|
for(int4 i=0;i<tails.size();++i) {
|
|
FlowBlock *tail = tails[i];
|
|
while(tail->getParent() != graph)
|
|
tail = tail->getParent();
|
|
tails[i] = tail;
|
|
if (tail == exitblock) // If the exitblock was collapsed into the tail, we no longer really have an exit
|
|
exitblock = (FlowBlock *)0;
|
|
}
|
|
list<FloatingEdge>::iterator iter,enditer;
|
|
iter = exitedges.begin();;
|
|
enditer = exitedges.end();
|
|
FlowBlock *holdin = (FlowBlock *)0;
|
|
FlowBlock *holdout = (FlowBlock *)0;
|
|
while(iter != enditer) {
|
|
int4 outedge;
|
|
FlowBlock *inbl = (*iter).getCurrentEdge(outedge,graph);
|
|
++iter;
|
|
if (inbl == (FlowBlock *)0) continue;
|
|
FlowBlock *outbl = inbl->getOut(outedge);
|
|
if (iter==enditer) {
|
|
if (outbl == exitblock) { // If this is the official exit edge
|
|
holdin = inbl; // Hold off putting the edge in list
|
|
holdout = outbl;
|
|
break;
|
|
}
|
|
}
|
|
likely.emplace_back(inbl,outbl);
|
|
}
|
|
for(int4 i=tails.size()-1;i>=0;--i) { // Go in reverse order, to put out less preferred back-edges first
|
|
if ((holdin!=(FlowBlock *)0)&&(i==0))
|
|
likely.emplace_back(holdin,holdout); // Put in delayed exit, right before final backedge
|
|
FlowBlock *tail = tails[i];
|
|
int4 sizeout = tail->sizeOut();
|
|
for(int4 j=0;j<sizeout;++j) {
|
|
FlowBlock *bl = tail->getOut(j);
|
|
if (bl == head) // If out edge to head (back-edge for this loop)
|
|
likely.emplace_back(tail,head); // emit it
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Exit edges have their f_loop_exit_edge property set.
|
|
/// \param graph is the containing control-flow structure
|
|
void LoopBody::setExitMarks(FlowBlock *graph)
|
|
|
|
{
|
|
list<FloatingEdge>::iterator iter;
|
|
for(iter=exitedges.begin();iter!=exitedges.end();++iter) {
|
|
int4 outedge;
|
|
FlowBlock *inloop = (*iter).getCurrentEdge(outedge,graph);
|
|
if (inloop != (FlowBlock *)0)
|
|
inloop->setLoopExit(outedge);
|
|
}
|
|
}
|
|
|
|
/// This clears the f_loop_exit_edge on any edge exiting \b this loop.
|
|
/// \param graph is the containing control-flow structure
|
|
void LoopBody::clearExitMarks(FlowBlock *graph)
|
|
|
|
{
|
|
list<FloatingEdge>::iterator iter;
|
|
for(iter=exitedges.begin();iter!=exitedges.end();++iter) {
|
|
int4 outedge;
|
|
FlowBlock *inloop = (*iter).getCurrentEdge(outedge,graph);
|
|
if (inloop != (FlowBlock *)0)
|
|
inloop->clearLoopExit(outedge);
|
|
}
|
|
}
|
|
|
|
/// Look for LoopBody records that share a \b head. Merge each \b tail
|
|
/// from one into the other. Set the merged LoopBody \b head to NULL,
|
|
/// for later clean up.
|
|
/// \param looporder is the list of LoopBody records
|
|
void LoopBody::mergeIdenticalHeads(vector<LoopBody *> &looporder)
|
|
|
|
{
|
|
int4 i=0;
|
|
int4 j=i+1;
|
|
|
|
LoopBody *curbody = looporder[i];
|
|
while(j < looporder.size()) {
|
|
LoopBody *nextbody = looporder[j++];
|
|
if (nextbody->head == curbody->head) {
|
|
curbody->addTail( nextbody->tails[0] );
|
|
nextbody->head = (FlowBlock *)0; // Mark this LoopBody as subsumed
|
|
}
|
|
else {
|
|
i += 1;
|
|
looporder[i] = nextbody;
|
|
curbody = nextbody;
|
|
}
|
|
}
|
|
i += 1; // Total size of merged array
|
|
looporder.resize(i);
|
|
}
|
|
|
|
/// Compare two loops based on the indices of the \b head and then the \e tail.
|
|
/// \param a is the first LoopBody to compare
|
|
/// \param b is the second LoopBody to compare
|
|
/// \return \b true if the first LoopBody comes before the second
|
|
bool LoopBody::compare_ends(LoopBody *a,LoopBody *b)
|
|
|
|
{
|
|
int4 aindex = a->head->getIndex();
|
|
int4 bindex = b->head->getIndex();
|
|
if (aindex != bindex)
|
|
return (aindex < bindex);
|
|
aindex = a->tails[0]->getIndex(); // Only compare the first tail
|
|
bindex = b->tails[0]->getIndex();
|
|
return (aindex < bindex);
|
|
}
|
|
|
|
/// Compare two loops based on the indices of the \b head
|
|
/// \param a is the first LoopBody to compare
|
|
/// \param looptop is the second
|
|
/// \return -1,0, or 1 if the first is ordered before, the same, or after the second
|
|
int4 LoopBody::compare_head(LoopBody *a,FlowBlock *looptop)
|
|
|
|
{
|
|
int4 aindex = a->head->getIndex();
|
|
int4 bindex = looptop->getIndex();
|
|
if (aindex != bindex)
|
|
return (aindex < bindex) ? -1 : 1;
|
|
return 0;
|
|
}
|
|
|
|
void TraceDAG::BranchPoint::createTraces(void)
|
|
|
|
{
|
|
int4 sizeout = top->sizeOut();
|
|
for(int4 i=0;i<sizeout;++i) {
|
|
if (!top->isLoopDAGOut(i)) continue;
|
|
paths.push_back( new BlockTrace(this,paths.size(),i) );
|
|
}
|
|
}
|
|
|
|
void TraceDAG::BranchPoint::markPath(void)
|
|
|
|
{
|
|
BranchPoint *cur = this;
|
|
do {
|
|
cur->ismark = !cur->ismark;
|
|
cur = cur->parent;
|
|
} while(cur != (BranchPoint *)0);
|
|
}
|
|
|
|
/// The \e distance is the number of edges from \b this up to the common
|
|
/// ancestor plus the number of edges down to the other BranchPoint.
|
|
/// We assume that \b this has had its path up to the root marked.
|
|
/// \param op2 is the other BranchPoint
|
|
/// \return the distance
|
|
int4 TraceDAG::BranchPoint::distance(BranchPoint *op2)
|
|
|
|
{
|
|
// find the common ancestor
|
|
BranchPoint *cur = op2;
|
|
do {
|
|
if (cur->ismark) { // Found the common ancestor
|
|
return (depth - cur->depth) + (op2->depth - cur->depth);
|
|
}
|
|
cur = cur->parent;
|
|
} while(cur != (BranchPoint *)0);
|
|
return depth + op2->depth + 1;
|
|
}
|
|
|
|
/// Get the first FlowBlock along the i-th BlockTrace path.
|
|
/// \param i is the index of the path
|
|
/// \return the first FlowBlock along the path
|
|
FlowBlock *TraceDAG::BranchPoint::getPathStart(int4 i)
|
|
|
|
{
|
|
int4 res=0;
|
|
int4 sizeout = top->sizeOut();
|
|
for(int4 j=0;j<sizeout;++j) {
|
|
if (!top->isLoopDAGOut(j)) continue;
|
|
if (res == i)
|
|
return top->getOut(j);
|
|
res += 1;
|
|
}
|
|
return (FlowBlock *)0;
|
|
}
|
|
|
|
TraceDAG::BranchPoint::BranchPoint(void)
|
|
|
|
{
|
|
parent = (BranchPoint *)0;
|
|
depth = 0;
|
|
pathout = -1;
|
|
ismark = false;
|
|
top = (FlowBlock *)0;
|
|
}
|
|
|
|
TraceDAG::BranchPoint::BranchPoint(BlockTrace *parenttrace)
|
|
|
|
{
|
|
parent = parenttrace->top;
|
|
depth = parent->depth + 1;
|
|
pathout = parenttrace->pathout;
|
|
ismark = false;
|
|
top = parenttrace->destnode;
|
|
createTraces();
|
|
}
|
|
|
|
TraceDAG::BranchPoint::~BranchPoint(void)
|
|
|
|
{
|
|
for(int4 i=0;i<paths.size();++i)
|
|
delete paths[i];
|
|
}
|
|
|
|
/// \param t is the parent BranchPoint
|
|
/// \param po is the index of the formal \e path out of the BranchPoint to \b this
|
|
/// \param eo is the edge index out of the BranchPoints root FlowBlock
|
|
TraceDAG::BlockTrace::BlockTrace(BranchPoint *t,int4 po,int4 eo)
|
|
|
|
{
|
|
flags = 0;
|
|
top = t;
|
|
pathout = po;
|
|
bottom = top->top;
|
|
destnode = bottom->getOut(eo);
|
|
edgelump = 1;
|
|
derivedbp = (BranchPoint *)0;
|
|
}
|
|
|
|
/// Attach BlockTrace to a virtual root BranchPoint, where there
|
|
/// isn't an explicit FlowBlock acting as branch point.
|
|
/// \param root is the virtual BranchPoint
|
|
/// \param po is the \e path out the BranchPoint to \b this
|
|
/// \param bl is the first FlowBlock along the path
|
|
TraceDAG::BlockTrace::BlockTrace(BranchPoint *root,int4 po,FlowBlock *bl)
|
|
|
|
{
|
|
flags = 0;
|
|
top = root;
|
|
pathout = po;
|
|
bottom = (FlowBlock *)0;
|
|
destnode = bl;
|
|
edgelump = 1;
|
|
derivedbp = (BranchPoint *)0;
|
|
}
|
|
|
|
/// \param op2 is the other BadEdgeScore to compare with \b this
|
|
/// \return true if \b this is LESS likely to be the bad edge than \b op2
|
|
bool TraceDAG::BadEdgeScore::compareFinal(const BadEdgeScore &op2) const
|
|
|
|
{
|
|
if (siblingedge != op2.siblingedge)
|
|
return (op2.siblingedge < siblingedge); // A bigger sibling edge is less likely to be the bad edge
|
|
// A sibling edge is more important than a terminal edge. Terminal edges have the most effect on
|
|
// node-joined returns, which usually doesn't happen on a switch edge, whereas switch's frequently
|
|
// exit to a terminal node
|
|
if (terminal !=op2.terminal)
|
|
return (terminal < op2.terminal);
|
|
if (distance != op2.distance)
|
|
return (distance < op2.distance); // Less distance between branchpoints means less likely to be bad
|
|
return (trace->top->depth < op2.trace->top->depth); // Less depth means less likely to be bad
|
|
}
|
|
|
|
/// Comparator for grouping BlockTraces with the same exit block and parent BranchPoint
|
|
/// \param op2 is the other BadEdgeScore to compare to
|
|
/// \return \b true is \b this should be ordered before \b op2
|
|
bool TraceDAG::BadEdgeScore::operator<(const BadEdgeScore &op2) const
|
|
|
|
{
|
|
int4 thisind = exitproto->getIndex();
|
|
int4 op2ind = op2.exitproto->getIndex();
|
|
if (thisind != op2ind) // Sort on exit block being traced to
|
|
return (thisind < op2ind);
|
|
FlowBlock *tmpbl = trace->top->top;
|
|
thisind = (tmpbl != (FlowBlock *)0) ? tmpbl->getIndex() : -1;
|
|
tmpbl = op2.trace->top->top;
|
|
op2ind = (tmpbl != (FlowBlock *)0) ? tmpbl->getIndex() : -1;
|
|
if (thisind != op2ind) // Then sort on branch point being traced from
|
|
return (thisind < op2ind);
|
|
thisind = trace->pathout;
|
|
op2ind = op2.trace->pathout; // Then on the branch being taken
|
|
return (thisind < op2ind);
|
|
}
|
|
|
|
/// This adds the BlockTrace to the list of potential unstructured edges.
|
|
/// Then patch up the BranchPoint/BlockTrace/pathout hierarchy.
|
|
/// \param trace is the indicated BlockTrace to remove
|
|
void TraceDAG::removeTrace(BlockTrace *trace)
|
|
|
|
{
|
|
// Record that we should now treat this edge like goto
|
|
likelygoto.emplace_back(trace->bottom,trace->destnode); // Create goto record
|
|
trace->destnode->setVisitCount( trace->destnode->getVisitCount() + trace->edgelump ); // Ignore edge(s)
|
|
|
|
BranchPoint *parentbp = trace->top;
|
|
|
|
if (trace->bottom != parentbp->top) { // If trace has moved past the root branch, we can treat trace as terminal
|
|
trace->flags |= BlockTrace::f_terminal;
|
|
trace->bottom = (FlowBlock *)0;
|
|
trace->destnode = (FlowBlock *)0;
|
|
trace->edgelump = 0;
|
|
// Do NOT remove from active list
|
|
return;
|
|
}
|
|
// Otherwise we need to actually remove the path from the BranchPoint as the root branch will be marked as a goto
|
|
removeActive(trace); // The trace will no longer be active
|
|
int4 size = parentbp->paths.size();
|
|
for(int4 i=trace->pathout+1;i<size;++i) { // Move every trace above -trace-s pathout down one slot
|
|
BlockTrace *movedtrace = parentbp->paths[i];
|
|
movedtrace->pathout -= 1; // Correct the trace's pathout
|
|
BranchPoint *derivedbp = movedtrace->derivedbp;
|
|
if (derivedbp != (BranchPoint *)0)
|
|
derivedbp->pathout -= 1; // Correct any derived BranchPoint's pathout
|
|
parentbp->paths[i-1] = movedtrace;
|
|
}
|
|
parentbp->paths.pop_back(); // Remove the vacated slot
|
|
|
|
delete trace; // Delete the record
|
|
}
|
|
|
|
/// \brief Process a set of conflicting BlockTrace objects that go to the same exit point.
|
|
///
|
|
/// For each conflicting BlockTrace, calculate the minimum distance between it and any other BlockTrace.
|
|
/// \param start is the beginning of the list of conflicting BlockTraces (annotated as BadEdgeScore)
|
|
/// \param end is the end of the list of conflicting BlockTraces
|
|
void TraceDAG::processExitConflict(list<BadEdgeScore>::iterator start,list<BadEdgeScore>::iterator end)
|
|
|
|
{
|
|
list<BadEdgeScore>::iterator iter;
|
|
BranchPoint *startbp;
|
|
|
|
while(start != end) {
|
|
iter = start;
|
|
++iter;
|
|
startbp = (*start).trace->top;
|
|
if (iter != end) {
|
|
startbp->markPath(); // Mark path to root, so we can find common ancestors easily
|
|
do {
|
|
if (startbp == (*iter).trace->top) { // Edge coming from same BranchPoint
|
|
(*start).siblingedge += 1;
|
|
(*iter).siblingedge += 1;
|
|
}
|
|
int4 dist = startbp->distance( (*iter).trace->top );
|
|
// Distance is symmetric with respect to the pair of traces,
|
|
// Update minimum for both traces
|
|
if (((*start).distance == -1)||((*start).distance > dist))
|
|
(*start).distance = dist;
|
|
if (((*iter).distance == -1)||((*iter).distance > dist))
|
|
(*iter).distance = dist;
|
|
++iter;
|
|
} while(iter != end);
|
|
startbp->markPath(); // Unmark the path
|
|
}
|
|
++start;
|
|
}
|
|
}
|
|
|
|
/// Run through the list of active BlockTrace objects, annotate them using
|
|
/// the BadEdgeScore class, then select the BlockTrace which is the most likely
|
|
/// candidate for an unstructured edge.
|
|
/// \return the BlockTrace corresponding to the unstructured edge
|
|
TraceDAG::BlockTrace *TraceDAG::selectBadEdge(void)
|
|
|
|
{
|
|
list<BadEdgeScore> badedgelist;
|
|
list<BlockTrace *>::const_iterator aiter;
|
|
for(aiter=activetrace.begin();aiter!=activetrace.end();++aiter) {
|
|
if ((*aiter)->isTerminal()) continue;
|
|
if (((*aiter)->top->top == (FlowBlock *)0)&&((*aiter)->bottom==(FlowBlock *)0))
|
|
continue; // Never remove virtual edges
|
|
badedgelist.emplace_back();
|
|
BadEdgeScore &score( badedgelist.back() );
|
|
score.trace = *aiter;
|
|
score.exitproto = score.trace->destnode;
|
|
score.distance = -1;
|
|
score.siblingedge = 0;
|
|
score.terminal = (score.trace->destnode->sizeOut()==0) ? 1 : 0;
|
|
}
|
|
badedgelist.sort();
|
|
|
|
list<BadEdgeScore>::iterator iter=badedgelist.begin();
|
|
list<BadEdgeScore>::iterator startiter = iter;
|
|
FlowBlock *curbl = (*iter).exitproto;
|
|
int4 samenodecount = 1;
|
|
++iter;
|
|
while(iter != badedgelist.end()) { // Find traces to the same exitblock
|
|
BadEdgeScore &score( *iter );
|
|
if (curbl == score.exitproto) {
|
|
samenodecount += 1; // Count another trace to the same exit
|
|
++iter;
|
|
}
|
|
else { // A new exit node
|
|
if (samenodecount > 1)
|
|
processExitConflict(startiter,iter);
|
|
curbl = score.exitproto;
|
|
startiter = iter;
|
|
samenodecount = 1;
|
|
++iter;
|
|
}
|
|
}
|
|
if (samenodecount > 1) // Process possible final group of traces exiting to same block
|
|
processExitConflict(startiter,iter);
|
|
|
|
iter = badedgelist.begin();
|
|
|
|
list<BadEdgeScore>::iterator maxiter = iter;
|
|
++iter;
|
|
while(iter != badedgelist.end()) {
|
|
if ((*maxiter).compareFinal( *iter )) {
|
|
maxiter = iter;
|
|
}
|
|
++iter;
|
|
}
|
|
return (*maxiter).trace;
|
|
}
|
|
|
|
/// \param trace is the BlockTrace to mark as \e active
|
|
void TraceDAG::insertActive(BlockTrace *trace)
|
|
|
|
{
|
|
activetrace.push_back(trace);
|
|
list<BlockTrace *>::iterator iter = activetrace.end();
|
|
--iter;
|
|
trace->activeiter = iter;
|
|
trace->flags |= BlockTrace::f_active;
|
|
activecount += 1;
|
|
}
|
|
|
|
/// \param trace is the BlockTrace to be unmarked
|
|
void TraceDAG::removeActive(BlockTrace *trace)
|
|
|
|
{
|
|
activetrace.erase(trace->activeiter);
|
|
trace->flags &= ~((uint4)BlockTrace::f_active);
|
|
activecount -= 1;
|
|
}
|
|
|
|
/// Verify the given BlockTrace can push into the next FlowBlock (\b destnode).
|
|
/// A FlowBlock node can only be \e opened if all the incoming edges have been traced.
|
|
/// \param trace is the given BlockTrace to push
|
|
/// \return \b true is the new node can be opened
|
|
bool TraceDAG::checkOpen(BlockTrace *trace)
|
|
|
|
{
|
|
if (trace->isTerminal()) return false; // Already been opened
|
|
bool isroot = false;
|
|
if (trace->top->depth == 0) {
|
|
if (trace->bottom == (FlowBlock *)0)
|
|
return true; // Artificial root can always open its first level (edge is not real edge)
|
|
isroot = true;
|
|
}
|
|
|
|
FlowBlock *bl = trace->destnode;
|
|
if ((bl == finishblock)&&(!isroot))
|
|
return false; // If there is a designated exit, only the root can open it
|
|
int4 ignore = trace->edgelump + bl->getVisitCount();
|
|
int4 count = 0;
|
|
for(int4 i=0;i<bl->sizeIn();++i) {
|
|
if (bl->isLoopDAGIn(i)) {
|
|
count += 1;
|
|
if (count > ignore) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Given that a BlockTrace can be opened into its next FlowBlock node,
|
|
/// create a new BranchPoint at that node, and set up new sub-traces.
|
|
/// \param parent is the given BlockTrace to split
|
|
/// \return an iterator (within the \e active list) to the new BlockTrace objects
|
|
list<TraceDAG::BlockTrace *>::iterator TraceDAG::openBranch(BlockTrace *parent)
|
|
|
|
{
|
|
BranchPoint *newbranch = new BranchPoint( parent );
|
|
parent->derivedbp = newbranch;
|
|
if (newbranch->paths.size() == 0) { // No new traces, return immediately to parent trace
|
|
delete newbranch;
|
|
parent->derivedbp = (BranchPoint *)0;
|
|
parent->flags |= BlockTrace::f_terminal; // marking it as terminal
|
|
parent->bottom = (FlowBlock *)0;
|
|
parent->destnode = (FlowBlock *)0;
|
|
parent->edgelump = 0;
|
|
return parent->activeiter;
|
|
}
|
|
removeActive(parent);
|
|
branchlist.push_back( newbranch );
|
|
for(int4 i=0;i<newbranch->paths.size();++i)
|
|
insertActive(newbranch->paths[i]);
|
|
return newbranch->paths[0]->activeiter;
|
|
}
|
|
|
|
/// For the given BlockTrace, make sure all other sibling BlockTraces from its
|
|
/// BranchPoint parent either terminate or flow to the same FlowBlock node.
|
|
/// If so, return \b true and pass back that node as the \b exitblock.
|
|
/// \param trace is the given BlockTrace
|
|
/// \param exitblock will hold the passed back exit block
|
|
/// \return \b true is the BlockTrace can be retired
|
|
bool TraceDAG::checkRetirement(BlockTrace *trace,FlowBlock *&exitblock)
|
|
|
|
{
|
|
if (trace->pathout != 0) return false; // Only check, if this is the first sibling
|
|
BranchPoint *bp = trace->top;
|
|
if (bp->depth == 0) { // Special conditions for retirement of root branch point
|
|
for(int4 i=0;i<bp->paths.size();++i) {
|
|
BlockTrace *curtrace = bp->paths[i];
|
|
if (!curtrace->isActive()) return false;
|
|
if (!curtrace->isTerminal()) return false; // All root paths must be terminal
|
|
}
|
|
return true;
|
|
}
|
|
FlowBlock *outblock = (FlowBlock *)0;
|
|
for(int4 i=0;i<bp->paths.size();++i) {
|
|
BlockTrace *curtrace = bp->paths[i];
|
|
if (!curtrace->isActive()) return false;
|
|
if (curtrace->isTerminal()) continue;
|
|
if (outblock == curtrace->destnode) continue;
|
|
if (outblock != (FlowBlock *)0) return false;
|
|
outblock = curtrace->destnode;
|
|
}
|
|
exitblock = outblock;
|
|
return true;
|
|
}
|
|
|
|
/// \brief Retire a BranchPoint, updating its parent BlockTrace
|
|
///
|
|
/// Knowing a given BranchPoint can be retired, remove all its BlockTraces
|
|
/// from the \e active list, and update the BranchPoint's parent BlockTrace
|
|
/// as having reached the BlockTrace exit point.
|
|
/// \param bp is the given BranchPoint
|
|
/// \param exitblock is unique exit FlowBlock (calculated by checkRetirement())
|
|
/// \return an iterator to the next \e active BlockTrace to examine
|
|
list<TraceDAG::BlockTrace *>::iterator TraceDAG::retireBranch(BranchPoint *bp,FlowBlock *exitblock)
|
|
|
|
{
|
|
FlowBlock *edgeout_bl = (FlowBlock *)0;
|
|
int4 edgelump_sum = 0;
|
|
|
|
for(int4 i=0;i<bp->paths.size();++i) {
|
|
BlockTrace *curtrace = bp->paths[i];
|
|
if (!curtrace->isTerminal()) {
|
|
edgelump_sum += curtrace->edgelump;
|
|
if (edgeout_bl == (FlowBlock *)0)
|
|
edgeout_bl = curtrace->bottom;
|
|
}
|
|
removeActive(curtrace); // Child traces are complete and no longer active
|
|
}
|
|
if (bp->depth == 0) // If this is the root block
|
|
return activetrace.begin(); // This is all there is to do
|
|
|
|
if (bp->parent != (BranchPoint *)0) {
|
|
BlockTrace *parenttrace = bp->parent->paths[bp->pathout];
|
|
parenttrace->derivedbp = (BranchPoint *)0; // Derived branchpoint is gone
|
|
if (edgeout_bl == (FlowBlock *)0) { // If all traces were terminal
|
|
parenttrace->flags |= BlockTrace::f_terminal;
|
|
parenttrace->bottom = (FlowBlock *)0;
|
|
parenttrace->destnode = (FlowBlock *)0;
|
|
parenttrace->edgelump = 0;
|
|
}
|
|
else {
|
|
parenttrace->bottom = edgeout_bl;
|
|
parenttrace->destnode = exitblock;
|
|
parenttrace->edgelump = edgelump_sum;
|
|
}
|
|
insertActive(parenttrace); // Parent trace gets re-activated
|
|
return parenttrace->activeiter;
|
|
}
|
|
return activetrace.begin();
|
|
}
|
|
|
|
/// The \b visitcount field is only modified in removeTrace() whenever we put an edge
|
|
/// in the \b likelygoto list.
|
|
void TraceDAG::clearVisitCount(void)
|
|
|
|
{
|
|
list<FloatingEdge>::const_iterator iter;
|
|
for(iter=likelygoto.begin();iter!=likelygoto.end();++iter)
|
|
(*iter).getBottom()->setVisitCount(0);
|
|
}
|
|
|
|
/// Prepare for a new trace using the provided storage for the likely unstructured
|
|
/// edges that will be discovered.
|
|
/// \param lg is the container for likely unstructured edges
|
|
TraceDAG::TraceDAG(list<FloatingEdge> &lg)
|
|
: likelygoto(lg)
|
|
{
|
|
activecount = 0;
|
|
finishblock = (FlowBlock *)0;
|
|
}
|
|
|
|
TraceDAG::~TraceDAG(void)
|
|
|
|
{
|
|
for(int4 i=0;i<branchlist.size();++i)
|
|
delete branchlist[i];
|
|
}
|
|
|
|
/// Given the registered root FlowBlocks, create the initial (virtual) BranchPoint
|
|
/// and an associated BlockTrace for each root FlowBlock.
|
|
void TraceDAG::initialize(void)
|
|
|
|
{
|
|
BranchPoint *rootBranch = new BranchPoint(); // Create a virtual BranchPoint for all entry points
|
|
branchlist.push_back(rootBranch);
|
|
|
|
for(uint4 i=0;i<rootlist.size();++i) { // Find the entry points
|
|
BlockTrace *newtrace = new BlockTrace(rootBranch,rootBranch->paths.size(),rootlist[i]);
|
|
rootBranch->paths.push_back(newtrace);
|
|
insertActive(newtrace);
|
|
}
|
|
}
|
|
|
|
/// From the root BranchPoint, recursively push the trace. At any point where pushing
|
|
/// is no longer possible, select an appropriate edge to remove and add it to the
|
|
/// list of likely unstructured edges. Then continue pushing the trace.
|
|
void TraceDAG::pushBranches(void)
|
|
|
|
{
|
|
FlowBlock *exitblock;
|
|
|
|
current_activeiter = activetrace.begin();
|
|
missedactivecount = 0;
|
|
while(activecount > 0) {
|
|
if (current_activeiter == activetrace.end())
|
|
current_activeiter = activetrace.begin();
|
|
BlockTrace *curtrace = *current_activeiter;
|
|
if (missedactivecount >= activecount) { // Could not push any trace further
|
|
BlockTrace *badtrace = selectBadEdge(); // So we pick an edge to be unstructured
|
|
removeTrace(badtrace); // destroy the trace
|
|
current_activeiter = activetrace.begin();
|
|
missedactivecount = 0;
|
|
}
|
|
else if (checkRetirement(curtrace,exitblock)) {
|
|
current_activeiter = retireBranch(curtrace->top,exitblock);
|
|
missedactivecount = 0;
|
|
}
|
|
else if (checkOpen(curtrace)) {
|
|
current_activeiter = openBranch(curtrace);
|
|
missedactivecount = 0;
|
|
}
|
|
else {
|
|
missedactivecount += 1;
|
|
current_activeiter++;
|
|
}
|
|
}
|
|
clearVisitCount();
|
|
}
|
|
|
|
/// Given the top FlowBlock of a loop, find corresponding LoopBody record from an ordered list.
|
|
/// This assumes mergeIdenticalHeads() has been run so that the head is uniquely identifying.
|
|
/// \param looptop is the top of the loop
|
|
/// \param looporder is the ordered list of LoopBody records
|
|
/// \return the LoopBody or NULL if none found
|
|
LoopBody *LoopBody::find(FlowBlock *looptop,const vector<LoopBody *> &looporder)
|
|
|
|
{
|
|
int4 min=0;
|
|
int4 max=looporder.size()-1;
|
|
while(min<=max) {
|
|
int4 mid = (min + max)/2;
|
|
int4 comp = compare_head(looporder[mid],looptop);
|
|
if (comp == 0) return looporder[mid];
|
|
if (comp < 0)
|
|
min = mid + 1;
|
|
else
|
|
max = mid - 1;
|
|
}
|
|
return (LoopBody *)0;
|
|
}
|
|
|
|
/// \param body is the list of FlowBlock nodes that have been marked
|
|
void LoopBody::clearMarks(vector<FlowBlock *> &body)
|
|
|
|
{
|
|
for(int4 i=0;i<body.size();++i)
|
|
body[i]->clearMark();
|
|
}
|
|
|
|
/// \brief Mark FlowBlocks \b only reachable from a given root
|
|
///
|
|
/// For a given root FlowBlock, find all the FlowBlocks that can only be reached from it,
|
|
/// mark them and put them in a list/
|
|
/// \param root is the given FlowBlock root
|
|
/// \param body is the container to hold the list of reachable nodes
|
|
void CollapseStructure::onlyReachableFromRoot(FlowBlock *root,vector<FlowBlock *> &body)
|
|
|
|
{
|
|
vector<FlowBlock *> trial;
|
|
int4 i=0;
|
|
root->setMark();
|
|
body.push_back(root);
|
|
while(i<body.size()) {
|
|
FlowBlock *bl = body[i++];
|
|
int4 sizeout = bl->sizeOut();
|
|
for(int4 j=0;j<sizeout;++j) {
|
|
FlowBlock *curbl = bl->getOut(j);
|
|
if (curbl->isMark()) continue;
|
|
int4 count = curbl->getVisitCount();
|
|
if (count == 0)
|
|
trial.push_back(curbl); // New possible extension
|
|
count += 1;
|
|
curbl->setVisitCount(count);
|
|
if (count == curbl->sizeIn()) {
|
|
curbl->setMark();
|
|
body.push_back(curbl);
|
|
}
|
|
}
|
|
}
|
|
for(i=0;i<trial.size();++i)
|
|
trial[i]->setVisitCount(0); // Make sure to clear the count
|
|
}
|
|
|
|
/// The FlowBlock objects in the \b body must all be marked.
|
|
/// \param body is the list of FlowBlock objects in the body
|
|
/// \return the number of edges that were marked as \e unstructured
|
|
int4 CollapseStructure::markExitsAsGotos(vector<FlowBlock *> &body)
|
|
|
|
{
|
|
int4 changecount = 0;
|
|
for(int4 i=0;i<body.size();++i) {
|
|
FlowBlock *bl = body[i];
|
|
int4 sizeout = bl->sizeOut();
|
|
for(int4 j=0;j<sizeout;++j) {
|
|
FlowBlock *curbl = bl->getOut(j);
|
|
if (!curbl->isMark()) {
|
|
bl->setGotoBranch(j); // mark edge as goto
|
|
changecount += 1;
|
|
}
|
|
}
|
|
}
|
|
return changecount;
|
|
}
|
|
|
|
/// Find distinct control-flow FlowBlock roots (having no incoming edges).
|
|
/// These delineate disjoint subsets of the control-flow graph, where a subset
|
|
/// is defined as the FlowBlock nodes that are only reachable from the root.
|
|
/// This method searches for one disjoint subset with \b cross-over edges,
|
|
/// edges from that subset into another. The exiting edges for this subset are marked
|
|
/// as \e unstructured \e gotos and \b true is returned.
|
|
/// \return true if any cross-over edges were found (and marked)
|
|
bool CollapseStructure::clipExtraRoots(void)
|
|
|
|
{
|
|
for(int4 i=1;i<graph.getSize();++i) { // Skip the canonical root
|
|
FlowBlock *bl = graph.getBlock(i);
|
|
if (bl->sizeIn() != 0) continue;
|
|
vector<FlowBlock *> body;
|
|
onlyReachableFromRoot(bl,body);
|
|
int4 count = markExitsAsGotos(body);
|
|
LoopBody::clearMarks(body);
|
|
if (count != 0)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Identify all the distinct loops in the graph (via their back-edge) and create a LoopBody record.
|
|
/// \param looporder is the container that will hold the LoopBody record for each loop
|
|
void CollapseStructure::labelLoops(vector<LoopBody *> &looporder)
|
|
|
|
{
|
|
for(int4 i=0;i<graph.getSize();++i) {
|
|
FlowBlock *bl = graph.getBlock(i);
|
|
int4 sizein = bl->sizeIn();
|
|
for(int4 j=0;j<sizein;++j) {
|
|
if (bl->isBackEdgeIn(j)) { // back-edge coming in must be from the bottom of a loop
|
|
FlowBlock *loopbottom = bl->getIn(j);
|
|
loopbody.emplace_back(bl);
|
|
LoopBody &curbody( loopbody.back() );
|
|
curbody.addTail(loopbottom);
|
|
looporder.push_back( & curbody );
|
|
}
|
|
}
|
|
}
|
|
sort(looporder.begin(),looporder.end(),LoopBody::compare_ends);
|
|
}
|
|
|
|
/// Find the loop bodies, then:
|
|
/// - Label all edges which exit their loops.
|
|
/// - Generate a partial order on the loop bodies.
|
|
void CollapseStructure::orderLoopBodies(void)
|
|
|
|
{
|
|
vector<LoopBody *> looporder;
|
|
labelLoops(looporder);
|
|
if (!loopbody.empty()) {
|
|
int4 oldsize = looporder.size();
|
|
LoopBody::mergeIdenticalHeads(looporder);
|
|
list<LoopBody>::iterator iter;
|
|
if (oldsize != looporder.size()) { // If there was merging
|
|
iter = loopbody.begin();
|
|
while(iter != loopbody.end()) {
|
|
if ((*iter).getHead() == (FlowBlock *)0) {
|
|
list<LoopBody>::iterator deliter = iter;
|
|
++iter;
|
|
loopbody.erase(deliter); // Delete the subsumed loopbody
|
|
}
|
|
else
|
|
++iter;
|
|
}
|
|
}
|
|
for(iter=loopbody.begin();iter!=loopbody.end();++iter) {
|
|
vector<FlowBlock *> body;
|
|
(*iter).findBase(body);
|
|
(*iter).labelContainments(body,looporder);
|
|
LoopBody::clearMarks(body);
|
|
}
|
|
loopbody.sort(); // Sort based on nesting depth (deepest come first) (sorting is stable)
|
|
for(iter=loopbody.begin();iter!=loopbody.end();++iter) {
|
|
vector<FlowBlock *> body;
|
|
(*iter).findBase(body);
|
|
(*iter).findExit(body);
|
|
(*iter).orderTails();
|
|
(*iter).extend(body);
|
|
(*iter).labelExitEdges(body);
|
|
LoopBody::clearMarks(body);
|
|
}
|
|
}
|
|
likelylistfull = false;
|
|
loopbodyiter = loopbody.begin();
|
|
}
|
|
|
|
/// Find the current innermost loop, make sure its \e likely \e goto edges are calculated.
|
|
/// If there are no loops, make sure the \e likely \e goto edges are calculated for the final DAG.
|
|
/// \return true if there are likely \e unstructured edges left to provide
|
|
bool CollapseStructure::updateLoopBody(void)
|
|
|
|
{
|
|
if (finaltrace) { // If we've already performed trace on DAG with no likely goto edges
|
|
return false; // don't repeat the trace
|
|
}
|
|
FlowBlock *loopbottom = (FlowBlock *)0;
|
|
FlowBlock *looptop = (FlowBlock *)0;
|
|
while (loopbodyiter != loopbody.end()) { // Last innermost loop
|
|
LoopBody &curBody( *loopbodyiter );
|
|
loopbottom = curBody.update(&graph);
|
|
if (loopbottom != (FlowBlock *)0) {
|
|
looptop = curBody.getHead();
|
|
if (loopbottom == looptop) { // Check for single node looping back to itself
|
|
// If sizeout is 1 or 2, the loop would have collapsed, so the node is likely a switch.
|
|
likelygoto.clear();
|
|
likelygoto.emplace_back(looptop,looptop); // Mark the loop edge as a goto
|
|
likelyiter = likelygoto.begin();
|
|
likelylistfull = true;
|
|
return true;
|
|
}
|
|
if (!likelylistfull || likelyiter != likelygoto.end()) {
|
|
break; // Loop still exists
|
|
}
|
|
}
|
|
++loopbodyiter;
|
|
likelylistfull = false; // Need to generate likely list for new loopbody (or no loopbody)
|
|
loopbottom = (FlowBlock *)0;
|
|
}
|
|
if (likelylistfull && likelyiter != likelygoto.end())
|
|
return true;
|
|
|
|
// If we reach here, need to generate likely gotos for a new inner loop or DAG
|
|
likelygoto.clear(); // Clear out any old likely gotos from last inner loop
|
|
TraceDAG tracer(likelygoto);
|
|
if (loopbottom != (FlowBlock *)0) {
|
|
tracer.addRoot( looptop ); // Trace from the top of the loop
|
|
tracer.setFinishBlock(loopbottom);
|
|
(*loopbodyiter).setExitMarks(&graph); // Set the bounds of the TraceDAG
|
|
}
|
|
else {
|
|
for(uint4 i=0;i<graph.getSize();++i) {
|
|
FlowBlock *bl = graph.getBlock(i);
|
|
if (bl->sizeIn() == 0)
|
|
tracer.addRoot(bl);
|
|
}
|
|
}
|
|
tracer.initialize();
|
|
tracer.pushBranches();
|
|
likelylistfull = true; // Mark likelygoto generation complete for current loop or DAG
|
|
if (loopbottom != (FlowBlock *)0) {
|
|
(*loopbodyiter).emitLikelyEdges(likelygoto,&graph);
|
|
(*loopbodyiter).clearExitMarks(&graph);
|
|
}
|
|
else if (likelygoto.empty()) {
|
|
finaltrace = true; // No loops left and trace didn't find gotos
|
|
return false;
|
|
}
|
|
likelyiter = likelygoto.begin();
|
|
return true;
|
|
}
|
|
|
|
/// Pick an edge from among the \e likely \e goto list generated by a
|
|
/// trace of the current innermost loop. Given ongoing collapsing, this
|
|
/// may involve updating which loop is currently innermost and throwing
|
|
/// out potential edges whose endpoints have already been collapsed.
|
|
/// \return the FlowBlock whose outgoing edge was marked \e unstructured or NULL
|
|
FlowBlock *CollapseStructure::selectGoto(void)
|
|
|
|
{
|
|
while(updateLoopBody()) {
|
|
while(likelyiter != likelygoto.end()) {
|
|
int4 outedge;
|
|
FlowBlock *startbl = (*likelyiter).getCurrentEdge(outedge,&graph);
|
|
++likelyiter;
|
|
if (startbl != (FlowBlock *)0) {
|
|
startbl->setGotoBranch(outedge); // Mark the selected branch as goto
|
|
return startbl;
|
|
}
|
|
}
|
|
}
|
|
if (!clipExtraRoots())
|
|
throw LowlevelError("Could not finish collapsing block structure");
|
|
return (FlowBlock *)0;
|
|
}
|
|
|
|
/// Try to concatenate a straight sequences of blocks starting with the given FlowBlock.
|
|
/// All of the internal edges should be DAG (no \e exit, \e goto,or \e loopback).
|
|
/// The final edge can be an exit or loopback
|
|
/// \param bl is the given FlowBlock
|
|
/// \return \b true if the structure was applied
|
|
bool CollapseStructure::ruleBlockCat(FlowBlock *bl)
|
|
|
|
{
|
|
FlowBlock *outblock,*outbl2;
|
|
|
|
if (bl->sizeOut() != 1) return false;
|
|
if (bl->isSwitchOut()) return false;
|
|
if ((bl->sizeIn()==1)&&(bl->getIn(0)->sizeOut()==1)) return false; // Must be start of chain
|
|
outblock = bl->getOut(0);
|
|
if (outblock == bl) return false; // No looping
|
|
if (outblock->sizeIn() != 1) return false; // Nothing else can hit outblock
|
|
if (!bl->isDecisionOut(0)) return false; // Not a goto or a loopbottom
|
|
if (outblock->isSwitchOut()) return false; // Switch must be resolved first
|
|
|
|
vector<FlowBlock *> nodes;
|
|
nodes.push_back(bl); // The first two blocks being concatenated
|
|
nodes.push_back(outblock);
|
|
|
|
while(outblock->sizeOut()==1) {
|
|
outbl2 = outblock->getOut(0);
|
|
if (outbl2 == bl) break; // No looping
|
|
if (outbl2->sizeIn() != 1) break; // Nothing else can hit outblock
|
|
if (!outblock->isDecisionOut(0)) break; // Don't use loop bottom
|
|
if (outbl2->isSwitchOut()) break; // Switch must be resolved first
|
|
outblock = outbl2;
|
|
nodes.push_back(outblock); // Extend the cat chain
|
|
}
|
|
|
|
graph.newBlockList(nodes); // Concatenate the nodes into a single block
|
|
return true;
|
|
}
|
|
|
|
/// Try to find an OR conditions (finding ANDs by duality) starting with the given FlowBlock.
|
|
/// The top of the OR should not perform \e gotos, the edge to the \b orblock should not
|
|
/// be \e exit or \e loopback
|
|
/// \param bl is the given FlowBlock
|
|
/// \return \b true if the structure was applied
|
|
bool CollapseStructure::ruleBlockOr(FlowBlock *bl)
|
|
|
|
{
|
|
FlowBlock *orblock,*clauseblock;
|
|
int4 i,j;
|
|
|
|
if (bl->sizeOut() != 2) return false;
|
|
if (bl->isGotoOut(0)) return false;
|
|
if (bl->isGotoOut(1)) return false;
|
|
if (bl->isSwitchOut()) return false;
|
|
// NOTE: complex behavior can happen in the first block because we (may) only
|
|
// print the branch
|
|
// if (bl->isComplex()) return false; // Control flow too complicated for condition
|
|
for(i=0;i<2;++i) {
|
|
orblock = bl->getOut(i); // False out is other part of OR
|
|
if (orblock==bl) continue; // orblock cannot be same block
|
|
if (orblock->sizeIn()!=1) continue; // Nothing else can hit orblock
|
|
if (orblock->sizeOut()!=2) continue; // orblock must also be binary condition
|
|
if (orblock->isInteriorGotoTarget()) continue; // No unstructured jumps into or
|
|
if (orblock->isSwitchOut()) continue;
|
|
if (bl->isBackEdgeOut(i)) continue; // Don't use loop branch to get to orblock
|
|
if (orblock->isComplex()) continue;
|
|
// This line was always commented out. I assume minor
|
|
// block order variations were screwing up this rule
|
|
clauseblock = bl->getOut(1-i);
|
|
if (clauseblock == bl) continue; // No looping
|
|
if (clauseblock == orblock) continue;
|
|
for(j=0;j<2;++j) {
|
|
if (clauseblock != orblock->getOut(j)) continue; // Clauses don't match
|
|
break;
|
|
}
|
|
if (j==2) continue;
|
|
if (orblock->getOut(1-j) == bl) continue; // No looping
|
|
|
|
// Do we need to check that
|
|
// bl->isBackEdgeOut(i) => orblock->isBackEdgeOut(j)
|
|
// bl->isLoopExitOut(i) => orblock->isLoopExitOut(j)
|
|
if (i==1) { // orblock needs to be false out of bl
|
|
if (bl->negateCondition(true))
|
|
dataflow_changecount += 1;
|
|
}
|
|
if (j==0) { // clauseblock needs to be true out of orblock
|
|
if (orblock->negateCondition(true))
|
|
dataflow_changecount += 1;
|
|
}
|
|
|
|
graph.newBlockCondition(bl,orblock);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Try to structure a \e proper if structure (with no \b else clause) starting from the given FlowBlock.
|
|
/// The edge to the clause should not be an \e exit or \e loopbottom.
|
|
/// The outgoing edges can be \e exit or \e loopbottom.
|
|
/// \param bl is the given FlowBlock
|
|
/// \return \b true if the structure was applied
|
|
bool CollapseStructure::ruleBlockProperIf(FlowBlock *bl)
|
|
|
|
{
|
|
FlowBlock *clauseblock,*outblock;
|
|
int4 i;
|
|
|
|
if (bl->sizeOut() != 2) return false; // Must be binary condition
|
|
if (bl->isSwitchOut()) return false;
|
|
if (bl->getOut(0) == bl) return false; // No loops
|
|
if (bl->getOut(1) == bl) return false;
|
|
if (bl->isGotoOut(0)) return false; // Neither branch must be unstructured
|
|
if (bl->isGotoOut(1)) return false;
|
|
for(i=0;i<2;++i) {
|
|
clauseblock = bl->getOut(i);
|
|
if (clauseblock->sizeIn() != 1) continue; // Nothing else can hit clauseblock
|
|
if (clauseblock->sizeOut() != 1) continue; // Only one way out of clause
|
|
if (clauseblock->isSwitchOut()) continue; // Don't use switch (possibly with goto edges)
|
|
if (!bl->isDecisionOut(i)) continue; // Don't use loopbottom or exit
|
|
if (clauseblock->isGotoOut(0)) continue; // No unstructured jumps out of clause
|
|
outblock = clauseblock->getOut(0);
|
|
if (outblock != bl->getOut(1-i)) continue; // Path after clause must be the same
|
|
|
|
if (i==0) { // Clause must be true
|
|
if (bl->negateCondition(true))
|
|
dataflow_changecount += 1;
|
|
}
|
|
|
|
graph.newBlockIf(bl,clauseblock);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Try to find an if/else structure starting with the given FlowBlock.
|
|
/// Edges into the clauses cannot be \e goto, \e exit,or \e loopback.
|
|
/// The returning edges can be \e exit or \e loopback.
|
|
/// \param bl is the given FlowBlock
|
|
/// \return \b true if the structure was applied
|
|
bool CollapseStructure::ruleBlockIfElse(FlowBlock *bl)
|
|
|
|
{
|
|
FlowBlock *tc,*fc,*outblock;
|
|
|
|
if (bl->sizeOut() != 2) return false; // Must be binary condition
|
|
if (bl->isSwitchOut()) return false;
|
|
if (!bl->isDecisionOut(0)) return false;
|
|
if (!bl->isDecisionOut(1)) return false;
|
|
|
|
tc = bl->getTrueOut();
|
|
fc = bl->getFalseOut();
|
|
if (tc->sizeIn() != 1) return false; // Nothing else must hit true clause
|
|
if (fc->sizeIn() != 1) return false; // Nothing else must hit false clause
|
|
|
|
if (tc->sizeOut() != 1) return false; // Only one exit from clause
|
|
if (fc->sizeOut() != 1) return false; // Only one exit from clause
|
|
outblock = tc->getOut(0);
|
|
if (outblock == bl) return false; // No loops
|
|
if (outblock != fc->getOut(0)) return false; // Clauses must exit to same place
|
|
|
|
if (tc->isSwitchOut()) return false;
|
|
if (fc->isSwitchOut()) return false;
|
|
if (tc->isGotoOut(0)) return false;
|
|
if (fc->isGotoOut(0)) return false;
|
|
|
|
graph.newBlockIfElse(bl,tc,fc);
|
|
return true;
|
|
}
|
|
|
|
/// For the given FlowBlock, look for an outgoing edge marked as \e unstructured.
|
|
/// Create or update the BlockGoto or BlockMultiGoto structure.
|
|
/// \param bl is the given FlowBlock
|
|
/// \return \b true if the structure was applied
|
|
bool CollapseStructure::ruleBlockGoto(FlowBlock *bl)
|
|
|
|
{
|
|
int4 sizeout = bl->sizeOut();
|
|
for(int4 i=0;i<sizeout;++i) {
|
|
if (bl->isGotoOut(i)) {
|
|
if (bl->isSwitchOut()) {
|
|
graph.newBlockMultiGoto(bl,i);
|
|
return true;
|
|
}
|
|
if (sizeout == 2) {
|
|
if (!bl->isGotoOut(1)) { // True branch must be goto
|
|
if (bl->negateCondition(true))
|
|
dataflow_changecount += 1;
|
|
}
|
|
graph.newBlockIfGoto(bl);
|
|
return true;
|
|
}
|
|
if (sizeout == 1) {
|
|
graph.newBlockGoto(bl);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Try to find an if structure, where the condition clause does not exit,
|
|
/// starting with the given FlowBlock.
|
|
/// \param bl is the given FlowBlock
|
|
/// \return \b true if the structure was applied
|
|
bool CollapseStructure::ruleBlockIfNoExit(FlowBlock *bl)
|
|
|
|
{
|
|
FlowBlock *clauseblock;
|
|
int4 i;
|
|
|
|
if (bl->sizeOut() != 2) return false; // Must be binary condition
|
|
if (bl->isSwitchOut()) return false;
|
|
if (bl->getOut(0) == bl) return false; // No loops
|
|
if (bl->getOut(1) == bl) return false;
|
|
if (bl->isGotoOut(0)) return false;
|
|
if (bl->isGotoOut(1)) return false;
|
|
for(i=0;i<2;++i) {
|
|
clauseblock = bl->getOut(i);
|
|
if (clauseblock->sizeIn() != 1) continue; // Nothing else must hit clause
|
|
if (clauseblock->sizeOut() != 0) continue; // Must be no way out of clause
|
|
if (clauseblock->isSwitchOut()) continue;
|
|
if (!bl->isDecisionOut(i)) continue;
|
|
// if (clauseblock->isInteriorGotoTarget()) {
|
|
// bl->setGotoBranch(i);
|
|
// return true;
|
|
// }
|
|
|
|
if (i==0) { // clause must be true out of bl
|
|
if (bl->negateCondition(true))
|
|
dataflow_changecount += 1;
|
|
}
|
|
graph.newBlockIf(bl,clauseblock);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Try to find a while/do structure, starting with a given FlowBlock.
|
|
/// Any \e break or \e continue must have already been collapsed as some form of \e goto.
|
|
/// \param bl is the given FlowBlock
|
|
/// \return \b true if the structure was applied
|
|
bool CollapseStructure::ruleBlockWhileDo(FlowBlock *bl)
|
|
|
|
{
|
|
FlowBlock *clauseblock;
|
|
int4 i;
|
|
|
|
if (bl->sizeOut() != 2) return false; // Must be binary condition
|
|
if (bl->isSwitchOut()) return false;
|
|
if (bl->getOut(0) == bl) return false; // No loops at this point
|
|
if (bl->getOut(1) == bl) return false;
|
|
if (bl->isInteriorGotoTarget()) return false;
|
|
if (bl->isGotoOut(0)) return false;
|
|
if (bl->isGotoOut(1)) return false;
|
|
for(i=0;i<2;++i) {
|
|
clauseblock = bl->getOut(i);
|
|
if (clauseblock->sizeIn() != 1) continue; // Nothing else must hit clause
|
|
if (clauseblock->sizeOut() != 1) continue; // Only one way out of clause
|
|
if (clauseblock->isSwitchOut()) continue;
|
|
if (clauseblock->getOut(0) != bl) continue; // Clause must loop back to bl
|
|
|
|
bool overflow = bl->isComplex(); // Check if we need to use overflow syntax
|
|
if ((i==0)!=overflow) { // clause must be true out of bl unless we use overflow syntax
|
|
if (bl->negateCondition(true))
|
|
dataflow_changecount += 1;
|
|
}
|
|
BlockWhileDo *newbl = graph.newBlockWhileDo(bl,clauseblock);
|
|
if (overflow)
|
|
newbl->setOverflowSyntax();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Try to find a do/while structure, starting with the given FlowBlock.
|
|
/// Any \e break and \e continue must have already been collapsed as some form of \e goto.
|
|
/// \param bl is the given FlowBlock
|
|
/// \return \b true if the structure was applied
|
|
bool CollapseStructure::ruleBlockDoWhile(FlowBlock *bl)
|
|
|
|
{
|
|
int4 i;
|
|
|
|
if (bl->sizeOut() != 2) return false; // Must be binary condition
|
|
if (bl->isSwitchOut()) return false;
|
|
if (bl->isGotoOut(0)) return false;
|
|
if (bl->isGotoOut(1)) return false;
|
|
for(i=0;i<2;++i) {
|
|
if (bl->getOut(i) != bl) continue; // Must loop back on itself
|
|
if (i==0) { // must loop on true condition
|
|
if (bl->negateCondition(true))
|
|
dataflow_changecount += 1;
|
|
}
|
|
graph.newBlockDoWhile(bl);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Try to find a loop structure with no exits, starting at the given FlowBlock.
|
|
/// \param bl is the given FlowBlock
|
|
/// \return \b true if the structure was applied
|
|
bool CollapseStructure::ruleBlockInfLoop(FlowBlock *bl)
|
|
|
|
{
|
|
if (bl->sizeOut() != 1) return false; // Must only be one way out
|
|
|
|
// If the single out edge is from a switch (BRANCHIND) and also forms an infinite
|
|
// loop, the ruleBlockSwitch method will not hit because the switch won't have a
|
|
// proper exit block. So we let this method collapse it by NOT checking for switch.
|
|
// if (bl->isSwitchOut()) return false;
|
|
|
|
if (bl->isGotoOut(0)) return false;
|
|
if (bl->getOut(0) != bl) return false; // Must fall into itself
|
|
graph.newBlockInfLoop(bl);
|
|
return true;
|
|
}
|
|
|
|
/// \brief Check for switch edges that go straight to the exit block
|
|
///
|
|
/// Some switch forms have edges that effectively skip the body of the switch and go straight to the exit
|
|
/// Many jumptables schemes have a \e default (i.e. if nothing else matches) edge. This edge cannot be a normal
|
|
/// \b case because there would be too many labels to explicitly specify. The edge must either be labeled as
|
|
/// \e default or it must go straight to the exit block. If there is a \e default edge, if it does not go
|
|
/// straight to the exit, there can be no other edge that skips straight to the exit.
|
|
///
|
|
/// If such skip edges exist, they are converted to gotos and \b false is returned.
|
|
/// \param switchbl is the entry FlowBlock for the switch
|
|
/// \param exitblock is the designated exit FlowBlock for the switch
|
|
/// \return true if there are no \e skip edges
|
|
bool CollapseStructure::checkSwitchSkips(FlowBlock *switchbl,FlowBlock *exitblock)
|
|
|
|
{
|
|
if (exitblock == (FlowBlock *)0) return true;
|
|
|
|
// Is there a "default" edge that goes straight to the exitblock
|
|
int4 sizeout,edgenum;
|
|
sizeout = switchbl->sizeOut();
|
|
bool defaultnottoexit = false;
|
|
bool anyskiptoexit = false;
|
|
for(edgenum=0;edgenum<sizeout;++edgenum) {
|
|
if (switchbl->getOut(edgenum) == exitblock) {
|
|
if (!switchbl->isDefaultBranch(edgenum))
|
|
anyskiptoexit = true;
|
|
}
|
|
else {
|
|
if (switchbl->isDefaultBranch(edgenum))
|
|
defaultnottoexit = true;
|
|
}
|
|
}
|
|
|
|
if (!anyskiptoexit) return true;
|
|
|
|
if ((!defaultnottoexit)&&(switchbl->getType() == FlowBlock::t_multigoto)) {
|
|
BlockMultiGoto *multibl = (BlockMultiGoto *)switchbl;
|
|
if (multibl->hasDefaultGoto())
|
|
defaultnottoexit = true;
|
|
}
|
|
if (!defaultnottoexit) return true;
|
|
|
|
for(edgenum=0;edgenum<sizeout;++edgenum) {
|
|
if (switchbl->getOut(edgenum) == exitblock) {
|
|
if (!switchbl->isDefaultBranch(edgenum))
|
|
switchbl->setGotoBranch(edgenum);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Try to find a switch structure, starting with the given FlowBlock.
|
|
/// \param bl is the given FlowBlock
|
|
/// \return \b true if the structure was applied
|
|
bool CollapseStructure::ruleBlockSwitch(FlowBlock *bl)
|
|
|
|
{
|
|
if (!bl->isSwitchOut()) return false;
|
|
FlowBlock *exitblock = (FlowBlock *)0;
|
|
int4 sizeout = bl->sizeOut();
|
|
|
|
// Find "obvious" exitblock, is sizeIn>1 or sizeOut>1
|
|
for(int4 i=0;i<sizeout;++i) {
|
|
FlowBlock *curbl = bl->getOut(i);
|
|
if (curbl == bl) {
|
|
exitblock = curbl; // Exit back to top of switch (loop)
|
|
break;
|
|
}
|
|
if (curbl->sizeOut() > 1) {
|
|
exitblock = curbl;
|
|
break;
|
|
}
|
|
if (curbl->sizeIn() > 1) {
|
|
exitblock = curbl;
|
|
break;
|
|
}
|
|
}
|
|
if (exitblock == (FlowBlock *)0) {
|
|
// If we reach here, every immediate block out of switch must have sizeIn==1 and sizeOut<=1
|
|
// Any immediate block that was an "exitblock" would have no cases exiting to it (because sizeIn==1)
|
|
// If that block had an output, that output can also viably be an output.
|
|
// So as soon as we see an immediate block with an output, we make the output the exit
|
|
for(int4 i=0;i<sizeout;++i) {
|
|
FlowBlock *curbl = bl->getOut(i);
|
|
if (curbl->isGotoIn(0)) return false; // In cannot be a goto
|
|
if (curbl->isSwitchOut()) return false; // Must resolve nested switch first
|
|
if (curbl->sizeOut() == 1) {
|
|
if (curbl->isGotoOut(0)) return false; // Out cannot be goto
|
|
if (exitblock != (FlowBlock *)0) {
|
|
if (exitblock != curbl->getOut(0)) return false;
|
|
}
|
|
else {
|
|
exitblock = curbl->getOut(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else { // From here we have a determined exitblock
|
|
for(int4 i=0;i<exitblock->sizeIn();++i) // No in gotos to exitblock
|
|
if (exitblock->isGotoIn(i)) return false;
|
|
for(int4 i=0;i<exitblock->sizeOut();++i) // No out gotos from exitblock
|
|
if (exitblock->isGotoOut(i)) return false;
|
|
for(int4 i=0;i<sizeout;++i) {
|
|
FlowBlock *curbl = bl->getOut(i);
|
|
if (curbl == exitblock) continue; // The switch can go straight to the exit block
|
|
if (curbl->sizeIn() > 1) return false; // A case can only have the switch fall into it
|
|
if (curbl->isGotoIn(0)) return false; // In cannot be a goto
|
|
if (curbl->sizeOut() > 1) return false; // There can be at most 1 exit from a case
|
|
if (curbl->sizeOut() == 1) {
|
|
if (curbl->isGotoOut(0)) return false; // Out cannot be goto
|
|
if (curbl->getOut(0) != exitblock) return false; // which must be to the exitblock
|
|
}
|
|
if (curbl->isSwitchOut()) return false; // Nested switch must be resolved first
|
|
}
|
|
}
|
|
|
|
if (!checkSwitchSkips(bl,exitblock))
|
|
return true; // We match, but have special condition that adds gotos
|
|
|
|
vector<FlowBlock *> cases;
|
|
cases.push_back(bl);
|
|
for(int4 i=0;i<sizeout;++i) {
|
|
FlowBlock *curbl = bl->getOut(i);
|
|
if (curbl == exitblock) continue; // Don't include exit as a case
|
|
cases.push_back(curbl);
|
|
}
|
|
graph.newBlockSwitch(cases,(exitblock != (FlowBlock *)0));
|
|
return true;
|
|
}
|
|
|
|
/// Look for a switch case that falls thru to another switch case, starting
|
|
/// with the given switch FlowBlock.
|
|
/// \param bl is the given FlowBlock
|
|
/// \return \b true if the structure was applied
|
|
bool CollapseStructure::ruleCaseFallthru(FlowBlock *bl)
|
|
|
|
{
|
|
if (!bl->isSwitchOut()) return false;
|
|
int4 sizeout = bl->sizeOut();
|
|
int4 nonfallthru = 0; // Count of exits that are not fallthru
|
|
vector<FlowBlock *> fallthru;
|
|
|
|
for(int4 i=0;i<sizeout;++i) {
|
|
FlowBlock *curbl = bl->getOut(i);
|
|
if (curbl == bl) return false; // Cannot exit to itself
|
|
if ((curbl->sizeIn() > 2)||(curbl->sizeOut() > 1))
|
|
nonfallthru += 1;
|
|
else if (curbl->sizeOut()==1) {
|
|
FlowBlock *target = curbl->getOut(0);
|
|
if ((target->sizeIn()==2)&&(target->sizeOut()<=1)) {
|
|
int4 inslot = curbl->getOutRevIndex(0);
|
|
if (target->getIn(1-inslot)==bl)
|
|
fallthru.push_back(curbl);
|
|
}
|
|
}
|
|
if (nonfallthru > 1) return false; // Can have at most 1 other exit block
|
|
}
|
|
if (fallthru.empty()) return false; // No fall thru candidates
|
|
// Check exit block matches the 1 nonfallthru exit
|
|
|
|
// Mark the fallthru edges as gotos
|
|
for(int4 i=0;i<fallthru.size();++i) {
|
|
FlowBlock *curbl = fallthru[i];
|
|
curbl->setGotoBranch(0);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Collapse everything until no additional rules apply.
|
|
/// If handed a particular FlowBlock, try simplifying from that block first.
|
|
/// \param targetbl is the FlowBlock to start from or NULL
|
|
/// \return the count of \e isolated FlowBlocks (with no incoming or outgoing edges)
|
|
int4 CollapseStructure::collapseInternal(FlowBlock *targetbl)
|
|
|
|
{
|
|
int4 index;
|
|
bool change,fullchange;
|
|
int4 isolated_count;
|
|
FlowBlock *bl;
|
|
|
|
do {
|
|
do {
|
|
change = false;
|
|
index = 0;
|
|
isolated_count = 0;
|
|
while(index < graph.getSize()) {
|
|
if (targetbl == (FlowBlock *)0) {
|
|
bl = graph.getBlock(index);
|
|
index += 1;
|
|
}
|
|
else {
|
|
bl = targetbl; // Pick out targeted block
|
|
change = true; // but force a change so we still go through all blocks
|
|
targetbl = (FlowBlock *)0; // Only target the block once
|
|
index = graph.getSize();
|
|
}
|
|
if ((bl->sizeIn()==0)&&(bl->sizeOut()==0)) { // A completely collapsed block
|
|
isolated_count += 1;
|
|
continue; // This does not constitute a chanage
|
|
}
|
|
// Try each rule on the block
|
|
if (ruleBlockGoto(bl)) {
|
|
change = true;
|
|
continue;
|
|
}
|
|
if (ruleBlockCat(bl)) {
|
|
change = true;
|
|
continue;
|
|
}
|
|
if (ruleBlockProperIf(bl)) {
|
|
change = true;
|
|
continue;
|
|
}
|
|
if (ruleBlockIfElse(bl)) {
|
|
change = true;
|
|
continue;
|
|
}
|
|
if (ruleBlockWhileDo(bl)) {
|
|
change = true;
|
|
continue;
|
|
}
|
|
if (ruleBlockDoWhile(bl)) {
|
|
change = true;
|
|
continue;
|
|
}
|
|
if (ruleBlockInfLoop(bl)) {
|
|
change = true;
|
|
continue;
|
|
}
|
|
if (ruleBlockSwitch(bl)) {
|
|
change = true;
|
|
continue;
|
|
}
|
|
// if (ruleBlockOr(bl)) {
|
|
// change = true;
|
|
// continue;
|
|
// }
|
|
}
|
|
} while(change);
|
|
// Applying IfNoExit rule too early can cause other (preferable) rules to miss
|
|
// Only apply the rule if nothing else can apply
|
|
fullchange = false;
|
|
for(index=0;index<graph.getSize();++index) {
|
|
bl = graph.getBlock(index);
|
|
if (ruleBlockIfNoExit(bl)) { // If no other change is possible but still blocks left, try ifnoexit
|
|
fullchange = true;
|
|
break;
|
|
}
|
|
if (ruleCaseFallthru(bl)) { // Check for fallthru cases in a switch
|
|
fullchange = true;
|
|
break;
|
|
}
|
|
}
|
|
} while(fullchange);
|
|
return isolated_count;
|
|
}
|
|
|
|
/// Simplify just the conditional AND/OR constructions.
|
|
void CollapseStructure::collapseConditions(void)
|
|
|
|
{
|
|
bool change;
|
|
do {
|
|
change = false;
|
|
for(int4 i=0;i<graph.getSize();++i) {
|
|
if (ruleBlockOr(graph.getBlock(i)))
|
|
change = true;
|
|
}
|
|
} while(change);
|
|
}
|
|
|
|
/// The initial BlockGraph should be a copy of the permanent control-flow graph.
|
|
/// In particular the FlowBlock nodes should be BlockCopy instances.
|
|
/// \param g is the (copy of the) control-flow graph
|
|
CollapseStructure::CollapseStructure(BlockGraph &g)
|
|
: graph(g)
|
|
{
|
|
dataflow_changecount = 0;
|
|
}
|
|
|
|
/// Collapse everything in the control-flow graph to isolated blocks with no inputs and outputs.
|
|
void CollapseStructure::collapseAll(void)
|
|
|
|
{
|
|
int4 isolated_count;
|
|
|
|
finaltrace = false;
|
|
graph.clearVisitCount();
|
|
orderLoopBodies();
|
|
|
|
collapseConditions();
|
|
|
|
isolated_count = collapseInternal((FlowBlock *)0);
|
|
while(isolated_count < graph.getSize()) {
|
|
FlowBlock *targetbl = selectGoto();
|
|
isolated_count = collapseInternal(targetbl);
|
|
}
|
|
}
|
|
|
|
/// Compare based on the creation index of \b side1 first then \b side2
|
|
/// \param op2 is the MergePair to compare to \b this
|
|
/// \return \b true if \b this comes before \b op2
|
|
bool ConditionalJoin::MergePair::operator<(const MergePair &op2) const
|
|
|
|
{
|
|
uint4 s1 = side1->getCreateIndex();
|
|
uint4 s2 = op2.side1->getCreateIndex();
|
|
if (s1 != s2)
|
|
return (s1 < s2);
|
|
return (side2->getCreateIndex() < op2.side2->getCreateIndex());
|
|
}
|
|
|
|
/// Given two conditional blocks, determine if the corresponding conditional
|
|
/// expressions are equivalent, up to Varnodes that need to be merged.
|
|
/// Any Varnode pairs that need to be merged are put in the \b mergeneed map.
|
|
/// \return \b true if there are matching conditions
|
|
bool ConditionalJoin::findDups(void)
|
|
|
|
{
|
|
cbranch1 = block1->lastOp();
|
|
if (cbranch1->code() != CPUI_CBRANCH) return false;
|
|
cbranch2 = block2->lastOp();
|
|
if (cbranch2->code() != CPUI_CBRANCH) return false;
|
|
|
|
if (cbranch1->isBooleanFlip()) return false; // flip hasn't propagated through yet
|
|
if (cbranch2->isBooleanFlip()) return false;
|
|
|
|
Varnode *vn1 = cbranch1->getIn(1);
|
|
Varnode *vn2 = cbranch2->getIn(1);
|
|
|
|
if (vn1 == vn2)
|
|
return true;
|
|
|
|
// Parallel RulePushMulti, so we know it will apply if we do the join
|
|
if (!vn1->isWritten()) return false;
|
|
if (!vn2->isWritten()) return false;
|
|
if (vn1->isSpacebase()) return false;
|
|
if (vn2->isSpacebase()) return false;
|
|
Varnode *buf1[2];
|
|
Varnode *buf2[2];
|
|
int4 res = functionalEqualityLevel(vn1,vn2,buf1,buf2);
|
|
if (res < 0) return false;
|
|
if (res > 1) return false;
|
|
PcodeOp *op1 = vn1->getDef();
|
|
if (op1->code() == CPUI_SUBPIECE) return false;
|
|
if (op1->code() == CPUI_COPY) return false;
|
|
|
|
mergeneed[ MergePair(vn1,vn2) ] = (Varnode *)0;
|
|
return true;
|
|
}
|
|
|
|
/// \brief Look for additional Varnode pairs in an exit block that need to be merged.
|
|
///
|
|
/// Varnodes that are merged in the exit block flowing from \b block1 and \b block2
|
|
/// will need to merged in the new joined block. Add these pairs to the \b mergeneed map.
|
|
/// \param exit is the exit block
|
|
/// \param in1 is the index of the edge coming from \b block1
|
|
/// \param in2 is the index of the edge coming from \b block2
|
|
void ConditionalJoin::checkExitBlock(BlockBasic *exit,int4 in1,int4 in2)
|
|
|
|
{
|
|
list<PcodeOp *>::const_iterator iter,enditer;
|
|
|
|
iter = exit->beginOp();
|
|
enditer = exit->endOp();
|
|
while(iter != enditer) {
|
|
PcodeOp *op = *iter;
|
|
++iter;
|
|
if (op->code() == CPUI_MULTIEQUAL) { // Anything merging from our two root blocks -block1- and -block2-
|
|
Varnode *vn1 = op->getIn(in1);
|
|
Varnode *vn2 = op->getIn(in2);
|
|
if (vn1 != vn2)
|
|
mergeneed[ MergePair(vn1,vn2) ] = (Varnode *)0;
|
|
}
|
|
else if (op->code() != CPUI_COPY) break;
|
|
}
|
|
}
|
|
|
|
/// \brief Substitute new joined Varnode in the given exit block
|
|
///
|
|
/// For any MULTIEQUAL in the \b exit, given two input slots, remove one Varnode,
|
|
/// and substitute the other Varnode from the corresponding Varnode in the \b mergeneed map.
|
|
/// \param exit is the exit block
|
|
/// \param in1 is the index of the incoming edge from \b block1
|
|
/// \param in2 is the index of the incoming edge from \b block2
|
|
void ConditionalJoin::cutDownMultiequals(BlockBasic *exit,int4 in1,int4 in2)
|
|
|
|
{
|
|
list<PcodeOp *>::const_iterator iter,enditer;
|
|
|
|
int4 lo,hi;
|
|
if (in1 > in2) {
|
|
hi = in1;
|
|
lo = in2;
|
|
}
|
|
else {
|
|
hi = in2;
|
|
lo = in1;
|
|
}
|
|
iter = exit->beginOp();
|
|
enditer = exit->endOp();
|
|
while(iter != enditer) {
|
|
PcodeOp *op = *iter;
|
|
++iter; // Advance iterator before inserts happen
|
|
if (op->code() == CPUI_MULTIEQUAL) {
|
|
Varnode *vn1 = op->getIn(in1);
|
|
Varnode *vn2 = op->getIn(in2);
|
|
if (vn1 == vn2) {
|
|
data.opRemoveInput(op,hi);
|
|
}
|
|
else {
|
|
Varnode *subvn = mergeneed[ MergePair(vn1,vn2) ];
|
|
data.opRemoveInput(op,hi);
|
|
data.opSetInput(op,subvn,lo);
|
|
}
|
|
if (op->numInput() == 1) {
|
|
data.opUninsert(op);
|
|
data.opSetOpcode(op,CPUI_COPY);
|
|
data.opInsertBegin(op,exit);
|
|
}
|
|
}
|
|
else if (op->code() != CPUI_COPY) break;
|
|
}
|
|
}
|
|
|
|
/// Create a new Varnode and its defining MULTIEQUAL operation
|
|
/// for each MergePair in the map.
|
|
void ConditionalJoin::setupMultiequals(void)
|
|
|
|
{
|
|
map<MergePair,Varnode *>::iterator iter;
|
|
|
|
for(iter=mergeneed.begin();iter!=mergeneed.end();++iter) {
|
|
if ((*iter).second != (Varnode *)0) continue;
|
|
Varnode *vn1 = (*iter).first.side1;
|
|
Varnode *vn2 = (*iter).first.side2;
|
|
PcodeOp *multi = data.newOp(2,cbranch1->getAddr());
|
|
data.opSetOpcode(multi,CPUI_MULTIEQUAL);
|
|
Varnode *outvn = data.newUniqueOut(vn1->getSize(),multi);
|
|
data.opSetInput(multi,vn1,0);
|
|
data.opSetInput(multi,vn2,1);
|
|
(*iter).second = outvn;
|
|
data.opInsertEnd(multi,joinblock);
|
|
}
|
|
}
|
|
|
|
/// Remove the other CBRANCH
|
|
void ConditionalJoin::moveCbranch(void)
|
|
|
|
{
|
|
Varnode *vn1 = cbranch1->getIn(1);
|
|
Varnode *vn2 = cbranch2->getIn(1);
|
|
data.opUninsert(cbranch1);
|
|
data.opInsertEnd(cbranch1,joinblock);
|
|
Varnode *vn;
|
|
if (vn1 != vn2)
|
|
vn = mergeneed[ MergePair(vn1,vn2) ];
|
|
else
|
|
vn = vn1;
|
|
data.opSetInput(cbranch1,vn,1);
|
|
data.opDestroy(cbranch2);
|
|
}
|
|
|
|
/// Given a pair of conditional blocks, make sure that they match the \e split conditions
|
|
/// necessary for merging and set up to do the merge.
|
|
/// If the conditions are not met, this method cleans up so that additional calls can be made.
|
|
/// \param b1 is the BlockBasic exhibiting one side of the split
|
|
/// \param b2 is the BlockBasic on the other side of the split
|
|
/// \return \b true if the conditions for merging are met
|
|
bool ConditionalJoin::match(BlockBasic *b1,BlockBasic *b2)
|
|
|
|
{
|
|
block1 = b1;
|
|
block2 = b2;
|
|
// Check for the ConditionalJoin block pattern
|
|
if (block2 == block1) return false;
|
|
if (block1->sizeOut() != 2) return false;
|
|
if (block2->sizeOut() != 2) return false;
|
|
exita = (BlockBasic *)block1->getOut(0);
|
|
exitb = (BlockBasic *)block1->getOut(1);
|
|
if (exita == exitb) return false;
|
|
if (block2->getOut(0) != exita) return false; // False exits must match
|
|
if (block2->getOut(1) != exitb) return false; // True exits must match
|
|
a_in2 = block2->getOutRevIndex(0);
|
|
b_in2 = block2->getOutRevIndex(1);
|
|
a_in1 = block1->getOutRevIndex(0);
|
|
b_in1 = block1->getOutRevIndex(1);
|
|
|
|
if (!findDups()) {
|
|
clear();
|
|
return false;
|
|
}
|
|
checkExitBlock(exita,a_in1,a_in2);
|
|
checkExitBlock(exitb,b_in1,b_in2);
|
|
return true;
|
|
}
|
|
|
|
/// All the conditions have been met. Go ahead and do the join.
|
|
void ConditionalJoin::execute(void)
|
|
|
|
{
|
|
joinblock = data.nodeJoinCreateBlock(block1,block2,exita,exitb,(a_in1 > a_in2),(b_in1 > b_in2),cbranch1->getAddr());
|
|
setupMultiequals();
|
|
moveCbranch();
|
|
cutDownMultiequals(exita,a_in1,a_in2);
|
|
cutDownMultiequals(exitb,b_in1,b_in2);
|
|
}
|
|
|
|
void ConditionalJoin::clear(void)
|
|
|
|
{ // Clear out data from previous join
|
|
mergeneed.clear();
|
|
}
|
|
|
|
int4 ActionStructureTransform::apply(Funcdata &data)
|
|
|
|
{
|
|
data.getStructure().finalTransform(data);
|
|
return 0;
|
|
}
|
|
|
|
int4 ActionNormalizeBranches::apply(Funcdata &data)
|
|
|
|
{
|
|
const BlockGraph &graph(data.getBasicBlocks());
|
|
vector<PcodeOp *> fliplist;
|
|
|
|
for(int4 i=0;i<graph.getSize();++i) {
|
|
BlockBasic *bb = (BlockBasic *)graph.getBlock(i);
|
|
if (bb->sizeOut() != 2) continue;
|
|
PcodeOp *cbranch = bb->lastOp();
|
|
if (cbranch == (PcodeOp *)0) continue;
|
|
if (cbranch->code() != CPUI_CBRANCH) continue;
|
|
fliplist.clear();
|
|
if (Funcdata::opFlipInPlaceTest(cbranch,fliplist) != 0)
|
|
continue;
|
|
data.opFlipInPlaceExecute(fliplist);
|
|
bb->flipInPlaceExecute();
|
|
count += 1; // Indicate a change was made
|
|
}
|
|
data.clearDeadOps(); // Clear any ops deleted by opFlipInPlaceExecute
|
|
return 0;
|
|
}
|
|
|
|
int4 ActionPreferComplement::apply(Funcdata &data)
|
|
|
|
{
|
|
BlockGraph &graph(data.getStructure());
|
|
|
|
if (graph.getSize() == 0) return 0;
|
|
vector<BlockGraph *> vec;
|
|
vec.push_back(&graph);
|
|
int4 pos = 0;
|
|
|
|
while(pos < vec.size()) {
|
|
BlockGraph *curbl = vec[pos];
|
|
FlowBlock::block_type bt;
|
|
pos += 1;
|
|
int4 sz = curbl->getSize();
|
|
for(int4 i=0;i<sz;++i) {
|
|
FlowBlock *childbl = curbl->getBlock(i);
|
|
bt = childbl->getType();
|
|
if ((bt == FlowBlock::t_copy)||(bt == FlowBlock::t_basic))
|
|
continue;
|
|
vec.push_back((BlockGraph *)childbl);
|
|
}
|
|
if (curbl->preferComplement(data))
|
|
count += 1;
|
|
}
|
|
data.clearDeadOps(); // Clear any ops deleted during this action
|
|
return 0;
|
|
}
|
|
|
|
int4 ActionBlockStructure::apply(Funcdata &data)
|
|
|
|
{
|
|
BlockGraph &graph(data.getStructure());
|
|
|
|
// Check if already structured
|
|
if (graph.getSize() != 0) return 0;
|
|
data.installSwitchDefaults();
|
|
graph.buildCopy(data.getBasicBlocks());
|
|
|
|
CollapseStructure collapse(graph);
|
|
collapse.collapseAll();
|
|
count += collapse.getChangeCount();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int4 ActionFinalStructure::apply(Funcdata &data)
|
|
|
|
{
|
|
BlockGraph &graph(data.getStructure());
|
|
|
|
graph.orderBlocks();
|
|
graph.finalizePrinting(data);
|
|
graph.scopeBreak(-1,-1); // Put in \e break statements
|
|
graph.markUnstructured(); // Put in \e gotos
|
|
graph.markLabelBumpUp(false); // Fix up labeling
|
|
return 0;
|
|
}
|
|
|
|
/// \brief Gather all blocks that have \e goto edge to a RETURN
|
|
///
|
|
/// Collect all BlockGoto or BlockIf nodes, where there is a \e goto
|
|
/// edge to a RETURN block.
|
|
/// \param parent is a FlowBlock that ends in a RETURN operation
|
|
/// \param vec will hold the \e goto blocks
|
|
void ActionReturnSplit::gatherReturnGotos(FlowBlock *parent,vector<FlowBlock *> &vec)
|
|
|
|
{
|
|
FlowBlock *bl,*ret;
|
|
|
|
for(int4 i=0;i<parent->sizeIn();++i) {
|
|
bl = parent->getIn(i)->getCopyMap();
|
|
while(bl != (FlowBlock *)0) {
|
|
if (!bl->isMark()) {
|
|
ret = (FlowBlock *)0;
|
|
if (bl->getType() == FlowBlock::t_goto) {
|
|
if (((BlockGoto *)bl)->gotoPrints())
|
|
ret = ((BlockGoto *)bl)->getGotoTarget();
|
|
}
|
|
else if (bl->getType() == FlowBlock::t_if)
|
|
// if this is an ifgoto block, get target, otherwise null
|
|
ret = ((BlockIf *)bl)->getGotoTarget();
|
|
if (ret != (FlowBlock *)0) {
|
|
while(ret->getType() != FlowBlock::t_basic)
|
|
ret = ret->subBlock(0);
|
|
if (ret == parent) {
|
|
bl->setMark();
|
|
vec.push_back(bl);
|
|
}
|
|
}
|
|
}
|
|
bl = bl->getParent();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Given a BasicBlock ending in a RETURN operation, determine
|
|
/// if there is any other substantive operation going on in the block. If there
|
|
/// is, the block is deemed too complicated to split.
|
|
/// \param b is the given BasicBlock
|
|
/// \return \b true if the block can be split
|
|
bool ActionReturnSplit::isSplittable(BlockBasic *b)
|
|
|
|
{
|
|
list<PcodeOp *>::const_iterator iter;
|
|
PcodeOp *op;
|
|
|
|
for(iter=b->beginOp();iter!=b->endOp();++iter) {
|
|
op = *iter;
|
|
OpCode opc = op->code();
|
|
if (opc == CPUI_MULTIEQUAL) continue;
|
|
if ((opc == CPUI_COPY)||(opc == CPUI_RETURN)) {
|
|
for(int4 i=0;i<op->numInput();++i) {
|
|
if (op->getIn(i)->isConstant()) continue;
|
|
if (op->getIn(i)->isAnnotation()) continue;
|
|
if (op->getIn(i)->isFree()) return false;
|
|
}
|
|
continue;
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int4 ActionReturnSplit::apply(Funcdata &data)
|
|
|
|
{
|
|
PcodeOp *op;
|
|
BlockBasic *parent;
|
|
FlowBlock *bl;
|
|
list<PcodeOp *>::const_iterator iter,iterend;
|
|
vector<int4> splitedge;
|
|
vector<BlockBasic *> retnode;
|
|
|
|
if (data.getStructure().getSize() == 0)
|
|
return 0; // Some other restructuring happened first
|
|
iterend = data.endOp(CPUI_RETURN);
|
|
for(iter=data.beginOp(CPUI_RETURN);iter!=iterend;++iter) {
|
|
op = *iter;
|
|
if (op->isDead()) continue;
|
|
parent = op->getParent();
|
|
if (parent->sizeIn() <= 1) continue;
|
|
if (!isSplittable(parent)) continue;
|
|
vector<FlowBlock *> gotoblocks;
|
|
gatherReturnGotos(parent,gotoblocks);
|
|
if (gotoblocks.empty()) continue;
|
|
|
|
int4 splitcount = 0;
|
|
// splitedge will contain edges to be split, IN THE ORDER
|
|
// they will be split. So we start from the biggest index
|
|
// So that edge removal won't change the index of remaining edges
|
|
for(int4 i=parent->sizeIn()-1;i>=0;--i) {
|
|
bl = parent->getIn(i)->getCopyMap();
|
|
while(bl != (FlowBlock *)0) {
|
|
if (bl->isMark()) {
|
|
splitedge.push_back(i);
|
|
retnode.push_back(parent);
|
|
bl = (FlowBlock *)0;
|
|
splitcount += 1;
|
|
}
|
|
else
|
|
bl = bl->getParent();
|
|
}
|
|
}
|
|
|
|
for(int4 i=0;i<gotoblocks.size();++i) // Clear our marks
|
|
gotoblocks[i]->clearMark();
|
|
|
|
// Can't split ALL in edges
|
|
if (parent->sizeIn() == splitcount) {
|
|
splitedge.pop_back();
|
|
retnode.pop_back();
|
|
}
|
|
}
|
|
|
|
for(int4 i=0;i<splitedge.size();++i) {
|
|
data.nodeSplit(retnode[i],splitedge[i]);
|
|
count += 1;
|
|
#ifdef BLOCKCONSISTENT_DEBUG
|
|
if (!data.getBasicBlocks().isConsistent())
|
|
data.getArch()->printMessage("Block structure is not consistent");
|
|
#endif
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int4 ActionNodeJoin::apply(Funcdata &data)
|
|
|
|
{
|
|
const BlockGraph &graph(data.getBasicBlocks());
|
|
if (graph.getSize()==0) return 0;
|
|
|
|
ConditionalJoin condjoin(data);
|
|
|
|
for(int4 i=0;i<graph.getSize();++i) {
|
|
BlockBasic *bb = (BlockBasic *) graph.getBlock(i);
|
|
if (bb->sizeOut() != 2) continue;
|
|
BlockBasic *out1 = (BlockBasic *) bb->getOut(0);
|
|
BlockBasic *out2 = (BlockBasic *) bb->getOut(1);
|
|
int4 inslot;
|
|
BlockBasic *leastout;
|
|
if (out1->sizeIn() < out2->sizeIn()) {
|
|
leastout = out1;
|
|
inslot = bb->getOutRevIndex(0);
|
|
}
|
|
else {
|
|
leastout = out2;
|
|
inslot = bb->getOutRevIndex(1);
|
|
}
|
|
if (leastout->sizeIn()==1) continue;
|
|
|
|
for(int4 j=0;j<leastout->sizeIn();++j) {
|
|
if (j == inslot) continue;
|
|
BlockBasic *bb2 = (BlockBasic *)leastout->getIn(j);
|
|
if (condjoin.match(bb,bb2)) {
|
|
count += 1; // Indicate change has been made
|
|
condjoin.execute();
|
|
condjoin.clear();
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
} // End namespace ghidra
|