| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- /* 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 2
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
- #include "decompiler_codegen.h"
- #include "decompiler_engine.h"
- #include <algorithm>
- #include <iostream>
- #include <set>
- #include <boost/format.hpp>
- #include "make_unique.h"
- #define GET(vertex) (boost::get(boost::vertex_name, _g, vertex))
- #define GET_EDGE(edge) (boost::get(boost::edge_attribute, _g, edge))
- void CodeGenerator::onBeforeStartFunction(const Function&)
- {
- }
- void CodeGenerator::onEndFunction(const Function &)
- {
- addOutputLine("}", true, false);
- }
- std::string CodeGenerator::constructFuncSignature(const Function &)
- {
- 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;
- mTargetLang = std::make_unique<CTargetLanguage>();
- }
- typedef std::pair<GraphVertex, ValueStack> DFSEntry;
- void CodeGenerator::generatePass(InstVec& insts, const Graph& g)
- {
- _g = g;
- for (FuncMap::iterator fn = _engine->_functions.begin(); fn != _engine->_functions.end(); ++fn)
- {
- while (!_stack.empty())
- {
- _stack.pop();
- }
- GraphVertex entryPoint = fn->second._v;
- std::string funcSignature = constructFuncSignature(fn->second);
- // Write the function start
- bool printFuncSignature = !funcSignature.empty();
- if (printFuncSignature)
- {
- mCurGroup = GET(entryPoint);
- if (!(fn == _engine->_functions.begin()))
- {
- addOutputLine("");
- }
- onBeforeStartFunction(fn->second);
- addOutputLine(funcSignature, false, true);
- onStartFunction(fn->second);
- }
- 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(fn->second, insts, 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);
- }
- }
- }
- // Write the function end
- if (printFuncSignature)
- {
- mCurGroup = lastGroup;
- onEndFunction(fn->second);
- }
- // Print output
- GroupPtr p = GET(entryPoint);
- while (p != NULL)
- {
- for (auto it = p->_code.begin(); it != p->_code.end(); ++it)
- {
- if (it->_unindentBefore)
- {
- assert(_indentLevel > 0);
- _indentLevel--;
- }
- if (OutputOnlyRequiredLabels())
- {
- _output << indentString(it->_line) << std::endl;
- }
- else
- {
- _output << boost::format("%08X: %s") % (*p->_start)->_address % indentString(it->_line) << std::endl;
- }
- if (it->_indentAfter)
- {
- _indentLevel++;
- }
- }
- p = p->_next;
- }
- }
- }
- void CodeGenerator::generate(InstVec& insts, const Graph &g)
- {
- if (OutputOnlyRequiredLabels())
- {
- // Call twice, once where no output is generated but instructions are
- // marked as "needs label", then a 2nd time to actually output the code
- mIsLabelPass = true;
- generatePass(insts, g);
- }
- mIsLabelPass = false;
- generatePass(insts, g);
- }
- void CodeGenerator::addOutputLine(std::string s, bool unindentBefore, bool indentAfter)
- {
- // We don't generate output in the labels pass, we just find instructions that
- // require a label to be outputted
- if (!mIsLabelPass)
- {
- mCurGroup->_code.push_back(CodeLine(s, unindentBefore, indentAfter));
- }
- }
- void CodeGenerator::writeAssignment(ValuePtr dst, ValuePtr src)
- {
- std::stringstream s;
- s << dst << " = " << src << mTargetLang->LineTerminator();
- addOutputLine(s.str());
- }
- void CodeGenerator::process(Function& func, InstVec& insts, GraphVertex v)
- {
- _curVertex = v;
- mCurGroup = GET(v);
- // Check if we should add else start
- if (mCurGroup->_startElse)
- {
- addOutputLine(mTargetLang->EndBlock(ITargetLanaguge::eToElseBlock) + " " + mTargetLang->Else() + " " + mTargetLang->StartBlock(ITargetLanaguge::eBeginElse), 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(mTargetLang->DoLoopHeader(), false, true);
- break;
- case kIfCondGroupType:
- if (!mCurGroup->_startElse)
- {
- addOutputLine(mTargetLang->EndBlock(ITargetLanaguge::eEndOfIf), true, false);
- }
- break;
- case kWhileCondGroupType:
- addOutputLine(mTargetLang->EndBlock(ITargetLanaguge::eEndOfWhile), true, false);
- break;
- default:
- break;
- }
- }
- ConstInstIterator it = mCurGroup->_start;
- do
- {
- // If we only want to write labels that targets of goto's then check if this is the pass
- // after we've setup mLabelRequired on each instruction. If this is set then it needs a label
- // so write one out.
- if (OutputOnlyRequiredLabels() && !mIsLabelPass && (*it)->mLabelRequired)
- {
- addOutputLine(mTargetLang->Label((*it)->_address));
- }
- processInst(func, insts, *it);
- } while (it++ != mCurGroup->_end);
- // Add else end if necessary
- for (ElseEndIterator elseIt = mCurGroup->_endElse.begin(); elseIt != mCurGroup->_endElse.end(); ++elseIt)
- {
- if (!(*elseIt)->_coalescedElse)
- {
- addOutputLine(mTargetLang->EndBlock(ITargetLanaguge::eEndIfElseChain), true, false);
- }
- }
- }
- void CodeGenerator::processUncondJumpInst(Function& func, InstVec& insts, const InstPtr inst)
- {
- switch (mCurGroup->_type)
- {
- case kBreakGroupType:
- addOutputLine(mTargetLang->LoopBreak());
- break;
- case kContinueGroupType:
- addOutputLine(mTargetLang->LoopContinue());
- break;
- default: // Might be a goto
- {
- bool printJump = true;
- OutEdgeRange jumpTargets = boost::out_edges(_curVertex, _g);
- for (OutEdgeIterator target = jumpTargets.first; target != jumpTargets.second && printJump; ++target)
- {
- Group* next = mCurGroup->_next;
- if (next)
- {
- // Don't output jump to next vertex
- if (boost::target(*target, _g) == next->_vertex)
- {
- printJump = false;
- break;
- }
- // Don't output jump if next vertex starts an else block
- if (next->_startElse)
- {
- printJump = false;
- break;
- }
- OutEdgeRange targetR = boost::out_edges(boost::target(*target, _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) == next->_vertex)
- {
- printJump = false;
- }
- }
- if (printJump)
- {
- // Check if this instruction is the last instruction in the function
- // and its an uncond jump
- if (mCurGroup->_type == kDoWhileCondGroupType && inst->_address == func.mEndAddr && inst->isUncondJump())
- {
- printJump = false;
- addOutputLine(mTargetLang->DoLoopFooter(true) + "true" + mTargetLang->DoLoopFooter(false), true, false);
- }
- }
- }
- }
- if (printJump)
- {
- const uint32 dstAddr = inst->getDestAddress();
- if (mIsLabelPass)
- {
- // Mark the goto target
- for (auto& i : insts)
- {
- if (i->_address == dstAddr)
- {
- i->mLabelRequired = true;
- break;
- }
- }
- }
- addOutputLine(mTargetLang->Goto(dstAddr));
- }
- }
- break;
- }
- }
- void CodeGenerator::writeFunctionCall(std::string functionName, std::string paramsFormat, const std::vector<ValuePtr>& params)
- {
- std::string strFuncCall = functionName + mTargetLang->FunctionCallBegin();
- const char* str = paramsFormat.c_str();
- int paramIndex = 0;
- while (*str)
- {
- bool skipArgument = false;
- switch (*str)
- {
- case 'b':
- strFuncCall += params[paramIndex]->getUnsigned() ? "true" : "false";
- break;
- case 'n':
- strFuncCall += std::to_string(params[paramIndex]->getUnsigned());
- break;
- case 'f':
- strFuncCall += std::to_string(static_cast<float>(params[paramIndex]->getUnsigned()) / 30.0f);
- break;
- case '_': // Ignore param
- skipArgument = true;
- break;
- default:
- throw std::runtime_error("Unknown param type");
- break;
- }
- paramIndex++;
- str++;
- if (*str)
- {
- // There is another param
- if (!skipArgument)
- {
- strFuncCall += mTargetLang->FunctionCallArgumentSeperator() + " ";
- }
- }
- }
- strFuncCall += mTargetLang->FunctionCallEnd();
- addOutputLine(strFuncCall);
- }
- void CodeGenerator::processCondJumpInst(const InstPtr inst)
- {
- std::stringstream s;
- switch (mCurGroup->_type)
- {
- case kIfCondGroupType:
- if (mCurGroup->_startElse && mCurGroup->_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(), mCurGroup.get()) != oGr->_endElse.end())
- {
- coalesceElse = true;
- }
- }
- if (coalesceElse)
- {
- mCurGroup->_code.clear();
- mCurGroup->_coalescedElse = true;
- s << mTargetLang->EndBlock(ITargetLanaguge::eToElseBlock) << " " << mTargetLang->Else() << " ";
- }
- }
- s << mTargetLang->If(true) << _stack.pop()->negate() << mTargetLang->If(false);
- addOutputLine(s.str(), mCurGroup->_coalescedElse, true);
- break;
- case kWhileCondGroupType:
- s << mTargetLang->WhileHeader(true) << _stack.pop()->negate() << mTargetLang->WhileHeader(false) << " " << mTargetLang->StartBlock(ITargetLanaguge::eBeginWhile);
- addOutputLine(s.str(), false, true);
- break;
- case kDoWhileCondGroupType:
- s << mTargetLang->EndBlock(ITargetLanaguge::eEndWhile) << " " << mTargetLang->WhileHeader(true) << _stack.pop() << mTargetLang->WhileHeader(false);
- addOutputLine(s.str(), true, false);
- break;
- default:
- break;
- }
- }
- void CodeGenerator::processInst(Function& func, InstVec& insts, const InstPtr inst)
- {
- inst->processInst(func, _stack, _engine, this);
- if (inst->isCondJump())
- {
- processCondJumpInst(inst);
- }
- else if (inst->isUncondJump())
- {
- processUncondJumpInst(func, insts, inst);
- }
- }
- 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)
- {
- switch (c)
- {
- case 'p':
- addArg(_stack.pop());
- break;
- default:
- std::cerr << boost::format("WARNING: Unknown character in metadata: %c\n") % c;
- break;
- }
- }
|