summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cfg/Relooper.cpp125
-rw-r--r--src/cfg/Relooper.h50
2 files changed, 121 insertions, 54 deletions
diff --git a/src/cfg/Relooper.cpp b/src/cfg/Relooper.cpp
index 6488b3d06..cde61df0b 100644
--- a/src/cfg/Relooper.cpp
+++ b/src/cfg/Relooper.cpp
@@ -18,6 +18,7 @@
#include <string.h>
#include <stdlib.h>
+
#include <list>
#include <stack>
#include <string>
@@ -38,6 +39,59 @@ static void PrintDebug(const char *Format, ...);
#define DebugDump(x, ...)
#endif
+// Rendering utilities
+
+static wasm::Expression* HandleFollowupMultiples(wasm::Expression* Ret, Shape* Parent, RelooperBuilder& Builder, bool InLoop) {
+ if (!Parent->Next) return Ret;
+
+ auto* Curr = Ret->dynCast<wasm::Block>();
+ if (!Curr || Curr->name.is()) {
+ Curr = Builder.makeBlock(Ret);
+ }
+ // for each multiple after us, we create a block target for breaks to reach
+ while (Parent->Next) {
+ auto* Multiple = Shape::IsMultiple(Parent->Next);
+ if (!Multiple) break;
+ for (auto& iter : Multiple->InnerMap) {
+ int Id = iter.first;
+ Shape* Body = iter.second;
+ Curr->name = Builder.getBlockBreakName(Id);
+ auto* Outer = Builder.makeBlock(Curr);
+ Outer->list.push_back(Body->Render(Builder, InLoop));
+ Outer->finalize(); // TODO: not really necessary
+ Curr = Outer;
+ }
+ Parent->Next = Parent->Next->Next;
+ }
+ // after the multiples is a simple or a loop, in both cases we must hit an entry
+ // block, and so this is the last one we need to take into account now (this
+ // is why we require that loops hit an entry).
+ if (Parent->Next) {
+ auto* Simple = Shape::IsSimple(Parent->Next);
+ if (Simple) {
+ // breaking on the next block's id takes us out, where we
+ // will reach its rendering
+ Curr->name = Builder.getBlockBreakName(Simple->Inner->Id);
+ } else {
+ // add one break target per entry for the loop
+ auto* Loop = Shape::IsLoop(Parent->Next);
+ assert(Loop);
+ assert(Loop->Entries.size() > 0);
+ if (Loop->Entries.size() == 1) {
+ Curr->name = Builder.getBlockBreakName((*Loop->Entries.begin())->Id);
+ } else {
+ for (auto* Entry : Loop->Entries) {
+ Curr->name = Builder.getBlockBreakName(Entry->Id);
+ auto* Outer = Builder.makeBlock(Curr);
+ Outer->finalize(); // TODO: not really necessary
+ Curr = Outer;
+ }
+ }
+ }
+ }
+ return Curr;
+}
+
// Branch
Branch::Branch(wasm::Expression* ConditionInit, wasm::Expression* CodeInit) : Ancestor(nullptr), Condition(ConditionInit), Code(CodeInit) {}
@@ -53,12 +107,11 @@ wasm::Expression* Branch::Render(RelooperBuilder& Builder, Block *Target, bool S
auto* Ret = Builder.makeBlock();
if (Code) Ret->list.push_back(Code);
if (SetLabel) Ret->list.push_back(Builder.makeSetLabel(Target->Id));
- if (Ancestor) {
- if (Type == Break) {
- Ret->list.push_back(Builder.makeShapeBreak(Ancestor->Id));
- } else if (Type == Continue) {
- Ret->list.push_back(Builder.makeShapeContinue(Ancestor->Id));
- }
+ if (Type == Break) {
+ Ret->list.push_back(Builder.makeBlockBreak(Target->Id));
+ } else if (Type == Continue) {
+ assert(Ancestor);
+ Ret->list.push_back(Builder.makeShapeContinue(Ancestor->Id));
}
Ret->finalize();
return Ret;
@@ -128,7 +181,6 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) {
if (Fused) {
PrintDebug("Fusing Multiple to Simple\n", 0);
Parent->Next = Parent->Next->Next;
-
// 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 a switch, then we can have multiple branches to the same target
@@ -171,22 +223,21 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) {
}
bool SetCurrLabel = SetLabel && Target->IsCheckedMultipleEntry;
bool HasFusedContent = Fused && contains(Fused->InnerMap, Target->Id);
+ if (HasFusedContent) {
+ assert(Details->Type == Branch::Break);
+ Details->Type = Branch::Direct;
+ }
wasm::Expression* CurrContent = nullptr;
+ bool IsDefault = iter == ProcessedBranchesOut.end();
if (SetCurrLabel || Details->Type != Branch::Direct || HasFusedContent || Details->Code) {
CurrContent = Details->Render(Builder, Target, SetCurrLabel);
if (HasFusedContent) {
CurrContent = Builder.blockify(CurrContent, Fused->InnerMap.find(Target->Id)->second->Render(Builder, InLoop));
- } else if (Details->Type == Branch::Nested) {
- // Nest the parent content here, and remove it from showing up afterwards as Next
- assert(Parent->Next);
- CurrContent = Builder.blockify(CurrContent, Parent->Next->Render(Builder, InLoop));
- Parent->Next = nullptr;
}
}
- bool isDefault = iter == ProcessedBranchesOut.end();
// If there is nothing to show in this branch, omit the condition
if (CurrContent) {
- if (isDefault) {
+ if (IsDefault) {
wasm::Expression* Now;
if (RemainingConditions) {
Now = Builder.makeIf(RemainingConditions, CurrContent);
@@ -217,7 +268,7 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) {
RemainingConditions = Now;
}
}
- if (isDefault) break;
+ if (IsDefault) break;
}
} else {
// Emit a switch
@@ -240,16 +291,15 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) {
// generate the content for this block
bool SetCurrLabel = SetLabel && Target->IsCheckedMultipleEntry;
bool HasFusedContent = Fused && contains(Fused->InnerMap, Target->Id);
+ if (HasFusedContent) {
+ assert(Details->Type == Branch::Break);
+ Details->Type = Branch::Direct;
+ }
wasm::Expression* CurrContent = nullptr;
if (SetCurrLabel || Details->Type != Branch::Direct || HasFusedContent || Details->Code) {
CurrContent = Details->Render(Builder, Target, SetCurrLabel);
if (HasFusedContent) {
CurrContent = Builder.blockify(CurrContent, Fused->InnerMap.find(Target->Id)->second->Render(Builder, InLoop));
- } else if (Details->Type == Branch::Nested) {
- // Nest the parent content here, and remove it from showing up afterwards as Next
- assert(Parent->Next);
- CurrContent = Builder.blockify(CurrContent, Parent->Next->Render(Builder, InLoop));
- Parent->Next = nullptr;
}
}
// generate a block to branch to, if we have content
@@ -285,15 +335,8 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) {
}
if (Root) {
- if (Fused) {
- // We are fusing a multiple into this simple
- auto* Block = Builder.makeBlock(Root);
- Block->name = Builder.getBreakName(Fused->Id);
- Root = Block;
- }
Ret->list.push_back(Root);
}
-
Ret->finalize();
return Ret;
@@ -303,6 +346,7 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) {
wasm::Expression* SimpleShape::Render(RelooperBuilder& Builder, bool InLoop) {
auto* Ret = Inner->Render(Builder, InLoop);
+ Ret = HandleFollowupMultiples(Ret, this, Builder, InLoop);
if (Next) {
Ret = Builder.makeSequence(Ret, Next->Render(Builder, InLoop));
}
@@ -328,8 +372,8 @@ wasm::Expression* MultipleShape::Render(RelooperBuilder& Builder, bool InLoop) {
CurrIf = Now;
}
}
- auto* Ret = Builder.makeBlock(FirstIf);
- Ret->name = Builder.getBreakName(Id);
+ wasm::Expression* Ret = Builder.makeBlock(FirstIf);
+ Ret = HandleFollowupMultiples(Ret, this, Builder, InLoop);
if (Next) {
Ret = Builder.makeSequence(Ret, Next->Render(Builder, InLoop));
}
@@ -339,7 +383,8 @@ wasm::Expression* MultipleShape::Render(RelooperBuilder& Builder, bool InLoop) {
// LoopShape
wasm::Expression* LoopShape::Render(RelooperBuilder& Builder, bool InLoop) {
- wasm::Expression* Ret = Builder.makeLoop(Builder.getBreakName(Id), Builder.getContinueName(Id), Inner->Render(Builder, true));
+ wasm::Expression* Ret = Builder.makeLoop(wasm::Name(), Builder.getShapeContinueName(Id), Inner->Render(Builder, true));
+ Ret = HandleFollowupMultiples(Ret, this, Builder, InLoop);
if (Next) {
Ret = Builder.makeSequence(Ret, Next->Render(Builder, InLoop));
}
@@ -455,7 +500,7 @@ void Relooper::Calculate(Block *Entry) {
BlockSet JustInner;
JustInner.insert(Inner);
for (BlockSet::iterator iter = NextEntries.begin(); iter != NextEntries.end(); iter++) {
- Solipsize(*iter, Branch::Direct, Simple, JustInner);
+ Solipsize(*iter, Branch::Break, Simple, JustInner);
}
}
return Simple;
@@ -570,6 +615,7 @@ void Relooper::Calculate(Block *Entry) {
// Finish up
Shape *Inner = Process(InnerBlocks, Entries);
Loop->Inner = Inner;
+ Loop->Entries = Entries;
return Loop;
}
@@ -790,7 +836,8 @@ void Relooper::Calculate(Block *Entry) {
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).
+ // a loop inside the multiple block, which is both the performant order to do it, and
+ // preserves the property that a loop will always reach an entry.
for (BlockBlockSetMap::iterator iter = IndependentGroups.begin(); iter != IndependentGroups.end();) {
Block *Entry = iter->first;
BlockSet &Group = iter->second;
@@ -853,8 +900,18 @@ void Relooper::Calculate(Block *Entry) {
if (IndependentGroups.size() > 0) {
// Some groups removable ==> Multiple
- // checked multiple if prev is not simple (in which case we would be fused)
- Make(MakeMultiple(Blocks, *Entries, IndependentGroups, *NextEntries, !(Shape::IsSimple(Prev))));
+ // This is a checked multiple if it has an entry that is an entry to this Process call, that is,
+ // if we can reach it from outside this set of blocks, then we must check the label variable
+ // to do so. Otherwise, if it is just internal blocks, those can always be jumped to forward,
+ // without using the label variable
+ bool Checked = false;
+ for (auto* Entry : *Entries) {
+ if (InitialEntries.count(Entry)) {
+ Checked = true;
+ break;
+ }
+ }
+ Make(MakeMultiple(Blocks, *Entries, IndependentGroups, *NextEntries, Checked));
}
}
// No independent groups, must be loopable ==> Loop
diff --git a/src/cfg/Relooper.h b/src/cfg/Relooper.h
index 8ac877d08..50f40fd88 100644
--- a/src/cfg/Relooper.h
+++ b/src/cfg/Relooper.h
@@ -53,17 +53,21 @@ public:
wasm::Binary* makeCheckLabel(wasm::Index value) {
return makeBinary(wasm::EqInt32, makeGetLabel(), makeConst(wasm::Literal(int32_t(value))));
}
- wasm::Break* makeShapeBreak(int id) {
- return wasm::Builder::makeBreak(getBreakName(id));
+
+ // breaks are on blocks, as they can be specific, we make one wasm block per basic block
+ wasm::Break* makeBlockBreak(int id) {
+ return wasm::Builder::makeBreak(getBlockBreakName(id));
}
+ // continues are on shapes, as there is one per loop, and if we have more than one
+ // going there, it is irreducible control flow anyhow
wasm::Break* makeShapeContinue(int id) {
- return wasm::Builder::makeBreak(getContinueName(id));
+ return wasm::Builder::makeBreak(getShapeContinueName(id));
}
- wasm::Name getBreakName(int id) {
- return wasm::Name(std::string("shape$") + std::to_string(id) + "$break");
+ wasm::Name getBlockBreakName(int id) {
+ return wasm::Name(std::string("block$") + std::to_string(id) + "$break");
}
- wasm::Name getContinueName(int id) {
+ wasm::Name getShapeContinueName(int id) {
return wasm::Name(std::string("shape$") + std::to_string(id) + "$continue");
}
};
@@ -76,9 +80,7 @@ struct Branch {
enum FlowType {
Direct = 0, // We will directly reach the right location through other means, no need for continue or break
Break = 1,
- Continue = 2,
- Nested = 3 // This code is directly reached, but we must be careful to ensure it is nested in an if - it is not reached
- // unconditionally, other code paths exist alongside it that we need to make sure do not intertwine
+ Continue = 2
};
Shape *Ancestor; // If not NULL, this shape is the relevant one for purposes of getting to the target block. We break or continue on it
Branch::FlowType Type; // If Ancestor is not NULL, this says whether to break or continue
@@ -144,12 +146,14 @@ struct InsertOrderedSet
InsertOrderedSet() {}
InsertOrderedSet(const InsertOrderedSet& other) {
+ *this = other;
+ }
+ InsertOrderedSet& operator=(const InsertOrderedSet& other) {
+ clear();
for (auto i : other.List) {
insert(i); // inserting manually creates proper iterators
}
- }
- InsertOrderedSet& operator=(const InsertOrderedSet& other) {
- abort(); // TODO, watch out for iterators
+ return *this;
}
};
@@ -241,15 +245,20 @@ struct Block {
// Represents a structured control flow shape, one of
//
-// Simple: No control flow at all, just instructions. If several
-// blocks, then
+// Simple: No control flow at all, just instructions in a single
+// basic block.
//
-// Multiple: A shape with more than one entry. If the next block to
-// be entered is among them, we run it and continue to
-// the next shape, otherwise we continue immediately to the
-// next shape.
+// Multiple: A shape with at least one entry. We may visit one of
+// the entries, or none, before continuing to the next
+// shape after this.
//
-// Loop: An infinite loop.
+// Loop: An infinite loop. We assume the property that a loop
+// will always visit one of its entries, and so for example
+// we cannot have a loop containing a multiple and nothing
+// else (since we might not visit any of the multiple's
+// blocks). Multiple entries are possible for the block,
+// however, which is necessary for irreducible control
+// flow, of course.
//
struct SimpleShape;
@@ -285,7 +294,6 @@ struct SimpleShape : public Shape {
wasm::Expression* Render(RelooperBuilder& Builder, bool InLoop) override;
};
-// Blocks with the same id were split and are identical, so we just care about ids in Multiple entries
typedef std::map<int, Shape*> IdShapeMap;
struct MultipleShape : public Shape {
@@ -299,6 +307,8 @@ struct MultipleShape : public Shape {
struct LoopShape : public Shape {
Shape *Inner;
+ BlockSet Entries; // we must visit at least one of these
+
LoopShape() : Shape(Loop), Inner(NULL) {}
wasm::Expression* Render(RelooperBuilder& Builder, bool InLoop) override;
};