| Index: lib/Target/JSBackend/Relooper.cpp
|
| diff --git a/lib/Target/JSBackend/Relooper.cpp b/lib/Target/JSBackend/Relooper.cpp
|
| deleted file mode 100644
|
| index 39a2ad8bb8d53d4bc993e40d8245a06ef99cf52d..0000000000000000000000000000000000000000
|
| --- a/lib/Target/JSBackend/Relooper.cpp
|
| +++ /dev/null
|
| @@ -1,1438 +0,0 @@
|
| -// We are implementing the Relooper C API, so always export from this file.
|
| -#ifndef RELOOPERDLL_EXPORTS
|
| -#define RELOOPERDLL_EXPORTS
|
| -#endif
|
| -
|
| -#include "Relooper.h"
|
| -
|
| -#include <string.h>
|
| -#include <stdlib.h>
|
| -#include <list>
|
| -#include <stack>
|
| -
|
| -#if EMSCRIPTEN
|
| -#include "ministring.h"
|
| -#else
|
| -#include <string>
|
| -typedef std::string ministring;
|
| -#endif
|
| -
|
| -// uncomment these out to get LLVM errs() debugging support
|
| -//#include <llvm/Support/raw_ostream.h>
|
| -//using namespace llvm;
|
| -
|
| -template <class T, class U> static bool contains(const T& container, const U& contained) {
|
| - return container.count(contained);
|
| -}
|
| -
|
| -#if DEBUG
|
| -static void PrintDebug(const char *Format, ...);
|
| -#define DebugDump(x, ...) Debugging::Dump(x, __VA_ARGS__)
|
| -#else
|
| -#define PrintDebug(x, ...)
|
| -#define DebugDump(x, ...)
|
| -#endif
|
| -
|
| -#define INDENTATION 1
|
| -
|
| -struct Indenter {
|
| - static int CurrIndent;
|
| -
|
| - static void Indent() { CurrIndent++; }
|
| - static void Unindent() { CurrIndent--; }
|
| -};
|
| -
|
| -static void PrintIndented(const char *Format, ...);
|
| -static void PutIndented(const char *String);
|
| -
|
| -static char *OutputBufferRoot = NULL;
|
| -static char *OutputBuffer = NULL;
|
| -static int OutputBufferSize = 0;
|
| -static int OutputBufferOwned = false;
|
| -
|
| -static int LeftInOutputBuffer() {
|
| - return OutputBufferSize - (OutputBuffer - OutputBufferRoot);
|
| -}
|
| -
|
| -static bool EnsureOutputBuffer(int Needed) { // ensures the output buffer is sufficient. returns true is no problem happened
|
| - Needed++; // ensure the trailing \0 is not forgotten
|
| - int Left = LeftInOutputBuffer();
|
| - if (!OutputBufferOwned) {
|
| - assert(Needed < Left);
|
| - } else {
|
| - // we own the buffer, and can resize if necessary
|
| - if (Needed >= Left) {
|
| - int Offset = OutputBuffer - OutputBufferRoot;
|
| - int TotalNeeded = OutputBufferSize + Needed - Left + 10240;
|
| - int NewSize = OutputBufferSize;
|
| - while (NewSize < TotalNeeded) NewSize = NewSize + (NewSize/2);
|
| - //printf("resize %d => %d\n", OutputBufferSize, NewSize);
|
| - OutputBufferRoot = (char*)realloc(OutputBufferRoot, NewSize);
|
| - assert(OutputBufferRoot);
|
| - OutputBuffer = OutputBufferRoot + Offset;
|
| - OutputBufferSize = NewSize;
|
| - return false;
|
| - }
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -void PrintIndented(const char *Format, ...) {
|
| - assert(OutputBuffer);
|
| - EnsureOutputBuffer(Indenter::CurrIndent*INDENTATION);
|
| - for (int i = 0; i < Indenter::CurrIndent*INDENTATION; i++, OutputBuffer++) *OutputBuffer = ' ';
|
| - int Written;
|
| - while (1) { // write and potentially resize buffer until we have enough room
|
| - int Left = LeftInOutputBuffer();
|
| - va_list Args;
|
| - va_start(Args, Format);
|
| - Written = vsnprintf(OutputBuffer, Left, Format, Args);
|
| - va_end(Args);
|
| -#ifdef _MSC_VER
|
| - // VC CRT specific: vsnprintf returns -1 on failure, other runtimes return the number of characters that would have been
|
| - // written. On VC, if we get -1, count the number of characters manually.
|
| - if (Written < 0) {
|
| - va_start(Args, Format);
|
| - Written = _vscprintf(Format, Args);
|
| - va_end(Args);
|
| - }
|
| -#endif
|
| -
|
| - if (EnsureOutputBuffer(Written)) break;
|
| - }
|
| - OutputBuffer += Written;
|
| -}
|
| -
|
| -void PutIndented(const char *String) {
|
| - assert(OutputBuffer);
|
| - EnsureOutputBuffer(Indenter::CurrIndent*INDENTATION);
|
| - for (int i = 0; i < Indenter::CurrIndent*INDENTATION; i++, OutputBuffer++) *OutputBuffer = ' ';
|
| - int Needed = strlen(String)+1;
|
| - EnsureOutputBuffer(Needed);
|
| - strcpy(OutputBuffer, String);
|
| - OutputBuffer += strlen(String);
|
| - *OutputBuffer++ = '\n';
|
| - *OutputBuffer = 0;
|
| -}
|
| -
|
| -static int AsmJS = 0;
|
| -
|
| -// Indenter
|
| -
|
| -int Indenter::CurrIndent = 1;
|
| -
|
| -// Branch
|
| -
|
| -Branch::Branch(const char *ConditionInit, const char *CodeInit) : Ancestor(NULL), Labeled(true) {
|
| - Condition = ConditionInit ? strdup(ConditionInit) : NULL;
|
| - Code = CodeInit ? strdup(CodeInit) : NULL;
|
| -}
|
| -
|
| -Branch::~Branch() {
|
| - if (Condition) free((void*)Condition);
|
| - if (Code) free((void*)Code);
|
| -}
|
| -
|
| -void Branch::Render(Block *Target, bool SetLabel) {
|
| - if (Code) PrintIndented("%s\n", Code);
|
| - if (SetLabel) PrintIndented("label = %d;\n", Target->Id);
|
| - if (Ancestor) {
|
| - if (Type == Break || Type == Continue) {
|
| - if (Labeled) {
|
| - PrintIndented("%s L%d;\n", Type == Break ? "break" : "continue", Ancestor->Id);
|
| - } else {
|
| - PrintIndented("%s;\n", Type == Break ? "break" : "continue");
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -// Block
|
| -
|
| -Block::Block(const char *CodeInit, const char *BranchVarInit) : Parent(NULL), Id(-1), IsCheckedMultipleEntry(false) {
|
| - Code = strdup(CodeInit);
|
| - BranchVar = BranchVarInit ? strdup(BranchVarInit) : NULL;
|
| -}
|
| -
|
| -Block::~Block() {
|
| - if (Code) free((void*)Code);
|
| - if (BranchVar) free((void*)BranchVar);
|
| - for (BlockBranchMap::iterator iter = ProcessedBranchesOut.begin(); iter != ProcessedBranchesOut.end(); iter++) {
|
| - delete iter->second;
|
| - }
|
| - // XXX If not reachable, expected to have branches here. But need to clean them up to prevent leaks!
|
| -}
|
| -
|
| -void Block::AddBranchTo(Block *Target, const char *Condition, const char *Code) {
|
| - assert(!contains(BranchesOut, Target)); // cannot add more than one branch to the same target
|
| - BranchesOut[Target] = new Branch(Condition, Code);
|
| -}
|
| -
|
| -void Block::Render(bool InLoop) {
|
| - if (IsCheckedMultipleEntry && InLoop) {
|
| - PrintIndented("label = 0;\n");
|
| - }
|
| -
|
| - if (Code) {
|
| - // Print code in an indented manner, even over multiple lines
|
| - char *Start = const_cast<char*>(Code);
|
| - while (*Start) {
|
| - char *End = strchr(Start, '\n');
|
| - if (End) *End = 0;
|
| - PutIndented(Start);
|
| - if (End) *End = '\n'; else break;
|
| - Start = End+1;
|
| - }
|
| - }
|
| -
|
| - if (!ProcessedBranchesOut.size()) return;
|
| -
|
| - bool SetLabel = true; // in some cases it is clear we can avoid setting label, see later
|
| - bool ForceSetLabel = Shape::IsEmulated(Parent);
|
| -
|
| - // A setting of the label variable (label = x) is necessary if it can
|
| - // cause an impact. The main case is where we set label to x, then elsewhere
|
| - // we check if label is equal to that value, i.e., that label is an entry
|
| - // in a multiple block. We also need to reset the label when we enter
|
| - // that block, so that each setting is a one-time action: consider
|
| - //
|
| - // while (1) {
|
| - // if (check) label = 1;
|
| - // if (label == 1) { label = 0 }
|
| - // }
|
| - //
|
| - // (Note that this case is impossible due to fusing, but that is not
|
| - // material here.) So setting to 0 is important just to clear the 1 for
|
| - // future iterations.
|
| - // TODO: When inside a loop, if necessary clear the label variable
|
| - // once on the top, and never do settings that are in effect clears
|
| -
|
| - // Fusing: If the next is a Multiple, we can fuse it with this block. Note
|
| - // that we must be the Inner of a Simple, so fusing means joining a Simple
|
| - // to a Multiple. What happens there is that all options in the Multiple
|
| - // *must* appear in the Simple (the Simple is the only one reaching the
|
| - // Multiple), so we can remove the Multiple and add its independent groups
|
| - // into the Simple's branches.
|
| - MultipleShape *Fused = Shape::IsMultiple(Parent->Next);
|
| - if (Fused) {
|
| - PrintDebug("Fusing Multiple to Simple\n", 0);
|
| - Parent->Next = Parent->Next->Next;
|
| - Fused->UseSwitch = false; // TODO: emit switches here
|
| - Fused->RenderLoopPrefix();
|
| -
|
| - // When the Multiple has the same number of groups as we have branches,
|
| - // they will all be fused, so it is safe to not set the label at all
|
| - if (SetLabel && Fused->InnerMap.size() == ProcessedBranchesOut.size()) {
|
| - SetLabel = false;
|
| - }
|
| - }
|
| -
|
| - Block *DefaultTarget(NULL); // The block we branch to without checking the condition, if none of the other conditions held.
|
| -
|
| - // Find the default target, the one without a condition
|
| - for (BlockBranchMap::iterator iter = ProcessedBranchesOut.begin(); iter != ProcessedBranchesOut.end(); iter++) {
|
| - if (!iter->second->Condition) {
|
| - assert(!DefaultTarget); // Must be exactly one default
|
| - DefaultTarget = iter->first;
|
| - }
|
| - }
|
| - assert(DefaultTarget); // Since each block *must* branch somewhere, this must be set
|
| -
|
| - bool useSwitch = BranchVar != NULL;
|
| -
|
| - if (useSwitch) {
|
| - PrintIndented("switch (%s) {\n", BranchVar);
|
| - }
|
| -
|
| - ministring RemainingConditions;
|
| - bool First = !useSwitch; // when using a switch, there is no special first
|
| - for (BlockBranchMap::iterator iter = ProcessedBranchesOut.begin();; iter++) {
|
| - Block *Target;
|
| - Branch *Details;
|
| - if (iter != ProcessedBranchesOut.end()) {
|
| - Target = iter->first;
|
| - if (Target == DefaultTarget) continue; // done at the end
|
| - Details = iter->second;
|
| - assert(Details->Condition); // must have a condition if this is not the default target
|
| - } else {
|
| - Target = DefaultTarget;
|
| - Details = ProcessedBranchesOut[DefaultTarget];
|
| - }
|
| - bool SetCurrLabel = (SetLabel && Target->IsCheckedMultipleEntry) || ForceSetLabel;
|
| - bool HasFusedContent = Fused && contains(Fused->InnerMap, Target->Id);
|
| - bool HasContent = SetCurrLabel || Details->Type != Branch::Direct || HasFusedContent || Details->Code;
|
| - if (iter != ProcessedBranchesOut.end()) {
|
| - // If there is nothing to show in this branch, omit the condition
|
| - if (useSwitch) {
|
| - PrintIndented("%s {\n", Details->Condition);
|
| - } else {
|
| - if (HasContent) {
|
| - PrintIndented("%sif (%s) {\n", First ? "" : "} else ", Details->Condition);
|
| - First = false;
|
| - } else {
|
| - if (RemainingConditions.size() > 0) RemainingConditions += " && ";
|
| - RemainingConditions += "!(";
|
| - if (BranchVar) {
|
| - RemainingConditions += BranchVar;
|
| - RemainingConditions += " == ";
|
| - }
|
| - RemainingConditions += Details->Condition;
|
| - RemainingConditions += ")";
|
| - }
|
| - }
|
| - } else {
|
| - // this is the default
|
| - if (useSwitch) {
|
| - PrintIndented("default: {\n");
|
| - } else {
|
| - if (HasContent) {
|
| - if (RemainingConditions.size() > 0) {
|
| - if (First) {
|
| - PrintIndented("if (%s) {\n", RemainingConditions.c_str());
|
| - First = false;
|
| - } else {
|
| - PrintIndented("} else if (%s) {\n", RemainingConditions.c_str());
|
| - }
|
| - } else if (!First) {
|
| - PrintIndented("} else {\n");
|
| - }
|
| - }
|
| - }
|
| - }
|
| - if (!First) Indenter::Indent();
|
| - Details->Render(Target, SetCurrLabel);
|
| - if (HasFusedContent) {
|
| - Fused->InnerMap.find(Target->Id)->second->Render(InLoop);
|
| - } else if (Details->Type == Branch::Nested) {
|
| - // Nest the parent content here, and remove it from showing up afterwards as Next
|
| - assert(Parent->Next);
|
| - Parent->Next->Render(InLoop);
|
| - Parent->Next = NULL;
|
| - }
|
| - if (useSwitch && iter != ProcessedBranchesOut.end()) {
|
| - PrintIndented("break;\n");
|
| - }
|
| - if (!First) Indenter::Unindent();
|
| - if (useSwitch) {
|
| - PrintIndented("}\n");
|
| - }
|
| - if (iter == ProcessedBranchesOut.end()) break;
|
| - }
|
| - if (!First) PrintIndented("}\n");
|
| -
|
| - if (Fused) {
|
| - Fused->RenderLoopPostfix();
|
| - }
|
| -}
|
| -
|
| -// MultipleShape
|
| -
|
| -void MultipleShape::RenderLoopPrefix() {
|
| - if (Breaks) {
|
| - if (UseSwitch) {
|
| - if (Labeled) {
|
| - PrintIndented("L%d: ", Id);
|
| - }
|
| - } else {
|
| - if (Labeled) {
|
| - PrintIndented("L%d: do {\n", Id);
|
| - } else {
|
| - PrintIndented("do {\n");
|
| - }
|
| - Indenter::Indent();
|
| - }
|
| - }
|
| -}
|
| -
|
| -void MultipleShape::RenderLoopPostfix() {
|
| - if (Breaks && !UseSwitch) {
|
| - Indenter::Unindent();
|
| - PrintIndented("} while(0);\n");
|
| - }
|
| -}
|
| -
|
| -void MultipleShape::Render(bool InLoop) {
|
| - RenderLoopPrefix();
|
| -
|
| - if (!UseSwitch) {
|
| - // emit an if-else chain
|
| - bool First = true;
|
| - for (IdShapeMap::iterator iter = InnerMap.begin(); iter != InnerMap.end(); iter++) {
|
| - if (AsmJS) {
|
| - PrintIndented("%sif ((label|0) == %d) {\n", First ? "" : "else ", iter->first);
|
| - } else {
|
| - PrintIndented("%sif (label == %d) {\n", First ? "" : "else ", iter->first);
|
| - }
|
| - First = false;
|
| - Indenter::Indent();
|
| - iter->second->Render(InLoop);
|
| - Indenter::Unindent();
|
| - PrintIndented("}\n");
|
| - }
|
| - } else {
|
| - // emit a switch
|
| - if (AsmJS) {
|
| - PrintIndented("switch (label|0) {\n");
|
| - } else {
|
| - PrintIndented("switch (label) {\n");
|
| - }
|
| - Indenter::Indent();
|
| - for (IdShapeMap::iterator iter = InnerMap.begin(); iter != InnerMap.end(); iter++) {
|
| - PrintIndented("case %d: {\n", iter->first);
|
| - Indenter::Indent();
|
| - iter->second->Render(InLoop);
|
| - PrintIndented("break;\n");
|
| - Indenter::Unindent();
|
| - PrintIndented("}\n");
|
| - }
|
| - Indenter::Unindent();
|
| - PrintIndented("}\n");
|
| - }
|
| -
|
| - RenderLoopPostfix();
|
| - if (Next) Next->Render(InLoop);
|
| -}
|
| -
|
| -// LoopShape
|
| -
|
| -void LoopShape::Render(bool InLoop) {
|
| - if (Labeled) {
|
| - PrintIndented("L%d: while(1) {\n", Id);
|
| - } else {
|
| - PrintIndented("while(1) {\n");
|
| - }
|
| - Indenter::Indent();
|
| - Inner->Render(true);
|
| - Indenter::Unindent();
|
| - PrintIndented("}\n");
|
| - if (Next) Next->Render(InLoop);
|
| -}
|
| -
|
| -// EmulatedShape
|
| -
|
| -void EmulatedShape::Render(bool InLoop) {
|
| - PrintIndented("label = %d;\n", Entry->Id);
|
| - if (Labeled) {
|
| - PrintIndented("L%d: ", Id);
|
| - }
|
| - PrintIndented("while(1) {\n");
|
| - Indenter::Indent();
|
| - PrintIndented("switch(label|0) {\n");
|
| - Indenter::Indent();
|
| - for (BlockSet::iterator iter = Blocks.begin(); iter != Blocks.end(); iter++) {
|
| - Block *Curr = *iter;
|
| - PrintIndented("case %d: {\n", Curr->Id);
|
| - Indenter::Indent();
|
| - Curr->Render(InLoop);
|
| - PrintIndented("break;\n");
|
| - Indenter::Unindent();
|
| - PrintIndented("}\n");
|
| - }
|
| - Indenter::Unindent();
|
| - PrintIndented("}\n");
|
| - Indenter::Unindent();
|
| - PrintIndented("}\n");
|
| - if (Next) Next->Render(InLoop);
|
| -}
|
| -
|
| -// Relooper
|
| -
|
| -Relooper::Relooper() : Root(NULL), Emulate(false), MinSize(false), BlockIdCounter(1), ShapeIdCounter(0) { // block ID 0 is reserved for clearings
|
| -}
|
| -
|
| -Relooper::~Relooper() {
|
| - for (unsigned i = 0; i < Blocks.size(); i++) delete Blocks[i];
|
| - for (unsigned i = 0; i < Shapes.size(); i++) delete Shapes[i];
|
| -}
|
| -
|
| -void Relooper::AddBlock(Block *New, int Id) {
|
| - New->Id = Id == -1 ? BlockIdCounter++ : Id;
|
| - Blocks.push_back(New);
|
| -}
|
| -
|
| -struct RelooperRecursor {
|
| - Relooper *Parent;
|
| - RelooperRecursor(Relooper *ParentInit) : Parent(ParentInit) {}
|
| -};
|
| -
|
| -typedef std::list<Block*> BlockList;
|
| -
|
| -void Relooper::Calculate(Block *Entry) {
|
| - // Scan and optimize the input
|
| - struct PreOptimizer : public RelooperRecursor {
|
| - PreOptimizer(Relooper *Parent) : RelooperRecursor(Parent) {}
|
| - BlockSet Live;
|
| -
|
| - void FindLive(Block *Root) {
|
| - BlockList ToInvestigate;
|
| - ToInvestigate.push_back(Root);
|
| - while (ToInvestigate.size() > 0) {
|
| - Block *Curr = ToInvestigate.front();
|
| - ToInvestigate.pop_front();
|
| - if (contains(Live, Curr)) continue;
|
| - Live.insert(Curr);
|
| - for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter != Curr->BranchesOut.end(); iter++) {
|
| - ToInvestigate.push_back(iter->first);
|
| - }
|
| - }
|
| - }
|
| -
|
| - // If a block has multiple entries but no exits, and it is small enough, it is useful to split it.
|
| - // A common example is a C++ function where everything ends up at a final exit block and does some
|
| - // RAII cleanup. Without splitting, we will be forced to introduce labelled loops to allow
|
| - // reaching the final block
|
| - void SplitDeadEnds() {
|
| - unsigned TotalCodeSize = 0;
|
| - for (BlockSet::iterator iter = Live.begin(); iter != Live.end(); iter++) {
|
| - Block *Curr = *iter;
|
| - TotalCodeSize += strlen(Curr->Code);
|
| - }
|
| - BlockSet Splits;
|
| - BlockSet Removed;
|
| - //DebugDump(Live, "before");
|
| - for (BlockSet::iterator iter = Live.begin(); iter != Live.end(); iter++) {
|
| - Block *Original = *iter;
|
| - if (Original->BranchesIn.size() <= 1 || Original->BranchesOut.size() > 0) continue; // only dead ends, for now
|
| - if (contains(Original->BranchesOut, Original)) continue; // cannot split a looping node
|
| - if (strlen(Original->Code)*(Original->BranchesIn.size()-1) > TotalCodeSize/5) continue; // if splitting increases raw code size by a significant amount, abort
|
| - // Split the node (for simplicity, we replace all the blocks, even though we could have reused the original)
|
| - PrintDebug("Splitting block %d\n", Original->Id);
|
| - for (BlockSet::iterator iter = Original->BranchesIn.begin(); iter != Original->BranchesIn.end(); iter++) {
|
| - Block *Prior = *iter;
|
| - Block *Split = new Block(Original->Code, Original->BranchVar);
|
| - Parent->AddBlock(Split, Original->Id);
|
| - Split->BranchesIn.insert(Prior);
|
| - Branch *Details = Prior->BranchesOut[Original];
|
| - Prior->BranchesOut[Split] = new Branch(Details->Condition, Details->Code);
|
| - Prior->BranchesOut.erase(Original);
|
| - for (BlockBranchMap::iterator iter = Original->BranchesOut.begin(); iter != Original->BranchesOut.end(); iter++) {
|
| - Block *Post = iter->first;
|
| - Branch *Details = iter->second;
|
| - Split->BranchesOut[Post] = new Branch(Details->Condition, Details->Code);
|
| - Post->BranchesIn.insert(Split);
|
| - }
|
| - Splits.insert(Split);
|
| - Removed.insert(Original);
|
| - }
|
| - for (BlockBranchMap::iterator iter = Original->BranchesOut.begin(); iter != Original->BranchesOut.end(); iter++) {
|
| - Block *Post = iter->first;
|
| - Post->BranchesIn.erase(Original);
|
| - }
|
| - //DebugDump(Live, "mid");
|
| - }
|
| - for (BlockSet::iterator iter = Splits.begin(); iter != Splits.end(); iter++) {
|
| - Live.insert(*iter);
|
| - }
|
| - for (BlockSet::iterator iter = Removed.begin(); iter != Removed.end(); iter++) {
|
| - Live.erase(*iter);
|
| - }
|
| - //DebugDump(Live, "after");
|
| - }
|
| - };
|
| - PreOptimizer Pre(this);
|
| - Pre.FindLive(Entry);
|
| -
|
| - // Add incoming branches from live blocks, ignoring dead code
|
| - for (unsigned i = 0; i < Blocks.size(); i++) {
|
| - Block *Curr = Blocks[i];
|
| - if (!contains(Pre.Live, Curr)) continue;
|
| - for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter != Curr->BranchesOut.end(); iter++) {
|
| - iter->first->BranchesIn.insert(Curr);
|
| - }
|
| - }
|
| -
|
| - if (!Emulate && !MinSize) Pre.SplitDeadEnds();
|
| -
|
| - // Recursively process the graph
|
| -
|
| - struct Analyzer : public RelooperRecursor {
|
| - Analyzer(Relooper *Parent) : RelooperRecursor(Parent) {}
|
| -
|
| - // Add a shape to the list of shapes in this Relooper calculation
|
| - void Notice(Shape *New) {
|
| - New->Id = Parent->ShapeIdCounter++;
|
| - Parent->Shapes.push_back(New);
|
| - }
|
| -
|
| - // Create a list of entries from a block. If LimitTo is provided, only results in that set
|
| - // will appear
|
| - void GetBlocksOut(Block *Source, BlockSet& Entries, BlockSet *LimitTo=NULL) {
|
| - for (BlockBranchMap::iterator iter = Source->BranchesOut.begin(); iter != Source->BranchesOut.end(); iter++) {
|
| - if (!LimitTo || contains(*LimitTo, iter->first)) {
|
| - Entries.insert(iter->first);
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Converts/processes all branchings to a specific target
|
| - void Solipsize(Block *Target, Branch::FlowType Type, Shape *Ancestor, BlockSet &From) {
|
| - PrintDebug("Solipsizing branches into %d\n", Target->Id);
|
| - DebugDump(From, " relevant to solipsize: ");
|
| - for (BlockSet::iterator iter = Target->BranchesIn.begin(); iter != Target->BranchesIn.end();) {
|
| - Block *Prior = *iter;
|
| - if (!contains(From, Prior)) {
|
| - iter++;
|
| - continue;
|
| - }
|
| - Branch *PriorOut = Prior->BranchesOut[Target];
|
| - PriorOut->Ancestor = Ancestor;
|
| - PriorOut->Type = Type;
|
| - if (MultipleShape *Multiple = Shape::IsMultiple(Ancestor)) {
|
| - Multiple->Breaks++; // We are breaking out of this Multiple, so need a loop
|
| - }
|
| - iter++; // carefully increment iter before erasing
|
| - Target->BranchesIn.erase(Prior);
|
| - Target->ProcessedBranchesIn.insert(Prior);
|
| - Prior->BranchesOut.erase(Target);
|
| - Prior->ProcessedBranchesOut[Target] = PriorOut;
|
| - PrintDebug(" eliminated branch from %d\n", Prior->Id);
|
| - }
|
| - }
|
| -
|
| - Shape *MakeSimple(BlockSet &Blocks, Block *Inner, BlockSet &NextEntries) {
|
| - PrintDebug("creating simple block with block #%d\n", Inner->Id);
|
| - SimpleShape *Simple = new SimpleShape;
|
| - Notice(Simple);
|
| - Simple->Inner = Inner;
|
| - Inner->Parent = Simple;
|
| - if (Blocks.size() > 1) {
|
| - Blocks.erase(Inner);
|
| - GetBlocksOut(Inner, NextEntries, &Blocks);
|
| - BlockSet JustInner;
|
| - JustInner.insert(Inner);
|
| - for (BlockSet::iterator iter = NextEntries.begin(); iter != NextEntries.end(); iter++) {
|
| - Solipsize(*iter, Branch::Direct, Simple, JustInner);
|
| - }
|
| - }
|
| - return Simple;
|
| - }
|
| -
|
| - Shape *MakeEmulated(BlockSet &Blocks, Block *Entry, BlockSet &NextEntries) {
|
| - PrintDebug("creating emulated block with entry #%d and everything it can reach, %d blocks\n", Entry->Id, Blocks.size());
|
| - EmulatedShape *Emulated = new EmulatedShape;
|
| - Notice(Emulated);
|
| - Emulated->Entry = Entry;
|
| - for (BlockSet::iterator iter = Blocks.begin(); iter != Blocks.end(); iter++) {
|
| - Block *Curr = *iter;
|
| - Emulated->Blocks.insert(Curr);
|
| - Curr->Parent = Emulated;
|
| - Solipsize(Curr, Branch::Continue, Emulated, Blocks);
|
| - }
|
| - Blocks.clear();
|
| - return Emulated;
|
| - }
|
| -
|
| - Shape *MakeLoop(BlockSet &Blocks, BlockSet& Entries, BlockSet &NextEntries) {
|
| - // Find the inner blocks in this loop. Proceed backwards from the entries until
|
| - // you reach a seen block, collecting as you go.
|
| - BlockSet InnerBlocks;
|
| - BlockSet Queue = Entries;
|
| - while (Queue.size() > 0) {
|
| - Block *Curr = *(Queue.begin());
|
| - Queue.erase(Queue.begin());
|
| - if (!contains(InnerBlocks, Curr)) {
|
| - // This element is new, mark it as inner and remove from outer
|
| - InnerBlocks.insert(Curr);
|
| - Blocks.erase(Curr);
|
| - // Add the elements prior to it
|
| - for (BlockSet::iterator iter = Curr->BranchesIn.begin(); iter != Curr->BranchesIn.end(); iter++) {
|
| - Queue.insert(*iter);
|
| - }
|
| -#if 0
|
| - // Add elements it leads to, if they are dead ends. There is no reason not to hoist dead ends
|
| - // into loops, as it can avoid multiple entries after the loop
|
| - for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter != Curr->BranchesOut.end(); iter++) {
|
| - Block *Target = iter->first;
|
| - if (Target->BranchesIn.size() <= 1 && Target->BranchesOut.size() == 0) {
|
| - Queue.insert(Target);
|
| - }
|
| - }
|
| -#endif
|
| - }
|
| - }
|
| - assert(InnerBlocks.size() > 0);
|
| -
|
| - for (BlockSet::iterator iter = InnerBlocks.begin(); iter != InnerBlocks.end(); iter++) {
|
| - Block *Curr = *iter;
|
| - for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter != Curr->BranchesOut.end(); iter++) {
|
| - Block *Possible = iter->first;
|
| - if (!contains(InnerBlocks, Possible)) {
|
| - NextEntries.insert(Possible);
|
| - }
|
| - }
|
| - }
|
| -
|
| -#if 0
|
| - // We can avoid multiple next entries by hoisting them into the loop.
|
| - if (NextEntries.size() > 1) {
|
| - BlockBlockSetMap IndependentGroups;
|
| - FindIndependentGroups(NextEntries, IndependentGroups, &InnerBlocks);
|
| -
|
| - while (IndependentGroups.size() > 0 && NextEntries.size() > 1) {
|
| - Block *Min = NULL;
|
| - int MinSize = 0;
|
| - for (BlockBlockSetMap::iterator iter = IndependentGroups.begin(); iter != IndependentGroups.end(); iter++) {
|
| - Block *Entry = iter->first;
|
| - BlockSet &Blocks = iter->second;
|
| - if (!Min || Blocks.size() < MinSize) { // TODO: code size, not # of blocks
|
| - Min = Entry;
|
| - MinSize = Blocks.size();
|
| - }
|
| - }
|
| - // check how many new entries this would cause
|
| - BlockSet &Hoisted = IndependentGroups[Min];
|
| - bool abort = false;
|
| - for (BlockSet::iterator iter = Hoisted.begin(); iter != Hoisted.end() && !abort; iter++) {
|
| - Block *Curr = *iter;
|
| - for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter != Curr->BranchesOut.end(); iter++) {
|
| - Block *Target = iter->first;
|
| - if (!contains(Hoisted, Target) && !contains(NextEntries, Target)) {
|
| - // abort this hoisting
|
| - abort = true;
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - if (abort) {
|
| - IndependentGroups.erase(Min);
|
| - continue;
|
| - }
|
| - // hoist this entry
|
| - PrintDebug("hoisting %d into loop\n", Min->Id);
|
| - NextEntries.erase(Min);
|
| - for (BlockSet::iterator iter = Hoisted.begin(); iter != Hoisted.end(); iter++) {
|
| - Block *Curr = *iter;
|
| - InnerBlocks.insert(Curr);
|
| - Blocks.erase(Curr);
|
| - }
|
| - IndependentGroups.erase(Min);
|
| - }
|
| - }
|
| -#endif
|
| -
|
| - PrintDebug("creating loop block:\n", 0);
|
| - DebugDump(InnerBlocks, " inner blocks:");
|
| - DebugDump(Entries, " inner entries:");
|
| - DebugDump(Blocks, " outer blocks:");
|
| - DebugDump(NextEntries, " outer entries:");
|
| -
|
| - LoopShape *Loop = new LoopShape();
|
| - Notice(Loop);
|
| -
|
| - // Solipsize the loop, replacing with break/continue and marking branches as Processed (will not affect later calculations)
|
| - // A. Branches to the loop entries become a continue to this shape
|
| - for (BlockSet::iterator iter = Entries.begin(); iter != Entries.end(); iter++) {
|
| - Solipsize(*iter, Branch::Continue, Loop, InnerBlocks);
|
| - }
|
| - // B. Branches to outside the loop (a next entry) become breaks on this shape
|
| - for (BlockSet::iterator iter = NextEntries.begin(); iter != NextEntries.end(); iter++) {
|
| - Solipsize(*iter, Branch::Break, Loop, InnerBlocks);
|
| - }
|
| - // Finish up
|
| - Shape *Inner = Process(InnerBlocks, Entries, NULL);
|
| - Loop->Inner = Inner;
|
| - return Loop;
|
| - }
|
| -
|
| - // For each entry, find the independent group reachable by it. The independent group is
|
| - // the entry itself, plus all the blocks it can reach that cannot be directly reached by another entry. Note that we
|
| - // ignore directly reaching the entry itself by another entry.
|
| - // @param Ignore - previous blocks that are irrelevant
|
| - void FindIndependentGroups(BlockSet &Entries, BlockBlockSetMap& IndependentGroups, BlockSet *Ignore=NULL) {
|
| - typedef std::map<Block*, Block*> BlockBlockMap;
|
| -
|
| - struct HelperClass {
|
| - BlockBlockSetMap& IndependentGroups;
|
| - BlockBlockMap Ownership; // For each block, which entry it belongs to. We have reached it from there.
|
| -
|
| - HelperClass(BlockBlockSetMap& IndependentGroupsInit) : IndependentGroups(IndependentGroupsInit) {}
|
| - void InvalidateWithChildren(Block *New) { // TODO: rename New
|
| - BlockList ToInvalidate; // Being in the list means you need to be invalidated
|
| - ToInvalidate.push_back(New);
|
| - while (ToInvalidate.size() > 0) {
|
| - Block *Invalidatee = ToInvalidate.front();
|
| - ToInvalidate.pop_front();
|
| - Block *Owner = Ownership[Invalidatee];
|
| - if (contains(IndependentGroups, Owner)) { // Owner may have been invalidated, do not add to IndependentGroups!
|
| - IndependentGroups[Owner].erase(Invalidatee);
|
| - }
|
| - if (Ownership[Invalidatee]) { // may have been seen before and invalidated already
|
| - Ownership[Invalidatee] = NULL;
|
| - for (BlockBranchMap::iterator iter = Invalidatee->BranchesOut.begin(); iter != Invalidatee->BranchesOut.end(); iter++) {
|
| - Block *Target = iter->first;
|
| - BlockBlockMap::iterator Known = Ownership.find(Target);
|
| - if (Known != Ownership.end()) {
|
| - Block *TargetOwner = Known->second;
|
| - if (TargetOwner) {
|
| - ToInvalidate.push_back(Target);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| - };
|
| - HelperClass Helper(IndependentGroups);
|
| -
|
| - // We flow out from each of the entries, simultaneously.
|
| - // When we reach a new block, we add it as belonging to the one we got to it from.
|
| - // If we reach a new block that is already marked as belonging to someone, it is reachable by
|
| - // two entries and is not valid for any of them. Remove it and all it can reach that have been
|
| - // visited.
|
| -
|
| - BlockList Queue; // Being in the queue means we just added this item, and we need to add its children
|
| - for (BlockSet::iterator iter = Entries.begin(); iter != Entries.end(); iter++) {
|
| - Block *Entry = *iter;
|
| - Helper.Ownership[Entry] = Entry;
|
| - IndependentGroups[Entry].insert(Entry);
|
| - Queue.push_back(Entry);
|
| - }
|
| - while (Queue.size() > 0) {
|
| - Block *Curr = Queue.front();
|
| - Queue.pop_front();
|
| - Block *Owner = Helper.Ownership[Curr]; // Curr must be in the ownership map if we are in the queue
|
| - if (!Owner) continue; // we have been invalidated meanwhile after being reached from two entries
|
| - // Add all children
|
| - for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter != Curr->BranchesOut.end(); iter++) {
|
| - Block *New = iter->first;
|
| - BlockBlockMap::iterator Known = Helper.Ownership.find(New);
|
| - if (Known == Helper.Ownership.end()) {
|
| - // New node. Add it, and put it in the queue
|
| - Helper.Ownership[New] = Owner;
|
| - IndependentGroups[Owner].insert(New);
|
| - Queue.push_back(New);
|
| - continue;
|
| - }
|
| - Block *NewOwner = Known->second;
|
| - if (!NewOwner) continue; // We reached an invalidated node
|
| - if (NewOwner != Owner) {
|
| - // Invalidate this and all reachable that we have seen - we reached this from two locations
|
| - Helper.InvalidateWithChildren(New);
|
| - }
|
| - // otherwise, we have the same owner, so do nothing
|
| - }
|
| - }
|
| -
|
| - // Having processed all the interesting blocks, we remain with just one potential issue:
|
| - // If a->b, and a was invalidated, but then b was later reached by someone else, we must
|
| - // invalidate b. To check for this, we go over all elements in the independent groups,
|
| - // if an element has a parent which does *not* have the same owner, we must remove it
|
| - // and all its children.
|
| -
|
| - for (BlockSet::iterator iter = Entries.begin(); iter != Entries.end(); iter++) {
|
| - BlockSet &CurrGroup = IndependentGroups[*iter];
|
| - BlockList ToInvalidate;
|
| - for (BlockSet::iterator iter = CurrGroup.begin(); iter != CurrGroup.end(); iter++) {
|
| - Block *Child = *iter;
|
| - for (BlockSet::iterator iter = Child->BranchesIn.begin(); iter != Child->BranchesIn.end(); iter++) {
|
| - Block *Parent = *iter;
|
| - if (Ignore && contains(*Ignore, Parent)) continue;
|
| - if (Helper.Ownership[Parent] != Helper.Ownership[Child]) {
|
| - ToInvalidate.push_back(Child);
|
| - }
|
| - }
|
| - }
|
| - while (ToInvalidate.size() > 0) {
|
| - Block *Invalidatee = ToInvalidate.front();
|
| - ToInvalidate.pop_front();
|
| - Helper.InvalidateWithChildren(Invalidatee);
|
| - }
|
| - }
|
| -
|
| - // Remove empty groups
|
| - for (BlockSet::iterator iter = Entries.begin(); iter != Entries.end(); iter++) {
|
| - if (IndependentGroups[*iter].size() == 0) {
|
| - IndependentGroups.erase(*iter);
|
| - }
|
| - }
|
| -
|
| -#if DEBUG
|
| - PrintDebug("Investigated independent groups:\n");
|
| - for (BlockBlockSetMap::iterator iter = IndependentGroups.begin(); iter != IndependentGroups.end(); iter++) {
|
| - DebugDump(iter->second, " group: ");
|
| - }
|
| -#endif
|
| - }
|
| -
|
| - Shape *MakeMultiple(BlockSet &Blocks, BlockSet& Entries, BlockBlockSetMap& IndependentGroups, Shape *Prev, BlockSet &NextEntries) {
|
| - PrintDebug("creating multiple block with %d inner groups\n", IndependentGroups.size());
|
| - bool Fused = !!(Shape::IsSimple(Prev));
|
| - MultipleShape *Multiple = new MultipleShape();
|
| - Notice(Multiple);
|
| - BlockSet CurrEntries;
|
| - for (BlockBlockSetMap::iterator iter = IndependentGroups.begin(); iter != IndependentGroups.end(); iter++) {
|
| - Block *CurrEntry = iter->first;
|
| - BlockSet &CurrBlocks = iter->second;
|
| - PrintDebug(" multiple group with entry %d:\n", CurrEntry->Id);
|
| - DebugDump(CurrBlocks, " ");
|
| - // Create inner block
|
| - CurrEntries.clear();
|
| - CurrEntries.insert(CurrEntry);
|
| - for (BlockSet::iterator iter = CurrBlocks.begin(); iter != CurrBlocks.end(); iter++) {
|
| - Block *CurrInner = *iter;
|
| - // Remove the block from the remaining blocks
|
| - Blocks.erase(CurrInner);
|
| - // Find new next entries and fix branches to them
|
| - for (BlockBranchMap::iterator iter = CurrInner->BranchesOut.begin(); iter != CurrInner->BranchesOut.end();) {
|
| - Block *CurrTarget = iter->first;
|
| - BlockBranchMap::iterator Next = iter;
|
| - Next++;
|
| - if (!contains(CurrBlocks, CurrTarget)) {
|
| - NextEntries.insert(CurrTarget);
|
| - Solipsize(CurrTarget, Branch::Break, Multiple, CurrBlocks);
|
| - }
|
| - iter = Next; // increment carefully because Solipsize can remove us
|
| - }
|
| - }
|
| - Multiple->InnerMap[CurrEntry->Id] = Process(CurrBlocks, CurrEntries, NULL);
|
| - // If we are not fused, then our entries will actually be checked
|
| - if (!Fused) {
|
| - CurrEntry->IsCheckedMultipleEntry = true;
|
| - }
|
| - }
|
| - DebugDump(Blocks, " remaining blocks after multiple:");
|
| - // Add entries not handled as next entries, they are deferred
|
| - for (BlockSet::iterator iter = Entries.begin(); iter != Entries.end(); iter++) {
|
| - Block *Entry = *iter;
|
| - if (!contains(IndependentGroups, Entry)) {
|
| - NextEntries.insert(Entry);
|
| - }
|
| - }
|
| - // The multiple has been created, we can decide how to implement it
|
| - if (Multiple->InnerMap.size() >= 10) {
|
| - Multiple->UseSwitch = true;
|
| - Multiple->Breaks++; // switch captures breaks
|
| - }
|
| - return Multiple;
|
| - }
|
| -
|
| - // Main function.
|
| - // Process a set of blocks with specified entries, returns a shape
|
| - // The Make* functions receive a NextEntries. If they fill it with data, those are the entries for the
|
| - // ->Next block on them, and the blocks are what remains in Blocks (which Make* modify). In this way
|
| - // we avoid recursing on Next (imagine a long chain of Simples, if we recursed we could blow the stack).
|
| - Shape *Process(BlockSet &Blocks, BlockSet& InitialEntries, Shape *Prev) {
|
| - PrintDebug("Process() called\n", 0);
|
| - BlockSet *Entries = &InitialEntries;
|
| - BlockSet TempEntries[2];
|
| - int CurrTempIndex = 0;
|
| - BlockSet *NextEntries;
|
| - Shape *Ret = NULL;
|
| - #define Make(call) \
|
| - Shape *Temp = call; \
|
| - if (Prev) Prev->Next = Temp; \
|
| - if (!Ret) Ret = Temp; \
|
| - if (!NextEntries->size()) { PrintDebug("Process() returning\n", 0); return Ret; } \
|
| - Prev = Temp; \
|
| - Entries = NextEntries; \
|
| - continue;
|
| - while (1) {
|
| - PrintDebug("Process() running\n", 0);
|
| - DebugDump(Blocks, " blocks : ");
|
| - DebugDump(*Entries, " entries: ");
|
| -
|
| - CurrTempIndex = 1-CurrTempIndex;
|
| - NextEntries = &TempEntries[CurrTempIndex];
|
| - NextEntries->clear();
|
| -
|
| - if (Entries->size() == 0) return Ret;
|
| - if (Entries->size() == 1) {
|
| - Block *Curr = *(Entries->begin());
|
| - if (Parent->Emulate) {
|
| - Make(MakeEmulated(Blocks, Curr, *NextEntries));
|
| - }
|
| - if (Curr->BranchesIn.size() == 0) {
|
| - // One entry, no looping ==> Simple
|
| - Make(MakeSimple(Blocks, Curr, *NextEntries));
|
| - }
|
| - // One entry, looping ==> Loop
|
| - Make(MakeLoop(Blocks, *Entries, *NextEntries));
|
| - }
|
| -
|
| - // More than one entry, try to eliminate through a Multiple groups of
|
| - // independent blocks from an entry/ies. It is important to remove through
|
| - // multiples as opposed to looping since the former is more performant.
|
| - BlockBlockSetMap IndependentGroups;
|
| - FindIndependentGroups(*Entries, IndependentGroups);
|
| -
|
| - PrintDebug("Independent groups: %d\n", IndependentGroups.size());
|
| -
|
| - if (IndependentGroups.size() > 0) {
|
| - // We can handle a group in a multiple if its entry cannot be reached by another group.
|
| - // Note that it might be reachable by itself - a loop. But that is fine, we will create
|
| - // a loop inside the multiple block (which is the performant order to do it).
|
| - for (BlockBlockSetMap::iterator iter = IndependentGroups.begin(); iter != IndependentGroups.end();) {
|
| - Block *Entry = iter->first;
|
| - BlockSet &Group = iter->second;
|
| - BlockBlockSetMap::iterator curr = iter++; // iterate carefully, we may delete
|
| - for (BlockSet::iterator iterBranch = Entry->BranchesIn.begin(); iterBranch != Entry->BranchesIn.end(); iterBranch++) {
|
| - Block *Origin = *iterBranch;
|
| - if (!contains(Group, Origin)) {
|
| - // Reached from outside the group, so we cannot handle this
|
| - PrintDebug("Cannot handle group with entry %d because of incoming branch from %d\n", Entry->Id, Origin->Id);
|
| - IndependentGroups.erase(curr);
|
| - break;
|
| - }
|
| - }
|
| - }
|
| -
|
| - // As an optimization, if we have 2 independent groups, and one is a small dead end, we can handle only that dead end.
|
| - // The other then becomes a Next - without nesting in the code and recursion in the analysis.
|
| - // TODO: if the larger is the only dead end, handle that too
|
| - // TODO: handle >2 groups
|
| - // TODO: handle not just dead ends, but also that do not branch to the NextEntries. However, must be careful
|
| - // there since we create a Next, and that Next can prevent eliminating a break (since we no longer
|
| - // naturally reach the same place), which may necessitate a one-time loop, which makes the unnesting
|
| - // pointless.
|
| - if (IndependentGroups.size() == 2) {
|
| - // Find the smaller one
|
| - BlockBlockSetMap::iterator iter = IndependentGroups.begin();
|
| - Block *SmallEntry = iter->first;
|
| - int SmallSize = iter->second.size();
|
| - iter++;
|
| - Block *LargeEntry = iter->first;
|
| - int LargeSize = iter->second.size();
|
| - if (SmallSize != LargeSize) { // ignore the case where they are identical - keep things symmetrical there
|
| - if (SmallSize > LargeSize) {
|
| - Block *Temp = SmallEntry;
|
| - SmallEntry = LargeEntry;
|
| - LargeEntry = Temp; // Note: we did not flip the Sizes too, they are now invalid. TODO: use the smaller size as a limit?
|
| - }
|
| - // Check if dead end
|
| - bool DeadEnd = true;
|
| - BlockSet &SmallGroup = IndependentGroups[SmallEntry];
|
| - for (BlockSet::iterator iter = SmallGroup.begin(); iter != SmallGroup.end(); iter++) {
|
| - Block *Curr = *iter;
|
| - for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter != Curr->BranchesOut.end(); iter++) {
|
| - Block *Target = iter->first;
|
| - if (!contains(SmallGroup, Target)) {
|
| - DeadEnd = false;
|
| - break;
|
| - }
|
| - }
|
| - if (!DeadEnd) break;
|
| - }
|
| - if (DeadEnd) {
|
| - PrintDebug("Removing nesting by not handling large group because small group is dead end\n", 0);
|
| - IndependentGroups.erase(LargeEntry);
|
| - }
|
| - }
|
| - }
|
| -
|
| - PrintDebug("Handleable independent groups: %d\n", IndependentGroups.size());
|
| -
|
| - if (IndependentGroups.size() > 0) {
|
| - // Some groups removable ==> Multiple
|
| - Make(MakeMultiple(Blocks, *Entries, IndependentGroups, Prev, *NextEntries));
|
| - }
|
| - }
|
| - // No independent groups, must be loopable ==> Loop
|
| - Make(MakeLoop(Blocks, *Entries, *NextEntries));
|
| - }
|
| - }
|
| - };
|
| -
|
| - // Main
|
| -
|
| - BlockSet AllBlocks;
|
| - for (BlockSet::iterator iter = Pre.Live.begin(); iter != Pre.Live.end(); iter++) {
|
| - Block *Curr = *iter;
|
| - AllBlocks.insert(Curr);
|
| -#if DEBUG
|
| - PrintDebug("Adding block %d (%s)\n", Curr->Id, Curr->Code);
|
| -#endif
|
| - }
|
| -
|
| - BlockSet Entries;
|
| - Entries.insert(Entry);
|
| - Root = Analyzer(this).Process(AllBlocks, Entries, NULL);
|
| - assert(Root);
|
| -
|
| - // Post optimizations
|
| -
|
| - struct PostOptimizer {
|
| - Relooper *Parent;
|
| - void *Closure;
|
| -
|
| - PostOptimizer(Relooper *ParentInit) : Parent(ParentInit), Closure(NULL) {}
|
| -
|
| - #define RECURSE_Multiple(shape, func) \
|
| - for (IdShapeMap::iterator iter = shape->InnerMap.begin(); iter != shape->InnerMap.end(); iter++) { \
|
| - func(iter->second); \
|
| - }
|
| - #define RECURSE_Loop(shape, func) \
|
| - func(shape->Inner);
|
| - #define RECURSE(shape, func) RECURSE_##shape(shape, func);
|
| -
|
| - #define SHAPE_SWITCH(var, simple, multiple, loop) \
|
| - if (SimpleShape *Simple = Shape::IsSimple(var)) { \
|
| - (void)Simple; \
|
| - simple; \
|
| - } else if (MultipleShape *Multiple = Shape::IsMultiple(var)) { \
|
| - (void)Multiple; \
|
| - multiple; \
|
| - } else if (LoopShape *Loop = Shape::IsLoop(var)) { \
|
| - (void)Loop; \
|
| - loop; \
|
| - }
|
| -
|
| - // Find the blocks that natural control flow can get us directly to, or through a multiple that we ignore
|
| - void FollowNaturalFlow(Shape *S, BlockSet &Out) {
|
| - SHAPE_SWITCH(S, {
|
| - Out.insert(Simple->Inner);
|
| - }, {
|
| - for (IdShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
|
| - FollowNaturalFlow(iter->second, Out);
|
| - }
|
| - FollowNaturalFlow(Multiple->Next, Out);
|
| - }, {
|
| - FollowNaturalFlow(Loop->Inner, Out);
|
| - });
|
| - }
|
| -
|
| - void FindNaturals(Shape *Root, Shape *Otherwise=NULL) {
|
| - if (Root->Next) {
|
| - Root->Natural = Root->Next;
|
| - FindNaturals(Root->Next, Otherwise);
|
| - } else {
|
| - Root->Natural = Otherwise;
|
| - }
|
| -
|
| - SHAPE_SWITCH(Root, {
|
| - }, {
|
| - for (IdShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
|
| - FindNaturals(iter->second, Root->Natural);
|
| - }
|
| - }, {
|
| - FindNaturals(Loop->Inner, Loop->Inner);
|
| - });
|
| - }
|
| -
|
| - // Remove unneeded breaks and continues.
|
| - // A flow operation is trivially unneeded if the shape we naturally get to by normal code
|
| - // execution is the same as the flow forces us to.
|
| - void RemoveUnneededFlows(Shape *Root, Shape *Natural=NULL, LoopShape *LastLoop=NULL, unsigned Depth=0) {
|
| - BlockSet NaturalBlocks;
|
| - FollowNaturalFlow(Natural, NaturalBlocks);
|
| - Shape *Next = Root;
|
| - while (Next) {
|
| - Root = Next;
|
| - Next = NULL;
|
| - SHAPE_SWITCH(Root, {
|
| - if (Simple->Inner->BranchVar) LastLoop = NULL; // a switch clears out the loop (TODO: only for breaks, not continue)
|
| -
|
| - if (Simple->Next) {
|
| - if (!Simple->Inner->BranchVar && Simple->Inner->ProcessedBranchesOut.size() == 2 && Depth < 20) {
|
| - // If there is a next block, we already know at Simple creation time to make direct branches,
|
| - // and we can do nothing more in general. But, we try to optimize the case of a break and
|
| - // a direct: This would normally be if (break?) { break; } .. but if we
|
| - // make sure to nest the else, we can save the break, if (!break?) { .. } . This is also
|
| - // better because the more canonical nested form is easier to further optimize later. The
|
| - // downside is more nesting, which adds to size in builds with whitespace.
|
| - // Note that we avoid switches, as it complicates control flow and is not relevant
|
| - // for the common case we optimize here.
|
| - bool Found = false;
|
| - bool Abort = false;
|
| - for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) {
|
| - Block *Target = iter->first;
|
| - Branch *Details = iter->second;
|
| - if (Details->Type == Branch::Break) {
|
| - Found = true;
|
| - if (!contains(NaturalBlocks, Target)) Abort = true;
|
| - } else if (Details->Type != Branch::Direct) {
|
| - Abort = true;
|
| - }
|
| - }
|
| - if (Found && !Abort) {
|
| - for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) {
|
| - Branch *Details = iter->second;
|
| - if (Details->Type == Branch::Break) {
|
| - Details->Type = Branch::Direct;
|
| - if (MultipleShape *Multiple = Shape::IsMultiple(Details->Ancestor)) {
|
| - Multiple->Breaks--;
|
| - }
|
| - } else {
|
| - assert(Details->Type == Branch::Direct);
|
| - Details->Type = Branch::Nested;
|
| - }
|
| - }
|
| - }
|
| - Depth++; // this optimization increases depth, for us and all our next chain (i.e., until this call returns)
|
| - }
|
| - Next = Simple->Next;
|
| - } else {
|
| - // If there is no next then Natural is where we will
|
| - // go to by doing nothing, so we can potentially optimize some branches to direct.
|
| - for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) {
|
| - Block *Target = iter->first;
|
| - Branch *Details = iter->second;
|
| - if (Details->Type != Branch::Direct && contains(NaturalBlocks, Target)) { // note: cannot handle split blocks
|
| - Details->Type = Branch::Direct;
|
| - if (MultipleShape *Multiple = Shape::IsMultiple(Details->Ancestor)) {
|
| - Multiple->Breaks--;
|
| - }
|
| - } else if (Details->Type == Branch::Break && LastLoop && LastLoop->Natural == Details->Ancestor->Natural) {
|
| - // it is important to simplify breaks, as simpler breaks enable other optimizations
|
| - Details->Labeled = false;
|
| - if (MultipleShape *Multiple = Shape::IsMultiple(Details->Ancestor)) {
|
| - Multiple->Breaks--;
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }, {
|
| - for (IdShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
|
| - RemoveUnneededFlows(iter->second, Multiple->Next, Multiple->Breaks ? NULL : LastLoop, Depth+1);
|
| - }
|
| - Next = Multiple->Next;
|
| - }, {
|
| - RemoveUnneededFlows(Loop->Inner, Loop->Inner, Loop, Depth+1);
|
| - Next = Loop->Next;
|
| - });
|
| - }
|
| - }
|
| -
|
| - // After we know which loops exist, we can calculate which need to be labeled
|
| - void FindLabeledLoops(Shape *Root) {
|
| - bool First = Closure == NULL;
|
| - if (First) {
|
| - Closure = (void*)(new std::stack<Shape*>);
|
| - }
|
| - std::stack<Shape*> &LoopStack = *((std::stack<Shape*>*)Closure);
|
| -
|
| - Shape *Next = Root;
|
| - while (Next) {
|
| - Root = Next;
|
| - Next = NULL;
|
| -
|
| - SHAPE_SWITCH(Root, {
|
| - MultipleShape *Fused = Shape::IsMultiple(Root->Next);
|
| - // If we are fusing a Multiple with a loop into this Simple, then visit it now
|
| - if (Fused && Fused->Breaks) {
|
| - LoopStack.push(Fused);
|
| - }
|
| - if (Simple->Inner->BranchVar) {
|
| - LoopStack.push(NULL); // a switch means breaks are now useless, push a dummy
|
| - }
|
| - if (Fused) {
|
| - if (Fused->UseSwitch) {
|
| - LoopStack.push(NULL); // a switch means breaks are now useless, push a dummy
|
| - }
|
| - RECURSE_Multiple(Fused, FindLabeledLoops);
|
| - }
|
| - for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) {
|
| - Branch *Details = iter->second;
|
| - if (Details->Type == Branch::Break || Details->Type == Branch::Continue) {
|
| - assert(LoopStack.size() > 0);
|
| - if (Details->Ancestor != LoopStack.top() && Details->Labeled) {
|
| - LabeledShape *Labeled = Shape::IsLabeled(Details->Ancestor);
|
| - Labeled->Labeled = true;
|
| - } else {
|
| - Details->Labeled = false;
|
| - }
|
| - }
|
| - }
|
| - if (Fused && Fused->UseSwitch) {
|
| - LoopStack.pop();
|
| - }
|
| - if (Simple->Inner->BranchVar) {
|
| - LoopStack.pop();
|
| - }
|
| - if (Fused && Fused->Breaks) {
|
| - LoopStack.pop();
|
| - }
|
| - if (Fused) {
|
| - Next = Fused->Next;
|
| - } else {
|
| - Next = Root->Next;
|
| - }
|
| - }, {
|
| - if (Multiple->Breaks) {
|
| - LoopStack.push(Multiple);
|
| - }
|
| - RECURSE(Multiple, FindLabeledLoops);
|
| - if (Multiple->Breaks) {
|
| - LoopStack.pop();
|
| - }
|
| - Next = Root->Next;
|
| - }, {
|
| - LoopStack.push(Loop);
|
| - RECURSE(Loop, FindLabeledLoops);
|
| - LoopStack.pop();
|
| - Next = Root->Next;
|
| - });
|
| - }
|
| -
|
| - if (First) {
|
| - delete (std::stack<Shape*>*)Closure;
|
| - }
|
| - }
|
| -
|
| - void Process(Shape *Root) {
|
| - FindNaturals(Root);
|
| - RemoveUnneededFlows(Root);
|
| - FindLabeledLoops(Root);
|
| - }
|
| - };
|
| -
|
| - PrintDebug("=== Optimizing shapes ===\n", 0);
|
| -
|
| - PostOptimizer(this).Process(Root);
|
| -}
|
| -
|
| -void Relooper::Render() {
|
| - OutputBuffer = OutputBufferRoot;
|
| - assert(Root);
|
| - Root->Render(false);
|
| -}
|
| -
|
| -void Relooper::SetOutputBuffer(char *Buffer, int Size) {
|
| - OutputBufferRoot = OutputBuffer = Buffer;
|
| - OutputBufferSize = Size;
|
| - OutputBufferOwned = false;
|
| -}
|
| -
|
| -void Relooper::MakeOutputBuffer(int Size) {
|
| - if (OutputBufferRoot && OutputBufferSize >= Size && OutputBufferOwned) return;
|
| - OutputBufferRoot = OutputBuffer = (char*)malloc(Size);
|
| - OutputBufferSize = Size;
|
| - OutputBufferOwned = true;
|
| -}
|
| -
|
| -char *Relooper::GetOutputBuffer() {
|
| - return OutputBufferRoot;
|
| -}
|
| -
|
| -void Relooper::SetAsmJSMode(int On) {
|
| - AsmJS = On;
|
| -}
|
| -
|
| -#if DEBUG
|
| -// Debugging
|
| -
|
| -void Debugging::Dump(BlockSet &Blocks, const char *prefix) {
|
| - if (prefix) printf("%s ", prefix);
|
| - for (BlockSet::iterator iter = Blocks.begin(); iter != Blocks.end(); iter++) {
|
| - Block *Curr = *iter;
|
| - printf("%d:\n", Curr->Id);
|
| - for (BlockBranchMap::iterator iter2 = Curr->BranchesOut.begin(); iter2 != Curr->BranchesOut.end(); iter2++) {
|
| - Block *Other = iter2->first;
|
| - printf(" -> %d\n", Other->Id);
|
| - assert(contains(Other->BranchesIn, Curr));
|
| - }
|
| - }
|
| -}
|
| -
|
| -void Debugging::Dump(Shape *S, const char *prefix) {
|
| - if (prefix) printf("%s ", prefix);
|
| - if (!S) {
|
| - printf(" (null)\n");
|
| - return;
|
| - }
|
| - printf(" %d ", S->Id);
|
| - SHAPE_SWITCH(S, {
|
| - printf("<< Simple with block %d\n", Simple->Inner->Id);
|
| - }, {
|
| - printf("<< Multiple\n");
|
| - for (IdShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
|
| - printf(" with entry %d\n", iter->first);
|
| - }
|
| - }, {
|
| - printf("<< Loop\n");
|
| - });
|
| -}
|
| -
|
| -static void PrintDebug(const char *Format, ...) {
|
| - printf("// ");
|
| - va_list Args;
|
| - va_start(Args, Format);
|
| - vprintf(Format, Args);
|
| - va_end(Args);
|
| -}
|
| -#endif
|
| -
|
| -// C API - useful for binding to other languages
|
| -
|
| -typedef std::map<void*, int> VoidIntMap;
|
| -VoidIntMap __blockDebugMap__; // maps block pointers in currently running code to block ids, for generated debug output
|
| -
|
| -extern "C" {
|
| -
|
| -RELOOPERDLL_API void rl_set_output_buffer(char *buffer, int size) {
|
| -#if DEBUG
|
| - printf("#include \"Relooper.h\"\n");
|
| - printf("int main() {\n");
|
| - printf(" char buffer[100000];\n");
|
| - printf(" rl_set_output_buffer(buffer);\n");
|
| -#endif
|
| - Relooper::SetOutputBuffer(buffer, size);
|
| -}
|
| -
|
| -RELOOPERDLL_API void rl_make_output_buffer(int size) {
|
| - Relooper::SetOutputBuffer((char*)malloc(size), size);
|
| -}
|
| -
|
| -RELOOPERDLL_API void rl_set_asm_js_mode(int on) {
|
| - Relooper::SetAsmJSMode(on);
|
| -}
|
| -
|
| -RELOOPERDLL_API void *rl_new_block(const char *text, const char *branch_var) {
|
| - Block *ret = new Block(text, branch_var);
|
| -#if DEBUG
|
| - printf(" void *b%d = rl_new_block(\"// code %d\");\n", ret->Id, ret->Id);
|
| - __blockDebugMap__[ret] = ret->Id;
|
| - printf(" block_map[%d] = b%d;\n", ret->Id, ret->Id);
|
| -#endif
|
| - return ret;
|
| -}
|
| -
|
| -RELOOPERDLL_API void rl_delete_block(void *block) {
|
| -#if DEBUG
|
| - printf(" rl_delete_block(block_map[%d]);\n", ((Block*)block)->Id);
|
| -#endif
|
| - delete (Block*)block;
|
| -}
|
| -
|
| -RELOOPERDLL_API void rl_block_add_branch_to(void *from, void *to, const char *condition, const char *code) {
|
| -#if DEBUG
|
| - printf(" rl_block_add_branch_to(block_map[%d], block_map[%d], %s%s%s, %s%s%s);\n", ((Block*)from)->Id, ((Block*)to)->Id, condition ? "\"" : "", condition ? condition : "NULL", condition ? "\"" : "", code ? "\"" : "", code ? code : "NULL", code ? "\"" : "");
|
| -#endif
|
| - ((Block*)from)->AddBranchTo((Block*)to, condition, code);
|
| -}
|
| -
|
| -RELOOPERDLL_API void *rl_new_relooper() {
|
| -#if DEBUG
|
| - printf(" void *block_map[10000];\n");
|
| - printf(" void *rl = rl_new_relooper();\n");
|
| -#endif
|
| - return new Relooper;
|
| -}
|
| -
|
| -RELOOPERDLL_API void rl_delete_relooper(void *relooper) {
|
| - delete (Relooper*)relooper;
|
| -}
|
| -
|
| -RELOOPERDLL_API void rl_relooper_add_block(void *relooper, void *block) {
|
| -#if DEBUG
|
| - printf(" rl_relooper_add_block(rl, block_map[%d]);\n", ((Block*)block)->Id);
|
| -#endif
|
| - ((Relooper*)relooper)->AddBlock((Block*)block);
|
| -}
|
| -
|
| -RELOOPERDLL_API void rl_relooper_calculate(void *relooper, void *entry) {
|
| -#if DEBUG
|
| - printf(" rl_relooper_calculate(rl, block_map[%d]);\n", ((Block*)entry)->Id);
|
| - printf(" rl_relooper_render(rl);\n");
|
| - printf(" rl_delete_relooper(rl);\n");
|
| - printf(" puts(buffer);\n");
|
| - printf(" return 0;\n");
|
| - printf("}\n");
|
| -#endif
|
| - ((Relooper*)relooper)->Calculate((Block*)entry);
|
| -}
|
| -
|
| -RELOOPERDLL_API void rl_relooper_render(void *relooper) {
|
| - ((Relooper*)relooper)->Render();
|
| -}
|
| -
|
| -}
|
|
|