diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cfg/Relooper.cpp | 170 | ||||
-rw-r--r-- | src/cfg/Relooper.h | 76 | ||||
-rw-r--r-- | src/passes/RemoveUnusedNames.cpp | 78 | ||||
-rw-r--r-- | src/wasm-validator.h | 7 |
4 files changed, 207 insertions, 124 deletions
diff --git a/src/cfg/Relooper.cpp b/src/cfg/Relooper.cpp index 9330be718..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; @@ -100,7 +153,6 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) { } bool SetLabel = true; // in some cases it is clear we can avoid setting label, see later - bool ForceSetLabel = Shape::IsEmulated(Parent) != nullptr; // 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 @@ -129,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 @@ -170,24 +221,23 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) { Target = DefaultTarget; Details = ProcessedBranchesOut[DefaultTarget]; } - bool SetCurrLabel = (SetLabel && Target->IsCheckedMultipleEntry) || ForceSetLabel; + 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); @@ -218,7 +268,7 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) { RemainingConditions = Now; } } - if (isDefault) break; + if (IsDefault) break; } } else { // Emit a switch @@ -239,18 +289,17 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) { CurrName = SwitchDefault; } // generate the content for this block - bool SetCurrLabel = (SetLabel && Target->IsCheckedMultipleEntry) || ForceSetLabel; + 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 @@ -286,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; @@ -304,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)); } @@ -329,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)); } @@ -340,22 +383,17 @@ 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)); } return Ret; } -// EmulatedShape - -wasm::Expression* EmulatedShape::Render(RelooperBuilder& Builder, bool InLoop) { - abort(); // TODO -} - // Relooper -Relooper::Relooper() : Root(nullptr), Emulate(false), MinSize(false), BlockIdCounter(1), ShapeIdCounter(0) { // block ID 0 is reserved for clearings +Relooper::Relooper() : Root(nullptr), MinSize(false), BlockIdCounter(1), ShapeIdCounter(0) { // block ID 0 is reserved for clearings } Relooper::~Relooper() { @@ -462,27 +500,12 @@ 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; } - 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. @@ -590,8 +613,9 @@ void Relooper::Calculate(Block *Entry) { Solipsize(*iter, Branch::Break, Loop, InnerBlocks); } // Finish up - Shape *Inner = Process(InnerBlocks, Entries, nullptr); + Shape *Inner = Process(InnerBlocks, Entries); Loop->Inner = Inner; + Loop->Entries = Entries; return Loop; } @@ -715,9 +739,8 @@ void Relooper::Calculate(Block *Entry) { #endif } - Shape *MakeMultiple(BlockSet &Blocks, BlockSet& Entries, BlockBlockSetMap& IndependentGroups, Shape *Prev, BlockSet &NextEntries) { + Shape *MakeMultiple(BlockSet &Blocks, BlockSet& Entries, BlockBlockSetMap& IndependentGroups, BlockSet &NextEntries, bool IsCheckedMultiple) { PrintDebug("creating multiple block with %d inner groups\n", IndependentGroups.size()); - bool Fused = !!(Shape::IsSimple(Prev)); MultipleShape *Multiple = new MultipleShape(); Notice(Multiple); BlockSet CurrEntries; @@ -745,9 +768,8 @@ void Relooper::Calculate(Block *Entry) { iter = Next; // increment carefully because Solipsize can remove us } } - Multiple->InnerMap[CurrEntry->Id] = Process(CurrBlocks, CurrEntries, nullptr); - // If we are not fused, then our entries will actually be checked - if (!Fused) { + Multiple->InnerMap[CurrEntry->Id] = Process(CurrBlocks, CurrEntries); + if (IsCheckedMultiple) { CurrEntry->IsCheckedMultipleEntry = true; } } @@ -767,13 +789,14 @@ void Relooper::Calculate(Block *Entry) { // 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) { + Shape *Process(BlockSet &Blocks, BlockSet& InitialEntries) { PrintDebug("Process() called\n", 0); BlockSet *Entries = &InitialEntries; BlockSet TempEntries[2]; int CurrTempIndex = 0; BlockSet *NextEntries; Shape *Ret = nullptr; + Shape *Prev = nullptr; #define Make(call) \ Shape *Temp = call; \ if (Prev) Prev->Next = Temp; \ @@ -794,9 +817,6 @@ void Relooper::Calculate(Block *Entry) { 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)); @@ -816,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; @@ -879,7 +900,18 @@ void Relooper::Calculate(Block *Entry) { if (IndependentGroups.size() > 0) { // Some groups removable ==> Multiple - Make(MakeMultiple(Blocks, *Entries, IndependentGroups, Prev, *NextEntries)); + // 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 @@ -901,7 +933,7 @@ void Relooper::Calculate(Block *Entry) { BlockSet Entries; Entries.insert(Entry); - Root = Analyzer(this).Process(AllBlocks, Entries, nullptr); + Root = Analyzer(this).Process(AllBlocks, Entries); assert(Root); } diff --git a/src/cfg/Relooper.h b/src/cfg/Relooper.h index 74a95c8bf..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; } }; @@ -218,7 +222,7 @@ struct Block { BlockBranchMap ProcessedBranchesOut; BlockSet ProcessedBranchesIn; Shape *Parent; // The shape we are directly inside - int Id; // A unique identifier, defined when added to relooper. Note that this uniquely identifies a *logical* block - if we split it, the two instances have the same content *and* the same Id + int Id; // A unique identifier, defined when added to relooper wasm::Expression* Code; // The code in this block. This can be arbitrary wasm code, including internal control flow, it should just not branch to the outside wasm::Expression* SwitchCondition; // If nullptr, then this block ends in ifs (or nothing). otherwise, this block ends in a switch, done on this condition bool IsCheckedMultipleEntry; // If true, we are a multiple entry, so reaching us requires setting the label variable @@ -241,26 +245,25 @@ struct Block { // Represents a structured control flow shape, one of // -// Simple: No control flow at all, just instructions. If several -// blocks, then -// -// 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. +// Simple: No control flow at all, just instructions in a single +// basic block. // -// Loop: An infinite loop. +// 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. // -// Emulated: Control flow is managed by a switch in a loop. This -// is necessary in some cases, for example when control -// flow is not known until runtime (indirect branches, -// setjmp returns, etc.) +// 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; struct MultipleShape; struct LoopShape; -struct EmulatedShape; struct Shape { int Id; // A unique identifier. Used to identify loops, labels are Lx where x is the Id. Defined when added to relooper @@ -270,8 +273,7 @@ struct Shape { enum ShapeType { Simple, Multiple, - Loop, - Emulated + Loop }; ShapeType Type; @@ -283,7 +285,6 @@ struct Shape { static SimpleShape *IsSimple(Shape *It) { return It && It->Type == Simple ? (SimpleShape*)It : NULL; } static MultipleShape *IsMultiple(Shape *It) { return It && It->Type == Multiple ? (MultipleShape*)It : NULL; } static LoopShape *IsLoop(Shape *It) { return It && It->Type == Loop ? (LoopShape*)It : NULL; } - static EmulatedShape *IsEmulated(Shape *It) { return It && It->Type == Emulated ? (EmulatedShape*)It : NULL; } }; struct SimpleShape : public Shape { @@ -293,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 { @@ -307,17 +307,9 @@ struct MultipleShape : public Shape { struct LoopShape : public Shape { Shape *Inner; - LoopShape() : Shape(Loop), Inner(NULL) {} - wasm::Expression* Render(RelooperBuilder& Builder, bool InLoop) override; -}; + BlockSet Entries; // we must visit at least one of these -// TODO EmulatedShape is only partially functional. Currently it can be used for the -// entire set of blocks being relooped, but not subsets. -struct EmulatedShape : public Shape { - Block *Entry; - BlockSet Blocks; - - EmulatedShape() : Shape(Emulated) { } + LoopShape() : Shape(Loop), Inner(NULL) {} wasm::Expression* Render(RelooperBuilder& Builder, bool InLoop) override; }; @@ -336,7 +328,6 @@ struct Relooper { std::deque<Block*> Blocks; std::deque<Shape*> Shapes; Shape *Root; - bool Emulate; bool MinSize; int BlockIdCounter; int ShapeIdCounter; @@ -352,9 +343,6 @@ struct Relooper { // Renders the result. wasm::Expression* Render(RelooperBuilder& Builder); - // Sets whether we must emulate everything with switch-loop code - void SetEmulate(int E) { Emulate = !!E; } - // Sets us to try to minimize size void SetMinSize(bool MinSize_) { MinSize = MinSize_; } }; diff --git a/src/passes/RemoveUnusedNames.cpp b/src/passes/RemoveUnusedNames.cpp index 1cd2a483f..9c6743479 100644 --- a/src/passes/RemoveUnusedNames.cpp +++ b/src/passes/RemoveUnusedNames.cpp @@ -15,7 +15,8 @@ */ // -// Removes names from locations that are never branched to. +// Removes names from locations that are never branched to, and +// merge names when possible (by merging their blocks) // #include <wasm.h> @@ -30,27 +31,84 @@ struct RemoveUnusedNames : public WalkerPass<PostWalker<RemoveUnusedNames, Visit // We maintain a list of branches that we saw in children, then when we reach // a parent block, we know if it was branched to - std::set<Name> branchesSeen; + std::map<Name, std::set<Expression*>> branchesSeen; void visitBreak(Break *curr) { - branchesSeen.insert(curr->name); + branchesSeen[curr->name].insert(curr); + } + + void visitSwitch(Switch *curr) { + for (auto name : curr->targets) { + branchesSeen[name].insert(curr); + } + branchesSeen[curr->default_].insert(curr); + } + + void handleBreakTarget(Name& name) { + if (name.is()) { + if (branchesSeen.find(name) == branchesSeen.end()) { + name = Name(); + } else { + branchesSeen.erase(name); + } + } } void visitBlock(Block *curr) { - if (curr->name.is() && branchesSeen.count(curr->name) == 0) { - curr->name = Name(); + if (curr->name.is() && curr->list.size() == 1) { + auto* child = curr->list[0]->dynCast<Block>(); + if (child && child->name.is()) { + // we have just one child, this block, so breaking out of it goes to the same place as breaking out of us, we just need one name (and block) + auto& branches = branchesSeen[curr->name]; + for (auto* branch : branches) { + if (Break* br = branch->dynCast<Break>()) { + if (br->name == curr->name) br->name = child->name; + } else if (Switch* sw = branch->dynCast<Switch>()) { + for (auto& target : sw->targets) { + if (target == curr->name) target = child->name; + } + if (sw->default_ == curr->name) sw->default_ = child->name; + } else { + WASM_UNREACHABLE(); + } + } + replaceCurrent(child); + } + } + handleBreakTarget(curr->name); + if (curr->name.is() && curr->list.size() == 1) { + auto* child = curr->list[0]->dynCast<Loop>(); + if (child && !child->out.is()) { + // we have just one child, this loop, and it lacks an out label. So this block's name is doing just that! + child->out = curr->name; + replaceCurrent(child); + } } } - void visitSwitch(Switch *curr) { - for (auto name : curr->targets) { - branchesSeen.insert(name); + void visitLoop(Loop *curr) { + handleBreakTarget(curr->in); + // Loops can have just 'in', but cannot have just 'out' + auto out = curr->out; + handleBreakTarget(curr->out); + if (curr->out.is() && !curr->in.is()) { + auto* block = getModule()->allocator.alloc<Block>(); + block->name = out; + block->list.push_back(curr->body); + replaceCurrent(block); + } + if (curr->in.is() && !curr->out.is()) { + auto* child = curr->body->dynCast<Block>(); + if (child && child->name.is()) { + // we have just one child, this block, and we lack an out label. So we can take the block's! + curr->out = child->name; + child->name = Name(); + } } - branchesSeen.insert(curr->default_); } void visitFunction(Function *curr) { - branchesSeen.clear(); + assert(branchesSeen.empty()); } }; diff --git a/src/wasm-validator.h b/src/wasm-validator.h index b9ad30c73..066ab57da 100644 --- a/src/wasm-validator.h +++ b/src/wasm-validator.h @@ -312,7 +312,12 @@ public: void doWalkFunction(Function* func) { PostWalker<WasmValidator, Visitor<WasmValidator>>::doWalkFunction(func); - shouldBeTrue(breakTypes.size() == 0, "break targets", "all break targets must be valid"); + if (!shouldBeTrue(breakTypes.size() == 0, "break targets", "all break targets must be valid")) { + for (auto& target : breakTypes) { + std::cerr << " - " << target.first << '\n'; + } + breakTypes.clear(); + } } private: |