| /* | |
| * Copyright 2016 WebAssembly Community Group participants | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| /* | |
| This is an optimized C++ implementation of the Relooper algorithm originally | |
| developed as part of Emscripten. This implementation includes optimizations | |
| added since the original academic paper [1] was published about it. | |
| [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 | |
| */ | |
| #include <cassert> | |
| #include <cstdarg> | |
| #include <cstdio> | |
| #include <cstdlib> | |
| #include <deque> | |
| #include <map> | |
| #include <memory> | |
| #include "support/insert_ordered.h" | |
| #include "wasm-builder.h" | |
| #include "wasm.h" | |
| #ifndef RELOOPER_DEBUG | |
| #define RELOOPER_DEBUG 0 | |
| #endif | |
| namespace CFG { | |
| class RelooperBuilder : public wasm::Builder { | |
| wasm::Index labelHelper; | |
| public: | |
| RelooperBuilder(wasm::Module& wasm, wasm::Index labelHelper) | |
| : wasm::Builder(wasm), labelHelper(labelHelper) {} | |
| wasm::LocalGet* makeGetLabel() { | |
| return makeLocalGet(labelHelper, wasm::Type::i32); | |
| } | |
| wasm::LocalSet* makeSetLabel(wasm::Index value) { | |
| return makeLocalSet(labelHelper, makeConst(wasm::Literal(int32_t(value)))); | |
| } | |
| wasm::Binary* makeCheckLabel(wasm::Index value) { | |
| return makeBinary( | |
| wasm::EqInt32, makeGetLabel(), makeConst(wasm::Literal(int32_t(value)))); | |
| } | |
| // 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(getShapeContinueName(id)); | |
| } | |
| wasm::Name getBlockBreakName(int id) { | |
| return wasm::Name(std::string("block$") + std::to_string(id) + "$break"); | |
| } | |
| wasm::Name getShapeContinueName(int id) { | |
| return wasm::Name(std::string("shape$") + std::to_string(id) + "$continue"); | |
| } | |
| }; | |
| struct Relooper; | |
| struct Block; | |
| struct Shape; | |
| // Info about a branching from one block to another | |
| 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 | |
| }; | |
| // If not NULL, this shape is the relevant one for purposes of getting to the | |
| // target block. We break or continue on it | |
| Shape* Ancestor = nullptr; | |
| // If Ancestor is not NULL, this says whether to break or continue | |
| Branch::FlowType Type; | |
| // A branch either has a condition expression if the block ends in ifs, or if | |
| // the block ends in a switch, then a list of indexes, which becomes the | |
| // indexes in the table of the switch. If not a switch, the condition can be | |
| // any expression (or nullptr for the branch taken when no other condition is | |
| // true) A condition must not have side effects, as the Relooper can reorder | |
| // or eliminate condition checking. This must not have side effects. | |
| wasm::Expression* Condition; | |
| // Switches are rare, so have just a pointer for their values. This contains | |
| // the values for which the branch will be taken, or for the default it is | |
| // simply not present. | |
| std::unique_ptr<std::vector<wasm::Index>> SwitchValues; | |
| // If provided, code that is run right before the branch is taken. This is | |
| // useful for phis. | |
| wasm::Expression* Code; | |
| Branch(wasm::Expression* ConditionInit, wasm::Expression* CodeInit = nullptr); | |
| Branch(std::vector<wasm::Index>&& ValuesInit, | |
| wasm::Expression* CodeInit = nullptr); | |
| // Emits code for branch | |
| wasm::Expression* | |
| Render(RelooperBuilder& Builder, Block* Target, bool SetLabel); | |
| }; | |
| using BlockSet = wasm::InsertOrderedSet<Block*>; | |
| using BlockBranchMap = wasm::InsertOrderedMap<Block*, Branch*>; | |
| // Represents a basic block of code - some instructions that end with a | |
| // control flow modifier (a branch, return or throw). | |
| struct Block { | |
| // Reference to the relooper containing this block. | |
| Relooper* relooper; | |
| // Branches become processed after we finish the shape relevant to them. For | |
| // example, when we recreate a loop, branches to the loop start become | |
| // continues and are now processed. When we calculate what shape to generate | |
| // from a set of blocks, we ignore processed branches. Blocks own the Branch | |
| // objects they use, and destroy them when done. | |
| BlockBranchMap BranchesOut; | |
| BlockSet BranchesIn; | |
| BlockBranchMap ProcessedBranchesOut; | |
| BlockSet ProcessedBranchesIn; | |
| Shape* Parent = nullptr; // The shape we are directly inside | |
| int Id = -1; // A unique identifier, defined when added to relooper | |
| // 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* Code; | |
| // If nullptr, then this block ends in ifs (or nothing). otherwise, this block | |
| // ends in a switch, done on this condition | |
| wasm::Expression* SwitchCondition; | |
| // If true, we are a multiple entry, so reaching us requires setting the label | |
| // variable | |
| bool IsCheckedMultipleEntry; | |
| Block(Relooper* relooper, | |
| wasm::Expression* CodeInit, | |
| wasm::Expression* SwitchConditionInit = nullptr); | |
| // Add a branch: if the condition holds we branch (or if null, we branch if | |
| // all others failed) Note that there can be only one branch from A to B (if | |
| // you need multiple conditions for the branch, create a more interesting | |
| // expression in the Condition). If a Block has no outgoing branches, the | |
| // contents in Code must contain a terminating instruction, as the relooper | |
| // doesn't know whether you want control flow to stop with an `unreachable` or | |
| // a `return` or something else (if you forget to do this, control flow may | |
| // continue into the block that happens to be emitted right after it). | |
| // Internally, adding a branch only adds the outgoing branch. The matching | |
| // incoming branch on the target is added by the Relooper itself as it works. | |
| void AddBranchTo(Block* Target, | |
| wasm::Expression* Condition, | |
| wasm::Expression* Code = nullptr); | |
| // Add a switch branch: if the switch condition is one of these values, we | |
| // branch (or if the list is empty, we are the default) Note that there can be | |
| // only one branch from A to B (if you need multiple values for the branch, | |
| // that's what the array and default are for). | |
| void AddSwitchBranchTo(Block* Target, | |
| std::vector<wasm::Index>&& Values, | |
| wasm::Expression* Code = nullptr); | |
| // Emit code for the block, including its contents and branchings out | |
| wasm::Expression* Render(RelooperBuilder& Builder, bool InLoop); | |
| }; | |
| // Represents a structured control flow shape, one of | |
| // | |
| // Simple: No control flow at all, just instructions in a single | |
| // basic block. | |
| // | |
| // 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. 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 Shape { | |
| // A unique identifier. Used to identify loops, labels are Lx where x is the | |
| // Id. Defined when added to relooper | |
| int Id = -1; | |
| // The shape that will appear in the code right after this one | |
| Shape* Next = nullptr; | |
| // The shape that control flow gets to naturally (if there is Next, then this | |
| // is Next) | |
| Shape* Natural; | |
| enum ShapeType { Simple, Multiple, Loop }; | |
| ShapeType Type; | |
| Shape(ShapeType TypeInit) : Type(TypeInit) {} | |
| virtual ~Shape() = default; | |
| virtual wasm::Expression* Render(RelooperBuilder& Builder, bool InLoop) = 0; | |
| 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; | |
| } | |
| }; | |
| struct SimpleShape : public Shape { | |
| Block* Inner = nullptr; | |
| SimpleShape() : Shape(Simple) {} | |
| wasm::Expression* Render(RelooperBuilder& Builder, bool InLoop) override; | |
| }; | |
| using IdShapeMap = std::map<int, Shape*>; | |
| struct MultipleShape : public Shape { | |
| IdShapeMap InnerMap; // entry block ID -> shape | |
| MultipleShape() : Shape(Multiple) {} | |
| wasm::Expression* Render(RelooperBuilder& Builder, bool InLoop) override; | |
| }; | |
| struct LoopShape : public Shape { | |
| Shape* Inner = nullptr; | |
| BlockSet Entries; // we must visit at least one of these | |
| LoopShape() : Shape(Loop) {} | |
| wasm::Expression* Render(RelooperBuilder& Builder, bool InLoop) override; | |
| }; | |
| // Implements the relooper algorithm for a function's blocks. | |
| // | |
| // Usage: | |
| // 1. Instantiate this struct. | |
| // 2. Create the blocks you have. Each should have its | |
| // branchings in specified (the branchings out will | |
| // be calculated by the relooper). | |
| // 3. Call Render(). | |
| // | |
| // Implementation details: The Relooper instance takes ownership of the blocks, | |
| // branches and shapes when created using the `AddBlock` etc. methods, and frees | |
| // them when done. | |
| struct Relooper { | |
| wasm::Module* Module; | |
| std::deque<std::unique_ptr<Block>> Blocks; | |
| std::deque<std::unique_ptr<Branch>> Branches; | |
| std::deque<std::unique_ptr<Shape>> Shapes; | |
| Shape* Root; | |
| bool MinSize; | |
| int BlockIdCounter; | |
| int ShapeIdCounter; | |
| Relooper(wasm::Module* ModuleInit); | |
| // Creates a new block associated with (and cleaned up along) this relooper. | |
| Block* AddBlock(wasm::Expression* CodeInit, | |
| wasm::Expression* SwitchConditionInit = nullptr); | |
| // Creates a new branch associated with (and cleaned up along) this relooper. | |
| Branch* AddBranch(wasm::Expression* ConditionInit, | |
| wasm::Expression* CodeInit); | |
| // Creates a new branch associated with (and cleaned up along) this relooper. | |
| Branch* AddBranch(std::vector<wasm::Index>&& ValuesInit, | |
| wasm::Expression* CodeInit = nullptr); | |
| // Creates a new simple shape associated with (and cleaned up along) this | |
| // relooper. | |
| SimpleShape* AddSimpleShape(); | |
| // Creates a new multiple shape associated with (and cleaned up along) this | |
| // relooper. | |
| MultipleShape* AddMultipleShape(); | |
| // Creates a new loop shape associated with (and cleaned up along) this | |
| // relooper. | |
| LoopShape* AddLoopShape(); | |
| // Calculates the shapes | |
| void Calculate(Block* Entry); | |
| // Renders the result. | |
| wasm::Expression* Render(RelooperBuilder& Builder); | |
| // Sets us to try to minimize size | |
| void SetMinSize(bool MinSize_) { MinSize = MinSize_; } | |
| }; | |
| using BlockBlockSetMap = wasm::InsertOrderedMap<Block*, BlockSet>; | |
| #if RELOOPER_DEBUG | |
| struct Debugging { | |
| static void Dump(Block* Curr, const char* prefix = NULL); | |
| static void Dump(BlockSet& Blocks, const char* prefix = NULL); | |
| static void Dump(Shape* S, const char* prefix = NULL); | |
| }; | |
| #endif | |
| } // namespace CFG |