mirror of
https://github.com/scummvm/scummvm-tools.git
synced 2026-05-21 05:40:44 +00:00
268 lines
7.7 KiB
C++
268 lines
7.7 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 "codegen.h"
|
|
#include "engine.h"
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <set>
|
|
#include <boost/format.hpp>
|
|
|
|
#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<GraphVertex, ValueStack> 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<DFSEntry> dfsStack;
|
|
std::set<GraphVertex> 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<CodeLine>::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;
|
|
}
|
|
}
|
|
|