mirror of
https://github.com/scummvm/scummvm-tools.git
synced 2026-05-21 05:40:44 +00:00
615 lines
20 KiB
C++
615 lines
20 KiB
C++
/* ScummVM Tools
|
|
*
|
|
* ScummVM Tools is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "control_flow.h"
|
|
#include "stack.h"
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <set>
|
|
|
|
#include <boost/format.hpp>
|
|
|
|
#define PUT(vertex, group) boost::put(boost::vertex_name, _g, vertex, group);
|
|
#define PUT_EDGE(edge, isJump) boost::put(boost::edge_attribute, _g, edge, isJump);
|
|
#define PUT_ID(vertex, id) boost::put(boost::vertex_index, _g, vertex, id);
|
|
#define GET(vertex) (boost::get(boost::vertex_name, _g, vertex))
|
|
#define GET_EDGE(edge) (boost::get(boost::edge_attribute, _g, edge))
|
|
|
|
ControlFlow::ControlFlow(const InstVec &insts, Engine *engine) : _insts(insts) {
|
|
_engine = engine;
|
|
|
|
// Automatically add a function if we're not supposed to look for more functions and no functions are defined
|
|
// This avoids a special case for when no real functions exist in the script
|
|
if (engine->_functions.empty() && !_engine->detectMoreFuncs())
|
|
engine->_functions[(*insts.begin())->_address] = Function(insts.begin(), insts.end());
|
|
|
|
GroupPtr prev = NULL;
|
|
int id = 0;
|
|
// Create vertices
|
|
for (ConstInstIterator it = insts.begin(); it != insts.end(); ++it) {
|
|
GraphVertex cur = boost::add_vertex(_g);
|
|
_addrMap[(*it)->_address] = cur;
|
|
PUT(cur, new Group(cur, it, it, prev));
|
|
PUT_ID(cur, id);
|
|
id++;
|
|
|
|
// Add reference to vertex if function starts here
|
|
if (_engine->_functions.find((*it)->_address) != _engine->_functions.end())
|
|
_engine->_functions[(*it)->_address]._v = cur;
|
|
|
|
prev = GET(cur);
|
|
}
|
|
|
|
// Add regular edges
|
|
FuncMap::iterator fn;
|
|
GraphVertex last;
|
|
bool addEdge = false;
|
|
prev = NULL;
|
|
for (ConstInstIterator it = insts.begin(); it != insts.end(); ++it) {
|
|
if (_engine->_functions.find((*it)->_address) != _engine->_functions.end()) {
|
|
addEdge = false;
|
|
}
|
|
|
|
GraphVertex cur = find(it);
|
|
if (addEdge) {
|
|
GraphEdge e = boost::add_edge(last, cur, _g).first;
|
|
PUT_EDGE(e, false);
|
|
}
|
|
|
|
last = cur;
|
|
addEdge = !((*it)->isUncondJump() || (*it)->isReturn());
|
|
prev = GET(cur);
|
|
|
|
}
|
|
|
|
// Add jump edges
|
|
for (ConstInstIterator it = insts.begin(); it != insts.end(); ++it) {
|
|
if ((*it)->isJump()) {
|
|
GraphEdge e = boost::add_edge(find(it), find((*it)->getDestAddress()), _g).first;
|
|
PUT_EDGE(e, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
GraphVertex ControlFlow::find(const InstPtr inst) {
|
|
return _addrMap[inst->_address];
|
|
}
|
|
|
|
GraphVertex ControlFlow::find(ConstInstIterator it) {
|
|
return _addrMap[(*it)->_address];
|
|
}
|
|
|
|
GraphVertex ControlFlow::find(uint32 address) {
|
|
std::map<uint32, GraphVertex>::iterator it = _addrMap.find(address);
|
|
if (it == _addrMap.end())
|
|
std::cerr << "Request for instruction at unknown address " << boost::format("0x%08x") % address << std::endl;
|
|
return it->second;
|
|
}
|
|
|
|
void ControlFlow::merge(GraphVertex g1, GraphVertex g2) {
|
|
// Update property
|
|
GroupPtr gr1 = GET(g1);
|
|
GroupPtr gr2 = GET(g2);
|
|
gr1->_end = gr2->_end;
|
|
PUT(g1, gr1);
|
|
|
|
// Update address map
|
|
ConstInstIterator it = gr2->_start;
|
|
do {
|
|
_addrMap[(*it)->_address] = g1;
|
|
++it;
|
|
} while (gr2->_start != gr2->_end && it != gr2->_end);
|
|
|
|
// Add outgoing edges from g2
|
|
OutEdgeRange r = boost::out_edges(g2, _g);
|
|
for (OutEdgeIterator e = r.first; e != r.second; ++e) {
|
|
GraphEdge newE = boost::add_edge(g1, boost::target(*e, _g), _g).first;
|
|
PUT_EDGE(newE, GET_EDGE(*e));
|
|
}
|
|
|
|
// Update _next pointer
|
|
gr1->_next = gr2->_next;
|
|
if (gr2->_next != NULL)
|
|
gr2->_next->_prev = gr2->_prev;
|
|
|
|
// Remove edges to/from g2
|
|
boost::clear_vertex(g2, _g);
|
|
// Remove vertex
|
|
boost::remove_vertex(g2, _g);
|
|
}
|
|
|
|
typedef std::pair<GraphVertex, int> LevelEntry;
|
|
|
|
void ControlFlow::setStackLevel(GraphVertex g, int level) {
|
|
Stack<LevelEntry> levelStack;
|
|
std::set<GraphVertex> seen;
|
|
levelStack.push(LevelEntry(g, level));
|
|
seen.insert(g);
|
|
while (!levelStack.empty()) {
|
|
LevelEntry e = levelStack.pop();
|
|
GroupPtr gr = GET(e.first);
|
|
if (gr->_stackLevel != -1) {
|
|
if (gr->_stackLevel != e.second)
|
|
std::cerr << boost::format("WARNING: Inconsistency in expected stack level for instruction at address 0x%08x (current: %d, requested: %d)\n") % (*gr->_start)->_address % gr->_stackLevel % e.second;
|
|
continue;
|
|
}
|
|
gr->_stackLevel = e.second;
|
|
|
|
OutEdgeRange r = boost::out_edges(e.first, _g);
|
|
for (OutEdgeIterator oe = r.first; oe != r.second; ++oe) {
|
|
GraphVertex target = boost::target(*oe, _g);
|
|
if (seen.find(target) == seen.end()) {
|
|
levelStack.push(LevelEntry(target, e.second + (*gr->_start)->_stackChange));
|
|
seen.insert(target);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ControlFlow::detectFunctions() {
|
|
uint32 nextFunc = 0;
|
|
for (ConstInstIterator it = _insts.begin(); it != _insts.end(); ++it) {
|
|
GraphVertex v = find(it);
|
|
GroupPtr gr = GET(v);
|
|
|
|
if ((*it)->_address < nextFunc)
|
|
continue;
|
|
|
|
bool functionExists = false;
|
|
bool detectEndPoint = false;
|
|
for (FuncMap::iterator fn = _engine->_functions.begin(); fn != _engine->_functions.end(); ++fn) {
|
|
if (fn->first == (*it)->_address) {
|
|
if (fn->second._endIt == _insts.end()) {
|
|
return;
|
|
}
|
|
if (fn->second._startIt == fn->second._endIt) {
|
|
// We already know this is an entry point, we only need to detect the end point
|
|
detectEndPoint = true;
|
|
break;
|
|
}
|
|
nextFunc = (*fn->second._endIt)->_address;
|
|
functionExists = true;
|
|
}
|
|
}
|
|
|
|
if (functionExists)
|
|
continue;
|
|
|
|
bool isEntryPoint = true;
|
|
if (!detectEndPoint) {
|
|
InEdgeRange ier = boost::in_edges(v, _g);
|
|
for (InEdgeIterator e = ier.first; e != ier.second; ++e) {
|
|
// If an ingoing edge exists from earlier in the code, this is not a function entry point
|
|
if ((*GET(boost::source(*e, _g))->_start)->_address < (*gr->_start)->_address)
|
|
isEntryPoint = false;
|
|
}
|
|
}
|
|
|
|
if (isEntryPoint) {
|
|
// Detect end point
|
|
Stack<GraphVertex> stack;
|
|
std::set<GraphVertex> seen;
|
|
stack.push(v);
|
|
GroupPtr endPoint = gr;
|
|
while (!stack.empty()) {
|
|
v = stack.pop();
|
|
GroupPtr tmp = GET(v);
|
|
if ((*tmp->_start)->_address > (*endPoint->_start)->_address)
|
|
endPoint = tmp;
|
|
OutEdgeRange r = boost::out_edges(v, _g);
|
|
for (OutEdgeIterator i = r.first; i != r.second; ++i) {
|
|
GraphVertex target = boost::target(*i, _g);
|
|
if (seen.find(target) == seen.end()) {
|
|
stack.push(target);
|
|
seen.insert(target);
|
|
}
|
|
}
|
|
}
|
|
|
|
ConstInstIterator endInst;
|
|
if (endPoint->_next) {
|
|
endInst = endPoint->_next->_start;
|
|
nextFunc = (*endInst)->_address;
|
|
} else {
|
|
endInst = _insts.end();
|
|
}
|
|
Function f;
|
|
if (detectEndPoint) {
|
|
f = _engine->_functions[(*gr->_start)->_address];
|
|
f._endIt = endInst;
|
|
} else {
|
|
f = Function(gr->_start, endInst);
|
|
f._name = "auto_";
|
|
}
|
|
f._v = find(it);
|
|
_engine->_functions[(*gr->_start)->_address] = f;
|
|
if (!endPoint->_next)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ControlFlow::createGroups() {
|
|
if (!_engine->_functions.empty() && GET(_engine->_functions.begin()->second._v)->_stackLevel != -1)
|
|
return;
|
|
|
|
// Detect more functions
|
|
if (_engine->detectMoreFuncs())
|
|
detectFunctions();
|
|
|
|
for (FuncMap::iterator fn = _engine->_functions.begin(); fn != _engine->_functions.end(); ++fn)
|
|
setStackLevel(fn->second._v, 0);
|
|
ConstInstIterator curInst, nextInst;
|
|
nextInst = _insts.begin();
|
|
++nextInst;
|
|
int stackLevel = 0;
|
|
int expectedStackLevel = 0;
|
|
for (curInst = _insts.begin(); nextInst != _insts.end(); ++curInst, ++nextInst) {
|
|
GraphVertex cur = find(curInst);
|
|
GraphVertex next = find(nextInst);
|
|
|
|
GroupPtr grCur = GET(cur);
|
|
GroupPtr grNext = GET(next);
|
|
|
|
// Don't process unreachable code
|
|
if (grCur->_stackLevel < 0) {
|
|
stackLevel = grNext->_stackLevel;
|
|
continue;
|
|
}
|
|
|
|
expectedStackLevel = grCur->_stackLevel;
|
|
// If expected stack level decreases in next vertex, then use next vertex level as expected level
|
|
if (expectedStackLevel > grNext->_stackLevel && grNext->_stackLevel >= 0) {
|
|
expectedStackLevel = grNext->_stackLevel;
|
|
// Also set the stack level of the current group to remember that we expect it to be lower
|
|
grCur->_stackLevel = expectedStackLevel;
|
|
}
|
|
|
|
stackLevel += (*curInst)->_stackChange;
|
|
|
|
// For stack operations, the new stack level becomes the expected stack level starting from the next group
|
|
if ((*curInst)->isStackOp()) {
|
|
expectedStackLevel = stackLevel;
|
|
grNext->_stackLevel = stackLevel;
|
|
}
|
|
|
|
// Group ends after a jump
|
|
if ((*curInst)->isJump()) {
|
|
stackLevel = grNext->_stackLevel;
|
|
continue;
|
|
}
|
|
|
|
// Group ends with a return
|
|
if ((*curInst)->isReturn()) {
|
|
stackLevel = grNext->_stackLevel;
|
|
continue;
|
|
}
|
|
|
|
// Group ends before target of a jump
|
|
if (in_degree(next, _g) != 1) {
|
|
stackLevel = grNext->_stackLevel;
|
|
continue;
|
|
}
|
|
|
|
// This part is only relevant if we use the stack level.
|
|
if (!_engine->usePureGrouping()) {
|
|
// If group has no instructions with stack effect >= 0, don't merge on balanced stack
|
|
bool forceMerge = true;
|
|
ConstInstIterator it = grCur->_start;
|
|
do {
|
|
if ((*it)->_stackChange >= 0)
|
|
forceMerge = false;
|
|
++it;
|
|
} while (grCur->_start != grCur->_end && it != grCur->_end);
|
|
|
|
// Group ends when stack is balanced, unless just before conditional jump
|
|
if (stackLevel == expectedStackLevel && !forceMerge && !(*nextInst)->isCondJump()) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// All checks passed, merge groups
|
|
merge(cur, next);
|
|
}
|
|
|
|
// FIXME: The short-circuit detection is disabled because short-circuited groups require some special handling
|
|
// in the code generation. It's not entirely clear how to handle it properly, though: you need to deduce which
|
|
// effect is created by the conditional jumps in the middle of a block, which seems to get fairly complex when
|
|
// you have multiple groups that are merged by the short-circuit detection.
|
|
//detectShortCircuit();
|
|
}
|
|
|
|
void ControlFlow::detectShortCircuit() {
|
|
ConstInstIterator lastInst = _insts.end();
|
|
--lastInst;
|
|
GraphVertex cur = find(lastInst);
|
|
GroupPtr gr = GET(cur);
|
|
while (gr->_prev != NULL) {
|
|
bool doMerge = false;
|
|
cur = find(gr->_start);
|
|
GraphVertex prev = find(gr->_prev->_start);
|
|
// Block is candidate for short-circuit merging if it and the preceding block both end with conditional jumps
|
|
if (out_degree(cur, _g) == 2 && out_degree(prev, _g) == 2) {
|
|
doMerge = true;
|
|
OutEdgeRange rCur = boost::out_edges(cur, _g);
|
|
std::vector<GraphVertex> succs;
|
|
|
|
// Find possible target vertices
|
|
for (OutEdgeIterator it = rCur.first; it != rCur.second; ++it) {
|
|
succs.push_back(boost::target(*it, _g));
|
|
}
|
|
|
|
// Check if vertex would add new targets - if yes, don't merge
|
|
OutEdgeRange rPrev = boost::out_edges(prev, _g);
|
|
for (OutEdgeIterator it = rPrev.first; it != rPrev.second; ++it) {
|
|
GraphVertex target = boost::target(*it, _g);
|
|
doMerge &= (std::find(succs.begin(), succs.end(), target) != succs.end() || target == cur);
|
|
}
|
|
|
|
if (doMerge) {
|
|
gr = gr->_prev;
|
|
merge(prev, cur);
|
|
continue;
|
|
}
|
|
}
|
|
gr = gr->_prev;
|
|
}
|
|
}
|
|
|
|
const Graph &ControlFlow::analyze() {
|
|
detectDoWhile();
|
|
detectWhile();
|
|
detectBreak();
|
|
detectContinue();
|
|
detectIf();
|
|
detectElse();
|
|
return _g;
|
|
}
|
|
|
|
void ControlFlow::detectWhile() {
|
|
VertexRange vr = boost::vertices(_g);
|
|
for (VertexIterator v = vr.first; v != vr.second; ++v) {
|
|
GroupPtr gr = GET(*v);
|
|
// Undetermined block that ends with conditional jump
|
|
if (out_degree(*v, _g) == 2 && gr->_type == kNormalGroupType) {
|
|
InEdgeRange ier = boost::in_edges(*v, _g);
|
|
bool isWhile = false;
|
|
for (InEdgeIterator e = ier.first; e != ier.second; ++e) {
|
|
GroupPtr sourceGr = GET(boost::source(*e, _g));
|
|
// Block has ingoing edge from block later in the code that isn't a do-while condition
|
|
if ((*sourceGr->_start)->_address > (*gr->_start)->_address && sourceGr->_type != kDoWhileCondGroupType)
|
|
isWhile = true;
|
|
}
|
|
if (isWhile)
|
|
gr->_type = kWhileCondGroupType;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ControlFlow::detectDoWhile() {
|
|
VertexRange vr = boost::vertices(_g);
|
|
for (VertexIterator v = vr.first; v != vr.second; ++v) {
|
|
GroupPtr gr = GET(*v);
|
|
// Undetermined block that ends with conditional jump...
|
|
if (out_degree(*v, _g) == 2 && gr->_type == kNormalGroupType) {
|
|
OutEdgeRange oer = boost::out_edges(*v, _g);
|
|
for (OutEdgeIterator e = oer.first; e != oer.second; ++e) {
|
|
GroupPtr targetGr = GET(boost::target(*e, _g));
|
|
// ...to earlier in code
|
|
if ((*targetGr->_start)->_address < (*gr->_start)->_address)
|
|
gr->_type = kDoWhileCondGroupType;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ControlFlow::detectBreak() {
|
|
VertexRange vr = boost::vertices(_g);
|
|
for (VertexIterator v = vr.first; v != vr.second; ++v) {
|
|
GroupPtr gr = GET(*v);
|
|
// Undetermined block with unconditional jump...
|
|
if (gr->_type == kNormalGroupType && ((*gr->_end)->isUncondJump()) && out_degree(*v, _g) == 1) {
|
|
OutEdgeIterator oe = boost::out_edges(*v, _g).first;
|
|
GraphVertex target = boost::target(*oe, _g);
|
|
GroupPtr targetGr = GET(target);
|
|
// ...to somewhere later in the code...
|
|
if ((*gr->_start)->_address >= (*targetGr->_start)->_address)
|
|
continue;
|
|
InEdgeRange ier = boost::in_edges(target, _g);
|
|
for (InEdgeIterator ie = ier.first; ie != ier.second; ++ie) {
|
|
GroupPtr sourceGr = GET(boost::source(*ie, _g));
|
|
// ...to block immediately after a do-while condition, or to jump target of a while condition
|
|
if ((targetGr->_prev == sourceGr && sourceGr->_type == kDoWhileCondGroupType) || sourceGr->_type == kWhileCondGroupType) {
|
|
if (validateBreakOrContinue(gr, sourceGr))
|
|
gr->_type = kBreakGroupType;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ControlFlow::detectContinue() {
|
|
VertexRange vr = boost::vertices(_g);
|
|
for (VertexIterator v = vr.first; v != vr.second; ++v) {
|
|
GroupPtr gr = GET(*v);
|
|
// Undetermined block with unconditional jump...
|
|
if (gr->_type == kNormalGroupType && ((*gr->_end)->isUncondJump()) && out_degree(*v, _g) == 1) {
|
|
OutEdgeIterator oe = boost::out_edges(*v, _g).first;
|
|
GraphVertex target = boost::target(*oe, _g);
|
|
GroupPtr targetGr = GET(target);
|
|
// ...to a while or do-while condition...
|
|
if (targetGr->_type == kWhileCondGroupType || targetGr->_type == kDoWhileCondGroupType) {
|
|
bool isContinue = true;
|
|
// ...unless...
|
|
OutEdgeRange toer = boost::out_edges(target, _g);
|
|
bool afterJumpTargets = true;
|
|
for (OutEdgeIterator toe = toer.first; toe != toer.second; ++toe) {
|
|
// ...it is targeting a while condition which jumps to the next sequential group
|
|
if (targetGr->_type == kWhileCondGroupType && GET(boost::target(*toe, _g)) == gr->_next)
|
|
isContinue = false;
|
|
// ...or the instruction is placed after all jump targets from condition
|
|
if ((*GET(boost::target(*toe, _g))->_start)->_address > (*gr->_start)->_address)
|
|
afterJumpTargets = false;
|
|
}
|
|
if (afterJumpTargets)
|
|
isContinue = false;
|
|
|
|
if (isContinue && validateBreakOrContinue(gr, targetGr))
|
|
gr->_type = kContinueGroupType;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ControlFlow::validateBreakOrContinue(GroupPtr gr, GroupPtr condGr) {
|
|
GroupPtr from, to, cursor;
|
|
|
|
if (condGr->_type == kDoWhileCondGroupType) {
|
|
to = condGr;
|
|
from = gr;
|
|
} else {
|
|
to = gr;
|
|
from = condGr->_next;
|
|
}
|
|
|
|
GroupType ogt = (condGr->_type == kDoWhileCondGroupType ? kWhileCondGroupType : kDoWhileCondGroupType);
|
|
// Verify that destination deals with innermost while/do-while
|
|
for (cursor = from; cursor->_next != NULL && cursor != to; cursor = cursor->_next) {
|
|
if (cursor->_type == condGr->_type) {
|
|
OutEdgeRange oerValidate = boost::out_edges(find(cursor->_start), _g);
|
|
for (OutEdgeIterator oeValidate = oerValidate.first; oeValidate != oerValidate.second; ++oeValidate) {
|
|
GraphVertex vValidate = boost::target(*oeValidate, _g);
|
|
GroupPtr gValidate = GET(vValidate);
|
|
// For all other loops of same type found in range, all targets must fall within that range
|
|
if ((*gValidate->_start)->_address < (*from->_start)->_address || (*gValidate->_start)->_address > (*to->_start)->_address )
|
|
return false;
|
|
|
|
InEdgeRange ierValidate = boost::in_edges(vValidate, _g);
|
|
for (InEdgeIterator ieValidate = ierValidate.first; ieValidate != ierValidate.second; ++ieValidate) {
|
|
GroupPtr igValidate = GET(boost::source(*ieValidate, _g));
|
|
// All loops of other type going into range must be placed within range
|
|
if (igValidate->_type == ogt && ((*igValidate->_start)->_address < (*from->_start)->_address || (*igValidate->_start)->_address > (*to->_start)->_address ))
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ControlFlow::detectIf() {
|
|
VertexRange vr = boost::vertices(_g);
|
|
for (VertexIterator v = vr.first; v != vr.second; ++v) {
|
|
GroupPtr gr = GET(*v);
|
|
// if: Undetermined block with conditional jump
|
|
if (gr->_type == kNormalGroupType && ((*gr->_end)->isCondJump())) {
|
|
gr->_type = kIfCondGroupType;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ControlFlow::detectElse() {
|
|
VertexRange vr = boost::vertices(_g);
|
|
for (VertexIterator v = vr.first; v != vr.second; ++v) {
|
|
GroupPtr gr = GET(*v);
|
|
if (gr->_type == kIfCondGroupType) {
|
|
OutEdgeRange oer = boost::out_edges(*v, _g);
|
|
GraphVertex target;
|
|
uint32 maxAddress = 0;
|
|
GroupPtr targetGr;
|
|
// Find jump target
|
|
for (OutEdgeIterator oe = oer.first; oe != oer.second; ++oe) {
|
|
targetGr = GET(boost::target(*oe, _g));
|
|
if ((*targetGr->_start)->_address > maxAddress) {
|
|
target = boost::target(*oe, _g);
|
|
maxAddress = (*targetGr->_start)->_address;
|
|
}
|
|
}
|
|
targetGr = GET(target);
|
|
// else: Jump target of if immediately preceded by an unconditional jump...
|
|
if (!(*targetGr->_prev->_end)->isUncondJump())
|
|
continue;
|
|
// ...which is not a break or a continue...
|
|
if (targetGr->_prev->_type == kContinueGroupType || targetGr->_prev->_type == kBreakGroupType)
|
|
continue;
|
|
// ...to later in the code
|
|
OutEdgeIterator toe = boost::out_edges(find((*targetGr->_prev->_start)->_address), _g).first;
|
|
GroupPtr targetTargetGr = GET(boost::target(*toe, _g));
|
|
if ((*targetTargetGr->_start)->_address > (*targetGr->_end)->_address) {
|
|
if (validateElseBlock(gr, targetGr, targetTargetGr)) {
|
|
targetGr->_startElse = true;
|
|
targetTargetGr->_prev->_endElse.push_back(targetGr.get());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ControlFlow::validateElseBlock(GroupPtr ifGroup, GroupPtr start, GroupPtr end) {
|
|
for (GroupPtr cursor = start; cursor != end; cursor = cursor->_next) {
|
|
if (cursor->_type == kIfCondGroupType || cursor->_type == kWhileCondGroupType || cursor->_type == kDoWhileCondGroupType) {
|
|
// Validate outgoing edges of conditions
|
|
OutEdgeRange oer = boost::out_edges(find(cursor->_start), _g);
|
|
for (OutEdgeIterator oe = oer.first; oe != oer.second; ++oe) {
|
|
GraphVertex target = boost::target(*oe, _g);
|
|
GroupPtr targetGr = GET(target);
|
|
// Each edge from condition must not leave the range [start, end]
|
|
if ((*start->_start)->_address > (*targetGr->_start)->_address || (*targetGr->_start)->_address > (*end->_start)->_address)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// If previous group ends an else, that else must start inside the range
|
|
for (ElseEndIterator it = cursor->_prev->_endElse.begin(); it != cursor->_prev->_endElse.end(); ++it)
|
|
{
|
|
if ((*(*it)->_start)->_address < (*start->_start)->_address)
|
|
return false;
|
|
}
|
|
|
|
// Unless group is a simple unconditional jump...
|
|
if ((*cursor->_start)->isUncondJump())
|
|
continue;
|
|
|
|
// ...validate ingoing edges
|
|
InEdgeRange ier = boost::in_edges(find(cursor->_start), _g);
|
|
for (InEdgeIterator ie = ier.first; ie != ier.second; ++ie) {
|
|
GraphVertex source = boost::source(*ie, _g);
|
|
GroupPtr sourceGr = GET(source);
|
|
|
|
// Edges going to conditions...
|
|
if (sourceGr->_type == kIfCondGroupType || sourceGr->_type == kWhileCondGroupType || sourceGr->_type == kDoWhileCondGroupType) {
|
|
// ...must not come from outside the range [start, end]...
|
|
if ((*start->_start)->_address > (*sourceGr->_start)->_address || (*sourceGr->_start)->_address > (*end->_start)->_address) {
|
|
// ...unless source is simple unconditional jump...
|
|
if ((*sourceGr->_start)->isUncondJump())
|
|
continue;
|
|
// ...or the edge is from the if condition associated with this else
|
|
if (ifGroup == sourceGr)
|
|
continue;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|