aboutsummaryrefslogtreecommitdiff
path: root/src/relooper
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2012-11-11 10:04:33 -0800
committerAlon Zakai <alonzakai@gmail.com>2012-11-11 10:04:33 -0800
commit3634b45c63e420910c77a631352ab257b8a6039f (patch)
treee43051c8b9b6339cddb57a7d8dd6929fbeedb56a /src/relooper
parent795bcc6e76e59824d725acea1e9c2e1c553a6658 (diff)
add relooper sources
Diffstat (limited to 'src/relooper')
-rw-r--r--src/relooper/README.markdown14
-rw-r--r--src/relooper/Relooper.cpp1038
-rw-r--r--src/relooper/Relooper.h246
-rwxr-xr-xsrc/relooper/doit.sh68
-rw-r--r--src/relooper/emscripten/Makefile18
-rw-r--r--src/relooper/emscripten/glue.js54
-rw-r--r--src/relooper/emscripten/test.js44
-rw-r--r--src/relooper/fuzzer.py116
-rw-r--r--src/relooper/ministring.h35
-rw-r--r--src/relooper/paper.pdfbin0 -> 220318 bytes
-rw-r--r--src/relooper/test.cpp195
-rw-r--r--src/relooper/test.txt99
-rw-r--r--src/relooper/test2.c44
-rw-r--r--src/relooper/test2.txt12
-rw-r--r--src/relooper/test3.c42
-rw-r--r--src/relooper/test3.txt27
-rw-r--r--src/relooper/test4.cpp40
-rw-r--r--src/relooper/test4.txt24
-rw-r--r--src/relooper/test5.cpp40
-rw-r--r--src/relooper/test5.txt32
-rw-r--r--src/relooper/test6.cpp31
-rw-r--r--src/relooper/test6.txt12
-rw-r--r--src/relooper/test_dead.cpp28
-rw-r--r--src/relooper/test_dead.txt9
-rw-r--r--src/relooper/test_debug.cpp30
-rw-r--r--src/relooper/test_debug.txt96
-rw-r--r--src/relooper/test_fuzz1.cpp52
-rw-r--r--src/relooper/test_fuzz1.txt44
-rw-r--r--src/relooper/test_fuzz2.cpp34
-rw-r--r--src/relooper/test_fuzz2.txt14
-rw-r--r--src/relooper/test_fuzz3.cpp36
-rw-r--r--src/relooper/test_fuzz3.txt9
-rw-r--r--src/relooper/test_fuzz4.cpp38
-rw-r--r--src/relooper/test_fuzz4.txt20
-rw-r--r--src/relooper/test_fuzz5.cpp57
-rw-r--r--src/relooper/test_fuzz5.txt56
-rw-r--r--src/relooper/test_fuzz6.cpp322
-rw-r--r--src/relooper/test_fuzz6.txt116
-rw-r--r--src/relooper/test_inf.cpp813
-rw-r--r--src/relooper/test_inf.txt392
-rwxr-xr-xsrc/relooper/testit.sh60
-rwxr-xr-xsrc/relooper/updateit.sh16
42 files changed, 4473 insertions, 0 deletions
diff --git a/src/relooper/README.markdown b/src/relooper/README.markdown
new file mode 100644
index 00000000..9b0187b3
--- /dev/null
+++ b/src/relooper/README.markdown
@@ -0,0 +1,14 @@
+
+Relooper
+========
+
+This is an optimized C++ implemention of the Relooper algorithm originally
+developed as part of Emscripten. This implementation includes optimizations
+added since the original academic paper [1] - see paper.pdf - was published
+about it, and is written in an LLVM-friendly way with the goal of inclusion
+in upstream LLVM.
+
+License: MIT&LLVM
+
+[1] Alon Zakai. 2011. Emscripten: an LLVM-to-JavaScript compiler. In Proceedings of the ACM international conference companion on Object oriented programming systems languages and applications companion (SPLASH '11). ACM, New York, NY, USA, 301-312. DOI=10.1145/2048147.2048224 http://doi.acm.org/10.1145/2048147.2048224
+
diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp
new file mode 100644
index 00000000..f16055c0
--- /dev/null
+++ b/src/relooper/Relooper.cpp
@@ -0,0 +1,1038 @@
+
+#include "Relooper.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <list>
+#include <stack>
+
+#include "ministring.h"
+
+// TODO: move all set to unorderedset
+
+#if DEBUG
+static void PrintDebug(const char *Format, ...);
+#define DebugDump(x, ...) Debugging::Dump(x, __VA_ARGS__)
+#else
+#define PrintDebug(x, ...)
+#define DebugDump(x, ...)
+#endif
+
+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;
+
+void PrintIndented(const char *Format, ...) {
+ assert(OutputBuffer);
+ assert(OutputBuffer + Indenter::CurrIndent*2 - OutputBufferRoot < OutputBufferSize);
+ for (int i = 0; i < Indenter::CurrIndent*2; i++, OutputBuffer++) *OutputBuffer = ' ';
+ va_list Args;
+ va_start(Args, Format);
+ int left = OutputBufferSize - (OutputBuffer - OutputBufferRoot);
+ int written = vsnprintf(OutputBuffer, left, Format, Args);
+ assert(written < left);
+ OutputBuffer += written;
+ va_end(Args);
+}
+
+void PutIndented(const char *String) {
+ assert(OutputBuffer);
+ assert(OutputBuffer + Indenter::CurrIndent*2 - OutputBufferRoot < OutputBufferSize);
+ for (int i = 0; i < Indenter::CurrIndent*2; i++, OutputBuffer++) *OutputBuffer = ' ';
+ int left = OutputBufferSize - (OutputBuffer - OutputBufferRoot);
+ int needed = strlen(String)+1;
+ assert(needed < left);
+ strcpy(OutputBuffer, String);
+ OutputBuffer += strlen(String);
+ *OutputBuffer++ = '\n';
+ *OutputBuffer = 0;
+}
+
+// Indenter
+
+#if EMSCRIPTEN
+int Indenter::CurrIndent = 1;
+#else
+int Indenter::CurrIndent = 0;
+#endif
+
+// Branch
+
+Branch::Branch(const char *ConditionInit, const char *CodeInit) : Ancestor(NULL), Labeled(false) {
+ 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 != Direct) {
+ if (Labeled) {
+ PrintIndented("%s L%d;\n", Type == Break ? "break" : "continue", Ancestor->Id);
+ } else {
+ PrintIndented("%s;\n", Type == Break ? "break" : "continue");
+ }
+ }
+ }
+}
+
+// Block
+
+int Block::IdCounter = 1; // 0 is reserved for clearings
+
+Block::Block(const char *CodeInit) : Parent(NULL), Id(Block::IdCounter++), DefaultTarget(NULL), IsCheckedMultipleEntry(false) {
+ Code = strdup(CodeInit);
+}
+
+Block::~Block() {
+ if (Code) free((void*)Code);
+ for (BlockBranchMap::iterator iter = ProcessedBranchesIn.begin(); iter != ProcessedBranchesIn.end(); iter++) {
+ delete iter->second;
+ }
+ 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(BranchesOut.find(Target) == BranchesOut.end()); // 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
+
+ if (ProcessedBranchesOut.size() == 1 && ProcessedBranchesOut.begin()->second->Type == Branch::Direct) {
+ SetLabel = false;
+ }
+
+ // 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");
+ Parent->Next = Parent->Next->Next;
+ 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;
+ }
+ }
+
+ // We must do this here, because blocks can be split and even comparing their Ids is not enough. We must check the conditions.
+ 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); // Must be a default
+
+ ministring RemainingConditions;
+ bool First = true;
+ 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;
+ bool HasFusedContent = Fused && Fused->InnerMap.find(Target) != Fused->InnerMap.end();
+ 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 (HasContent) {
+ PrintIndented("%sif (%s) {\n", First ? "" : "} else ", Details->Condition);
+ First = false;
+ } else {
+ if (RemainingConditions.size() > 0) RemainingConditions += " && ";
+ RemainingConditions += "!(";
+ RemainingConditions += Details->Condition;
+ RemainingConditions += ")";
+ }
+ } 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)->second->Render(InLoop);
+ }
+ if (!First) Indenter::Unindent();
+ if (iter == ProcessedBranchesOut.end()) break;
+ }
+ if (!First) PrintIndented("}\n");
+
+ if (Fused) {
+ Fused->RenderLoopPostfix();
+ }
+}
+
+// Shape
+
+int Shape::IdCounter = 0;
+
+// MultipleShape
+
+void MultipleShape::RenderLoopPrefix() {
+ if (NeedLoop) {
+ if (Labeled) {
+ PrintIndented("L%d: do {\n", Id);
+ } else {
+ PrintIndented("do {\n");
+ }
+ Indenter::Indent();
+ }
+}
+
+void MultipleShape::RenderLoopPostfix() {
+ if (NeedLoop) {
+ Indenter::Unindent();
+ PrintIndented("} while(0);\n");
+ }
+}
+
+void MultipleShape::Render(bool InLoop) {
+ RenderLoopPrefix();
+ bool First = true;
+ for (BlockShapeMap::iterator iter = InnerMap.begin(); iter != InnerMap.end(); iter++) {
+ PrintIndented("%sif (label == %d) {\n", First ? "" : "else ", iter->first->Id);
+ First = false;
+ Indenter::Indent();
+ iter->second->Render(InLoop);
+ 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("while(1) {\n");
+ Indenter::Indent();
+ PrintIndented("switch(label) {\n");
+ Indenter::Indent();
+ for (int i = 0; i < Blocks.size(); i++) {
+ Block *Curr = Blocks[i];
+ 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) {
+}
+
+Relooper::~Relooper() {
+ for (int i = 0; i < Blocks.size(); i++) delete Blocks[i];
+ for (int i = 0; i < Shapes.size(); i++) delete Shapes[i];
+}
+
+void Relooper::AddBlock(Block *New) {
+ Blocks.push_back(New);
+}
+
+struct RelooperRecursor {
+ Relooper *Parent;
+ RelooperRecursor(Relooper *ParentInit) : Parent(ParentInit) {}
+};
+
+void Relooper::Calculate(Block *Entry) {
+ // Scan and optimize the input
+ struct PreOptimizer : public RelooperRecursor {
+ PreOptimizer(Relooper *Parent) : RelooperRecursor(Parent) {}
+ BlockSet Live;
+
+ void FindLive(Block *Curr) {
+ if (Live.find(Curr) != Live.end()) return;
+ Live.insert(Curr);
+ for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter != Curr->BranchesOut.end(); iter++) {
+ FindLive(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() {
+ int TotalCodeSize = 0;
+ for (BlockSet::iterator iter = Live.begin(); iter != Live.end(); iter++) {
+ Block *Curr = *iter;
+ TotalCodeSize += strlen(Curr->Code);
+ }
+
+ for (BlockSet::iterator iter = Live.begin(); iter != Live.end(); iter++) {
+ Block *Original = *iter;
+ if (Original->BranchesIn.size() <= 1 || Original->BranchesOut.size() > 0) continue;
+ 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)
+ for (BlockBranchMap::iterator iter = Original->BranchesIn.begin(); iter != Original->BranchesIn.end(); iter++) {
+ Block *Prior = iter->first;
+ Block *Split = new Block(Original->Code);
+ Split->BranchesIn[Prior] = new Branch(NULL);
+ Prior->BranchesOut[Split] = new Branch(Prior->BranchesOut[Original]->Condition, Prior->BranchesOut[Original]->Code);
+ Prior->BranchesOut.erase(Original);
+ Parent->AddBlock(Split);
+ Live.insert(Split);
+ }
+ }
+ }
+ };
+ PreOptimizer Pre(this);
+ Pre.FindLive(Entry);
+
+ // Add incoming branches from live blocks, ignoring dead code
+ for (int i = 0; i < Blocks.size(); i++) {
+ Block *Curr = Blocks[i];
+ if (Pre.Live.find(Curr) == Pre.Live.end()) continue;
+ for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter != Curr->BranchesOut.end(); iter++) {
+ iter->first->BranchesIn[Curr] = new Branch(NULL);
+ }
+ }
+
+ 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) {
+ 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 || LimitTo->find(iter->first) != LimitTo->end()) {
+ 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 (BlockBranchMap::iterator iter = Target->BranchesIn.begin(); iter != Target->BranchesIn.end();) {
+ Block *Prior = iter->first;
+ if (From.find(Prior) == From.end()) {
+ iter++;
+ continue;
+ }
+ Branch *TargetIn = iter->second;
+ Branch *PriorOut = Prior->BranchesOut[Target];
+ PriorOut->Ancestor = Ancestor; // Do we need this info
+ PriorOut->Type = Type; // on TargetIn too?
+ if (MultipleShape *Multiple = Shape::IsMultiple(Ancestor)) {
+ Multiple->NeedLoop++; // We are breaking out of this Multiple, so need a loop
+ }
+ iter++; // carefully increment iter before erasing
+ Target->BranchesIn.erase(Prior);
+ Target->ProcessedBranchesIn[Prior] = TargetIn;
+ 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 *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 (InnerBlocks.find(Curr) == InnerBlocks.end()) {
+ // 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 (BlockBranchMap::iterator iter = Curr->BranchesIn.begin(); iter != Curr->BranchesIn.end(); iter++) {
+ Queue.insert(iter->first);
+ }
+ }
+ }
+ 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 (InnerBlocks.find(Possible) == InnerBlocks.end() &&
+ NextEntries.find(Possible) == NextEntries.find(Possible)) {
+ NextEntries.insert(Possible);
+ }
+ }
+ }
+
+ PrintDebug("creating loop block:\n");
+ DebugDump(InnerBlocks, " inner blocks:");
+ DebugDump(Entries, " inner entries:");
+ DebugDump(Blocks, " outer blocks:");
+ DebugDump(NextEntries, " outer entries:");
+
+ // TODO: Optionally hoist additional blocks into the loop
+
+ 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.
+ void FindIndependentGroups(BlockSet &Blocks, BlockSet &Entries, BlockBlockSetMap& IndependentGroups) {
+ typedef std::map<Block*, Block*> BlockBlockMap;
+ typedef std::list<Block*> BlockList;
+
+ 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 (IndependentGroups.find(Owner) != IndependentGroups.end()) { // 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 (BlockBranchMap::iterator iter = Child->BranchesIn.begin(); iter != Child->BranchesIn.end(); iter++) {
+ Block *Parent = iter->first;
+ 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;
+