/* 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 .
*
*/
#include "codegen.h"
#include "engine.h"
#include
#include
#include
#include
#define GET(vertex) (boost::get(boost::vertex_name, _g, vertex))
#define GET_EDGE(edge) (boost::get(boost::edge_attribute, _g, edge))
std::string CodeGenerator::constructFuncSignature(const Function &func) {
return "";
}
std::string CodeGenerator::indentString(std::string s) {
std::stringstream stream;
stream << std::string(kIndentAmount * _indentLevel, ' ') << s;
return stream.str();
}
CodeGenerator::CodeGenerator(Engine *engine, std::ostream &output, ArgOrder binOrder, ArgOrder callOrder) : _output(output), _binOrder(binOrder), _callOrder(callOrder) {
_engine = engine;
_indentLevel = 0;
}
typedef std::pair DFSEntry;
void CodeGenerator::generate(const Graph &g) {
_g = g;
for (FuncMap::iterator fn = _engine->_functions.begin(); fn != _engine->_functions.end(); ++fn) {
_indentLevel = 0;
while (!_stack.empty())
_stack.pop();
GraphVertex entryPoint = fn->second._v;
std::string funcSignature = constructFuncSignature(fn->second);
bool printFuncSignature = !funcSignature.empty();
if (printFuncSignature) {
_curGroup = GET(entryPoint);
if (!(fn == _engine->_functions.begin()))
addOutputLine("");
addOutputLine(funcSignature, false, true);
}
GroupPtr lastGroup = GET(entryPoint);
// DFS from entry point to process each vertex
Stack dfsStack;
std::set seen;
dfsStack.push(DFSEntry(entryPoint, ValueStack()));
seen.insert(entryPoint);
while (!dfsStack.empty()) {
DFSEntry e = dfsStack.pop();
GroupPtr tmp = GET(e.first);
if ((*tmp->_start)->_address > (*lastGroup->_start)->_address)
lastGroup = tmp;
_stack = e.second;
GraphVertex v = e.first;
process(v);
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()) {
dfsStack.push(DFSEntry(target, _stack));
seen.insert(target);
}
}
}
if (printFuncSignature) {
_curGroup = lastGroup;
addOutputLine("}", true, false);
}
// Print output
GroupPtr p = GET(entryPoint);
while (p != NULL) {
for (std::vector::iterator it = p->_code.begin(); it != p->_code.end(); ++it) {
if (it->_unindentBefore) {
assert(_indentLevel > 0);
_indentLevel--;
}
_output << boost::format("%08X: %s") % (*p->_start)->_address % indentString(it->_line) << std::endl;
if (it->_indentAfter)
_indentLevel++;
}
p = p->_next;
}
if (_indentLevel != 0)
std::cerr << boost::format("WARNING: Indent level for function at %d ended at %d\n") % fn->first % _indentLevel;
}
}
void CodeGenerator::addOutputLine(std::string s, bool unindentBefore, bool indentAfter) {
_curGroup->_code.push_back(CodeLine(s, unindentBefore, indentAfter));
}
void CodeGenerator::writeAssignment(ValuePtr dst, ValuePtr src) {
std::stringstream s;
s << dst << " = " << src << ";";
addOutputLine(s.str());
}
void CodeGenerator::process(GraphVertex v) {
_curVertex = v;
_curGroup = GET(v);
// Check if we should add else start
if (_curGroup->_startElse)
addOutputLine("} else {", true, true);
// Check ingoing edges to see if we want to add any extra output
InEdgeRange ier = boost::in_edges(v, _g);
for (InEdgeIterator ie = ier.first; ie != ier.second; ++ie) {
GraphVertex in = boost::source(*ie, _g);
GroupPtr inGroup = GET(in);
if (!boost::get(boost::edge_attribute, _g, *ie)._isJump || inGroup->_stackLevel == -1)
continue;
switch (inGroup->_type) {
case kDoWhileCondGroupType:
addOutputLine("do {", false, true);
break;
case kIfCondGroupType:
if (!_curGroup->_startElse)
addOutputLine("}", true, false);
break;
case kWhileCondGroupType:
addOutputLine("}", true, false);
break;
default:
break;
}
}
ConstInstIterator it = _curGroup->_start;
do {
processInst(*it);
} while (it++ != _curGroup->_end);
// Add else end if necessary
for (ElseEndIterator elseIt = _curGroup->_endElse.begin(); elseIt != _curGroup->_endElse.end(); ++elseIt) {
if (!(*elseIt)->_coalescedElse)
addOutputLine("}", true, false);
}
}
void CodeGenerator::processInst(const InstPtr inst) {
inst->processInst(_stack, _engine, this);
if (inst->isCondJump()) {
std::stringstream s;
switch (_curGroup->_type) {
case kIfCondGroupType:
if (_curGroup->_startElse && _curGroup->_code.size() == 1) {
OutEdgeRange oer = boost::out_edges(_curVertex, _g);
bool coalesceElse = false;
for (OutEdgeIterator oe = oer.first; oe != oer.second; ++oe) {
GroupPtr oGr = GET(boost::target(*oe, _g))->_prev;
if (std::find(oGr->_endElse.begin(), oGr->_endElse.end(), _curGroup.get()) != oGr->_endElse.end())
coalesceElse = true;
}
if (coalesceElse) {
_curGroup->_code.clear();
_curGroup->_coalescedElse = true;
s << "} else ";
}
}
s << "if (" << _stack.pop()->negate() << ") {";
addOutputLine(s.str(), _curGroup->_coalescedElse, true);
break;
case kWhileCondGroupType:
s << "while (" << _stack.pop()->negate() << ") {";
addOutputLine(s.str(), false, true);
break;
case kDoWhileCondGroupType:
s << "} while (" << _stack.pop() << ")";
addOutputLine(s.str(), true, false);
break;
default:
break;
}
} else if (inst->isUncondJump()) {
switch (_curGroup->_type) {
case kBreakGroupType:
addOutputLine("break;");
break;
case kContinueGroupType:
addOutputLine("continue;");
break;
default:
{
bool printJump = true;
OutEdgeRange r = boost::out_edges(_curVertex, _g);
for (OutEdgeIterator e = r.first; e != r.second && printJump; ++e) {
// Don't output jump to next vertex
if (boost::target(*e, _g) == _curGroup->_next->_vertex) {
printJump = false;
break;
}
// Don't output jump if next vertex starts an else block
if (_curGroup->_next->_startElse) {
printJump = false;
break;
}
OutEdgeRange targetR = boost::out_edges(boost::target(*e, _g), _g);
for (OutEdgeIterator targetE = targetR.first; targetE != targetR.second; ++targetE) {
// Don't output jump to while loop that has jump to next vertex
if (boost::target(*targetE, _g) == _curGroup->_next->_vertex)
printJump = false;
}
}
if (printJump) {
std::stringstream s;
s << boost::format("jump 0x%X;") % inst->getDestAddress();
addOutputLine(s.str());
}
}
break;
}
}
}
void CodeGenerator::addArg(ValuePtr p) {
if (_callOrder == kFIFOArgOrder)
_argList.push_front(p);
else if (_callOrder == kLIFOArgOrder)
_argList.push_back(p);
}
void CodeGenerator::processSpecialMetadata(const InstPtr inst, char c, int pos) {
switch (c) {
case 'p':
addArg(_stack.pop());
break;
default:
std::cerr << boost::format("WARNING: Unknown character in metadata: %c\n") % c ;
break;
}
}