| //===-- Relooper.h - Top-level interface for WebAssembly ----*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===-------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// \brief This defines an optimized C++ implemention of the Relooper |
| /// algorithm, originally developed as part of Emscripten, which |
| /// generates a structured AST from arbitrary control flow. |
| /// |
| //===-------------------------------------------------------------------===// |
| |
| #include "llvm/ADT/MapVector.h" |
| #include "llvm/ADT/SetVector.h" |
| #include "llvm/Support/Casting.h" |
| |
| #include <cassert> |
| #include <cstdarg> |
| #include <cstdio> |
| #include <deque> |
| #include <list> |
| #include <map> |
| #include <memory> |
| #include <set> |
| |
| namespace llvm { |
| |
| namespace 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, |
| 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 |
| }; |
| Shape |
| *Ancestor; // If not nullptr, 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 nullptr, this says whether to break or |
| // continue |
| bool Labeled; // If a break or continue, whether we need to use a label |
| const char *Condition; // The condition for which we branch. For example, |
| // "my_var == 1". Conditions are checked one by one. |
| // One of the conditions should have nullptr as the |
| // condition, in which case it is the default |
| // FIXME: move from char* to LLVM data structures |
| const char *Code; // If provided, code that is run right before the branch is |
| // taken. This is useful for phis |
| // FIXME: move from char* to LLVM data structures |
| |
| Branch(const char *ConditionInit, const char *CodeInit = nullptr); |
| ~Branch(); |
| }; |
| |
| typedef SetVector<Block *> BlockSet; |
| typedef MapVector<Block *, Branch *> BlockBranchMap; |
| typedef MapVector<Block *, std::unique_ptr<Branch>> OwningBlockBranchMap; |
| |
| /// |
| /// Represents a basic block of code - some instructions that end with a |
| /// control flow modifier (a branch, return or throw). |
| /// |
| struct Block { |
| // 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. |
| OwningBlockBranchMap BranchesOut; |
| BlockSet BranchesIn; |
| OwningBlockBranchMap 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 |
| const char *Code; // The string representation of the code in this block. |
| // Owning pointer (we copy the input) |
| // FIXME: move from char* to LLVM data structures |
| const char *BranchVar; // A variable whose value determines where we go; if |
| // this is not nullptr, emit a switch on that variable |
| // FIXME: move from char* to LLVM data structures |
| bool IsCheckedMultipleEntry; // If true, we are a multiple entry, so reaching |
| // us requires setting the label variable |
| |
| Block(const char *CodeInit, const char *BranchVarInit); |
| ~Block(); |
| |
| void AddBranchTo(Block *Target, const char *Condition, |
| const char *Code = nullptr); |
| }; |
| |
| /// |
| /// Represents a structured control flow shape |
| /// |
| struct Shape { |
| int Id; // A unique identifier. Used to identify loops, labels are Lx where x |
| // is the Id. Defined when added to relooper |
| Shape *Next; // The shape that will appear in the code right after this one |
| Shape *Natural; // The shape that control flow gets to naturally (if there is |
| // Next, then this is Next) |
| |
| /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.) |
| enum ShapeKind { SK_Simple, SK_Multiple, SK_Loop }; |
| |
| private: |
| ShapeKind Kind; |
| |
| public: |
| ShapeKind getKind() const { return Kind; } |
| |
| Shape(ShapeKind KindInit) : Id(-1), Next(nullptr), Kind(KindInit) {} |
| }; |
| |
| /// |
| /// Simple: No control flow at all, just instructions. |
| /// |
| struct SimpleShape : public Shape { |
| Block *Inner; |
| |
| SimpleShape() : Shape(SK_Simple), Inner(nullptr) {} |
| |
| static bool classof(const Shape *S) { return S->getKind() == SK_Simple; } |
| }; |
| |
| /// |
| /// A shape that may be implemented with a labeled loop. |
| /// |
| struct LabeledShape : public Shape { |
| bool Labeled; // If we have a loop, whether it needs to be labeled |
| |
| LabeledShape(ShapeKind KindInit) : Shape(KindInit), Labeled(false) {} |
| }; |
| |
| // 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; |
| |
| /// |
| /// 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. |
| /// |
| struct MultipleShape : public LabeledShape { |
| IdShapeMap InnerMap; // entry block ID -> shape |
| int Breaks; // If we have branches on us, we need a loop (or a switch). This |
| // is a counter of requirements, |
| // if we optimize it to 0, the loop is unneeded |
| bool UseSwitch; // Whether to switch on label as opposed to an if-else chain |
| |
| MultipleShape() : LabeledShape(SK_Multiple), Breaks(0), UseSwitch(false) {} |
| |
| static bool classof(const Shape *S) { return S->getKind() == SK_Multiple; } |
| }; |
| |
| /// |
| /// Loop: An infinite loop. |
| /// |
| struct LoopShape : public LabeledShape { |
| Shape *Inner; |
| |
| LoopShape() : LabeledShape(SK_Loop), Inner(nullptr) {} |
| |
| static bool classof(const Shape *S) { return S->getKind() == SK_Loop; } |
| }; |
| |
| } // namespace Relooper |
| |
| } // namespace llvm |