187 lines
6.6 KiB
C++
187 lines
6.6 KiB
C++
//===-- 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
|