|
|
|
|
@ -1,47 +1,37 @@
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include "range.h"
|
|
|
|
|
#include <cassert>
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <iterator>
|
|
|
|
|
#include <list>
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <memory>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <type_traits>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
namespace sysy {
|
|
|
|
|
|
|
|
|
|
template <typename IterT> struct range {
|
|
|
|
|
using iterator = IterT;
|
|
|
|
|
using value_type = typename std::iterator_traits<iterator>::value_type;
|
|
|
|
|
using reference = typename std::iterator_traits<iterator>::reference;
|
|
|
|
|
|
|
|
|
|
iterator b;
|
|
|
|
|
iterator e;
|
|
|
|
|
explicit range(iterator b, iterator e) : b(b), e(e) {}
|
|
|
|
|
iterator begin() { return b; }
|
|
|
|
|
iterator end() { return e; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename IterT> range<IterT> make_range(IterT b, IterT e) {
|
|
|
|
|
return range(b, e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Types
|
|
|
|
|
//
|
|
|
|
|
// The SysY type system is quite simple.
|
|
|
|
|
// 1. The base class `Type` is used to represent all primitive scalar types,
|
|
|
|
|
// include `int`, `float`, `void`, and the label type representing branch
|
|
|
|
|
// targets.
|
|
|
|
|
// 2. `PointerType` and `FunctionType` derive from `Type` and represent pointer
|
|
|
|
|
// type and function type, respectively.
|
|
|
|
|
//
|
|
|
|
|
// NOTE `Type` and its derived classes have their ctors declared as 'protected'.
|
|
|
|
|
// Users must use Type::getXXXType() methods to obtain `Type` pointers.
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* \defgroup type Types
|
|
|
|
|
* The SysY type system is quite simple.
|
|
|
|
|
* 1. The base class `Type` is used to represent all primitive scalar types,
|
|
|
|
|
* include `int`, `float`, `void`, and the label type representing branch
|
|
|
|
|
* targets.
|
|
|
|
|
* 2. `PointerType` and `FunctionType` derive from `Type` and represent pointer
|
|
|
|
|
* type and function type, respectively.
|
|
|
|
|
*
|
|
|
|
|
* NOTE `Type` and its derived classes have their ctors declared as 'protected'.
|
|
|
|
|
* Users must use Type::getXXXType() methods to obtain `Type` pointers.
|
|
|
|
|
* @{
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* `Type` is used to represent all primitive scalar types,
|
|
|
|
|
* include `int`, `float`, `void`, and the label type representing branch
|
|
|
|
|
* targets
|
|
|
|
|
*/
|
|
|
|
|
class Type {
|
|
|
|
|
public:
|
|
|
|
|
enum Kind {
|
|
|
|
|
@ -66,7 +56,6 @@ public:
|
|
|
|
|
static Type *getPointerType(Type *baseType);
|
|
|
|
|
static Type *getFunctionType(Type *returnType,
|
|
|
|
|
const std::vector<Type *> ¶mTypes = {});
|
|
|
|
|
static int getTypeSize();
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
Kind getKind() const { return kind; }
|
|
|
|
|
@ -76,8 +65,14 @@ public:
|
|
|
|
|
bool isLabel() const { return kind == kLabel; }
|
|
|
|
|
bool isPointer() const { return kind == kPointer; }
|
|
|
|
|
bool isFunction() const { return kind == kFunction; }
|
|
|
|
|
int getSize() const;
|
|
|
|
|
template <typename T>
|
|
|
|
|
std::enable_if_t<std::is_base_of_v<Type, T>, T *> as() const {
|
|
|
|
|
return dynamic_cast<T *>(const_cast<Type *>(this));
|
|
|
|
|
}
|
|
|
|
|
}; // class Type
|
|
|
|
|
|
|
|
|
|
//! Pointer type
|
|
|
|
|
class PointerType : public Type {
|
|
|
|
|
protected:
|
|
|
|
|
Type *baseType;
|
|
|
|
|
@ -92,6 +87,7 @@ public:
|
|
|
|
|
Type *getBaseType() const { return baseType; }
|
|
|
|
|
}; // class PointerType
|
|
|
|
|
|
|
|
|
|
//! Function type
|
|
|
|
|
class FunctionType : public Type {
|
|
|
|
|
private:
|
|
|
|
|
Type *returnType;
|
|
|
|
|
@ -107,49 +103,73 @@ public:
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
Type *getReturnType() const { return returnType; }
|
|
|
|
|
const std::vector<Type *> &getParamTypes() const { return paramTypes; }
|
|
|
|
|
auto getParamTypes() const { return make_range(paramTypes); }
|
|
|
|
|
int getNumParams() const { return paramTypes.size(); }
|
|
|
|
|
}; // class FunctionType
|
|
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Values
|
|
|
|
|
//
|
|
|
|
|
// description
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
/*!
|
|
|
|
|
* @}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* \defgroup ir IR
|
|
|
|
|
*
|
|
|
|
|
* The SysY IR is an instruction level language. The IR is orgnized
|
|
|
|
|
* as a four-level tree structure, as shown below
|
|
|
|
|
*
|
|
|
|
|
* \dotfile ir-4level.dot IR Structure
|
|
|
|
|
*
|
|
|
|
|
* - `Module` corresponds to the top level "CompUnit" syntax structure
|
|
|
|
|
* - `GlobalValue` corresponds to the "Decl" syntax structure
|
|
|
|
|
* - `Function` corresponds to the "FuncDef" syntax structure
|
|
|
|
|
* - `BasicBlock` is a sequence of instructions without branching. A `Function`
|
|
|
|
|
* made up by one or more `BasicBlock`s.
|
|
|
|
|
* - `Instruction` represents a primitive operation on values, e.g., add or sub.
|
|
|
|
|
*
|
|
|
|
|
* The fundamental data concept in SysY IR is `Value`. A `Value` is like
|
|
|
|
|
* a register and is used by `Instruction`s as input/output operand. Each value
|
|
|
|
|
* has an associated `Type` indicating the data type held by the value.
|
|
|
|
|
*
|
|
|
|
|
* Most `Instruction`s have a three-address signature, i.e., there are at most 2
|
|
|
|
|
* input values and at most 1 output value.
|
|
|
|
|
*
|
|
|
|
|
* The SysY IR adots a Static-Single-Assignment (SSA) design. That is, `Value`
|
|
|
|
|
* is defined (as the output operand ) by some instruction, and used (as the
|
|
|
|
|
* input operand) by other instructions. While a value can be used by multiple
|
|
|
|
|
* instructions, the `definition` occurs only once. As a result, there is a
|
|
|
|
|
* one-to-one relation between a value and the instruction defining it. In other
|
|
|
|
|
* words, any instruction defines a value can be viewed as the defined value
|
|
|
|
|
* itself. So `Instruction` is also a `Value` in SysY IR. See `Value` for the
|
|
|
|
|
* type hierachy.
|
|
|
|
|
*
|
|
|
|
|
* @{
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
class User;
|
|
|
|
|
class Value;
|
|
|
|
|
|
|
|
|
|
//! `Use` represents the relation between a `Value` and its `User`
|
|
|
|
|
class Use {
|
|
|
|
|
public:
|
|
|
|
|
enum Kind {
|
|
|
|
|
kRead,
|
|
|
|
|
kWrite,
|
|
|
|
|
kReadWrite,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Kind kind;
|
|
|
|
|
//! the position of value in the user's operands, i.e.,
|
|
|
|
|
//! user->getOperands[index] == value
|
|
|
|
|
int index;
|
|
|
|
|
User *user;
|
|
|
|
|
Value *value;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
Use() = default;
|
|
|
|
|
Use(Kind kind, int index, User *user, Value *value)
|
|
|
|
|
: kind(kind), index(index), user(user), value(value) {}
|
|
|
|
|
Use(int index, User *user, Value *value)
|
|
|
|
|
: index(index), user(user), value(value) {}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
Kind getKind() const { return kind; }
|
|
|
|
|
int getIndex() const { return index; }
|
|
|
|
|
User *getUser() const { return user; }
|
|
|
|
|
Value *getValue() const { return value; }
|
|
|
|
|
bool isRead() const { return kind == kRead; }
|
|
|
|
|
bool isWrite() const { return kind == kWrite; }
|
|
|
|
|
bool isReadWrite() const { return kind == kReadWrite; }
|
|
|
|
|
void setValue(Value *value) { value = value; }
|
|
|
|
|
}; // class Use
|
|
|
|
|
|
|
|
|
|
//! The base class of all value types
|
|
|
|
|
class Value {
|
|
|
|
|
protected:
|
|
|
|
|
Type *type;
|
|
|
|
|
@ -172,43 +192,57 @@ public:
|
|
|
|
|
void removeUse(Use *use) { uses.remove(use); }
|
|
|
|
|
}; // class Value
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* Static constants known at compile time.
|
|
|
|
|
*
|
|
|
|
|
* `ConstantValue`s are not defined by instructions, and do not use any other
|
|
|
|
|
* `Value`s. It's type is either `int` or `float`.
|
|
|
|
|
*/
|
|
|
|
|
class ConstantValue : public Value {
|
|
|
|
|
friend class IRBuilder;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
union {
|
|
|
|
|
int iConstant;
|
|
|
|
|
float fConstant;
|
|
|
|
|
int iScalar;
|
|
|
|
|
float fScalar;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
ConstantValue(int value, const std::string &name = "")
|
|
|
|
|
: Value(Type::getIntType(), name), iConstant(value) {}
|
|
|
|
|
: Value(Type::getIntType(), name), iScalar(value) {}
|
|
|
|
|
ConstantValue(float value, const std::string &name = "")
|
|
|
|
|
: Value(Type::getFloatType(), name), fConstant(value) {}
|
|
|
|
|
: Value(Type::getFloatType(), name), fScalar(value) {}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
static ConstantValue *getInt(int value, const std::string &name = "");
|
|
|
|
|
static ConstantValue *getFloat(float value, const std::string &name = "");
|
|
|
|
|
static ConstantValue *get(int value, const std::string &name = "");
|
|
|
|
|
static ConstantValue *get(float value, const std::string &name = "");
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
int getInt() const {
|
|
|
|
|
assert(isInt());
|
|
|
|
|
return iConstant;
|
|
|
|
|
return iScalar;
|
|
|
|
|
}
|
|
|
|
|
float getFloat() const {
|
|
|
|
|
assert(isFloat());
|
|
|
|
|
return fConstant;
|
|
|
|
|
return fScalar;
|
|
|
|
|
}
|
|
|
|
|
}; // class ConstantValue
|
|
|
|
|
|
|
|
|
|
class BasicBlock;
|
|
|
|
|
/*!
|
|
|
|
|
* Arguments of `BasicBlock`s.
|
|
|
|
|
*
|
|
|
|
|
* SysY IR is an SSA language, however, it does not use PHI instructions as in
|
|
|
|
|
* LLVM IR. `Value`s from different predecessor blocks are passed explicitly as
|
|
|
|
|
* block arguments. This is also the approach used by MLIR.
|
|
|
|
|
* NOTE that `Function` does not own `Argument`s, function arguments are
|
|
|
|
|
* implemented as its entry block's arguments.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
class Argument : public Value {
|
|
|
|
|
protected:
|
|
|
|
|
BasicBlock *block;
|
|
|
|
|
int index;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
public:
|
|
|
|
|
Argument(Type *type, BasicBlock *block, int index,
|
|
|
|
|
const std::string &name = "")
|
|
|
|
|
: Value(type, name), block(block), index(index) {}
|
|
|
|
|
@ -216,6 +250,13 @@ protected:
|
|
|
|
|
|
|
|
|
|
class Instruction;
|
|
|
|
|
class Function;
|
|
|
|
|
/*!
|
|
|
|
|
* The container for `Instruction` sequence.
|
|
|
|
|
*
|
|
|
|
|
* `BasicBlock` maintains a list of `Instruction`s, with the last one being
|
|
|
|
|
* a terminator (branch or return). Besides, `BasicBlock` stores its arguments
|
|
|
|
|
* and records its predecessor and successor `BasicBlock`s.
|
|
|
|
|
*/
|
|
|
|
|
class BasicBlock : public Value {
|
|
|
|
|
friend class Function;
|
|
|
|
|
|
|
|
|
|
@ -238,6 +279,7 @@ protected:
|
|
|
|
|
arguments(), successors(), predecessors() {}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
int getNumInstructions() const { return instructions.size(); }
|
|
|
|
|
int getNumArguments() const { return arguments.size(); }
|
|
|
|
|
int getNumPredecessors() const { return predecessors.size(); }
|
|
|
|
|
int getNumSuccessors() const { return successors.size(); }
|
|
|
|
|
@ -249,8 +291,15 @@ public:
|
|
|
|
|
iterator begin() { return instructions.begin(); }
|
|
|
|
|
iterator end() { return instructions.end(); }
|
|
|
|
|
iterator terminator() { return std::prev(end()); }
|
|
|
|
|
Argument *createArgument(Type *type, const std::string &name = "") {
|
|
|
|
|
arguments.emplace_back(type, this, arguments.size(), name);
|
|
|
|
|
return &arguments.back();
|
|
|
|
|
};
|
|
|
|
|
}; // class BasicBlock
|
|
|
|
|
|
|
|
|
|
//! User is the abstract base type of `Value` types which use other `Value` as
|
|
|
|
|
//! operands. Currently, there are two kinds of `User`s, `Instruction` and
|
|
|
|
|
//! `GlobalValue`.
|
|
|
|
|
class User : public Value {
|
|
|
|
|
protected:
|
|
|
|
|
std::vector<Use> operands;
|
|
|
|
|
@ -259,15 +308,27 @@ protected:
|
|
|
|
|
User(Type *type, const std::string &name = "")
|
|
|
|
|
: Value(type, name), operands() {}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
struct operand_iterator : std::vector<Use>::iterator {
|
|
|
|
|
using Base = std::vector<Use>::iterator;
|
|
|
|
|
using Base::Base;
|
|
|
|
|
using value_type = Value *;
|
|
|
|
|
value_type operator->() { return operator*().getValue(); }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
int getNumOperands() const { return operands.size(); }
|
|
|
|
|
const std::vector<Use> &getOperands() const { return operands; }
|
|
|
|
|
const Use &getOperand(int index) const { return operands[index]; }
|
|
|
|
|
void addOperand(Value *value, Use::Kind mode = Use::kRead) {
|
|
|
|
|
operands.emplace_back(mode, operands.size(), this, value);
|
|
|
|
|
auto operand_begin() const { return operands.begin(); }
|
|
|
|
|
auto operand_end() const { return operands.end(); }
|
|
|
|
|
auto getOperands() const {
|
|
|
|
|
return make_range(operand_begin(), operand_end());
|
|
|
|
|
}
|
|
|
|
|
Value *getOperand(int index) const { return operands[index].getValue(); }
|
|
|
|
|
void addOperand(Value *value) {
|
|
|
|
|
operands.emplace_back(operands.size(), this, value);
|
|
|
|
|
value->addUse(&operands.back());
|
|
|
|
|
}
|
|
|
|
|
void addOperands(const std::vector<Value *> &operands) {
|
|
|
|
|
template <typename ContainerT> void addOperands(const ContainerT &operands) {
|
|
|
|
|
for (auto value : operands)
|
|
|
|
|
addOperand(value);
|
|
|
|
|
}
|
|
|
|
|
@ -276,6 +337,9 @@ public:
|
|
|
|
|
const std::string &getName() const { return name; }
|
|
|
|
|
}; // class User
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* Base of all concrete instruction types.
|
|
|
|
|
*/
|
|
|
|
|
class Instruction : public User {
|
|
|
|
|
public:
|
|
|
|
|
enum Kind : uint64_t {
|
|
|
|
|
@ -337,24 +401,47 @@ public:
|
|
|
|
|
BasicBlock *getParent() const { return parent; }
|
|
|
|
|
void setParent(BasicBlock *bb) { parent = bb; }
|
|
|
|
|
|
|
|
|
|
bool isCmp() const {
|
|
|
|
|
static constexpr uint64_t CondOpMask =
|
|
|
|
|
bool isBinary() const {
|
|
|
|
|
static constexpr uint64_t BinaryOpMask =
|
|
|
|
|
(kAdd | kSub | kMul | kDiv | kRem) |
|
|
|
|
|
(kICmpEQ | kICmpNE | kICmpLT | kICmpGT | kICmpLE | kICmpGE) |
|
|
|
|
|
(kFAdd | kFSub | kFMul | kFDiv | kFRem) |
|
|
|
|
|
(kFCmpEQ | kFCmpNE | kFCmpLT | kFCmpGT | kFCmpLE | kFCmpGE);
|
|
|
|
|
return kind & CondOpMask;
|
|
|
|
|
return kind & BinaryOpMask;
|
|
|
|
|
}
|
|
|
|
|
bool isUnary() const {
|
|
|
|
|
static constexpr uint64_t UnaryOpMask = kNeg | kNot | kFNeg | kFtoI | kIToF;
|
|
|
|
|
return kind & UnaryOpMask;
|
|
|
|
|
}
|
|
|
|
|
bool isMemory() const {
|
|
|
|
|
static constexpr uint64_t MemoryOpMask = kAlloca | kLoad | kStore;
|
|
|
|
|
return kind & MemoryOpMask;
|
|
|
|
|
}
|
|
|
|
|
bool isTerminator() {
|
|
|
|
|
bool isTerminator() const {
|
|
|
|
|
static constexpr uint64_t TerminatorOpMask = kCondBr | kBr | kReturn;
|
|
|
|
|
return kind & TerminatorOpMask;
|
|
|
|
|
}
|
|
|
|
|
bool isCommutative() {
|
|
|
|
|
bool isCmp() const {
|
|
|
|
|
static constexpr uint64_t CmpOpMask =
|
|
|
|
|
(kICmpEQ | kICmpNE | kICmpLT | kICmpGT | kICmpLE | kICmpGE) |
|
|
|
|
|
(kFCmpEQ | kFCmpNE | kFCmpLT | kFCmpGT | kFCmpLE | kFCmpGE);
|
|
|
|
|
return kind & CmpOpMask;
|
|
|
|
|
}
|
|
|
|
|
bool isBranch() const {
|
|
|
|
|
static constexpr uint64_t BranchOpMask = kBr | kCondBr;
|
|
|
|
|
return kind & BranchOpMask;
|
|
|
|
|
}
|
|
|
|
|
bool isCommutative() const {
|
|
|
|
|
static constexpr uint64_t CommutativeOpMask =
|
|
|
|
|
kAdd | kMul | kICmpEQ | kICmpNE | kFAdd | kFMul | kFCmpEQ | kFCmpNE;
|
|
|
|
|
return kind & CommutativeOpMask;
|
|
|
|
|
}
|
|
|
|
|
bool isUnconditional() const { return kind == kBr; }
|
|
|
|
|
bool isConditional() const { return kind == kCondBr; }
|
|
|
|
|
}; // class Instruction
|
|
|
|
|
|
|
|
|
|
class Function;
|
|
|
|
|
//! Function call.
|
|
|
|
|
class CallInst : public Instruction {
|
|
|
|
|
friend class IRBuilder;
|
|
|
|
|
|
|
|
|
|
@ -365,10 +452,11 @@ protected:
|
|
|
|
|
public:
|
|
|
|
|
Function *getCallee();
|
|
|
|
|
auto getArguments() {
|
|
|
|
|
return make_range(std::next(operands.begin()), operands.end());
|
|
|
|
|
return make_range(std::next(operand_begin()), operand_end());
|
|
|
|
|
}
|
|
|
|
|
}; // class CallInst
|
|
|
|
|
|
|
|
|
|
//! Unary instruction, includes '!', '-' and type conversion.
|
|
|
|
|
class UnaryInst : public Instruction {
|
|
|
|
|
friend class IRBuilder;
|
|
|
|
|
|
|
|
|
|
@ -380,9 +468,10 @@ protected:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
Value *getOperand() const { return operands[0].getValue(); }
|
|
|
|
|
Value *getOperand() const { return User::getOperand(0); }
|
|
|
|
|
}; // class UnaryInst
|
|
|
|
|
|
|
|
|
|
//! Binary instruction, e.g., arithmatic, relation, logic, etc.
|
|
|
|
|
class BinaryInst : public Instruction {
|
|
|
|
|
friend class IRBuilder;
|
|
|
|
|
|
|
|
|
|
@ -395,42 +484,36 @@ protected:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
Value *getLhs() const { return operands[0].getValue(); }
|
|
|
|
|
Value *getRhs() const { return operands[1].getValue(); }
|
|
|
|
|
Value *getLhs() const { return getOperand(0); }
|
|
|
|
|
Value *getRhs() const { return getOperand(1); }
|
|
|
|
|
}; // class BinaryInst
|
|
|
|
|
|
|
|
|
|
class TerminatorInst : public Instruction {
|
|
|
|
|
protected:
|
|
|
|
|
using Instruction::Instruction;
|
|
|
|
|
}; // TerminatorInst
|
|
|
|
|
|
|
|
|
|
class ReturnInst : public TerminatorInst {
|
|
|
|
|
//! The return statement
|
|
|
|
|
class ReturnInst : public Instruction {
|
|
|
|
|
friend class IRBuilder;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
ReturnInst(Value *value = nullptr, BasicBlock *parent = nullptr)
|
|
|
|
|
: TerminatorInst(kReturn, Type::getVoidType(), parent, "") {
|
|
|
|
|
: Instruction(kReturn, Type::getVoidType(), parent, "") {
|
|
|
|
|
if (value)
|
|
|
|
|
addOperand(value);
|
|
|
|
|
}
|
|
|
|
|
}; // class ReturnInst
|
|
|
|
|
|
|
|
|
|
class BranchInst : public TerminatorInst {
|
|
|
|
|
protected:
|
|
|
|
|
using TerminatorInst::TerminatorInst;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
bool isUnconditional() const { return kind == kBr; }
|
|
|
|
|
bool isConditional() const { return kind == kCondBr; }
|
|
|
|
|
}; // class BranchInst
|
|
|
|
|
bool hasReturnValue() const { return not operands.empty(); }
|
|
|
|
|
Value *getReturnValue() const {
|
|
|
|
|
return hasReturnValue() ? getOperand(0) : nullptr;
|
|
|
|
|
}
|
|
|
|
|
}; // class ReturnInst
|
|
|
|
|
|
|
|
|
|
class UncondBrInst : public BranchInst {
|
|
|
|
|
//! Unconditional branch
|
|
|
|
|
class UncondBrInst : public Instruction {
|
|
|
|
|
friend class IRBuilder;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
UncondBrInst(BasicBlock *block, std::vector<Value *> args,
|
|
|
|
|
BasicBlock *parent = nullptr)
|
|
|
|
|
: BranchInst(kCondBr, Type::getVoidType(), parent, "") {
|
|
|
|
|
: Instruction(kCondBr, Type::getVoidType(), parent, "") {
|
|
|
|
|
assert(block->getNumArguments() == args.size());
|
|
|
|
|
addOperand(block);
|
|
|
|
|
addOperands(args);
|
|
|
|
|
@ -438,21 +521,22 @@ protected:
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
BasicBlock *getBlock() const {
|
|
|
|
|
return dynamic_cast<BasicBlock *>(getOperand(0).getValue());
|
|
|
|
|
return dynamic_cast<BasicBlock *>(getOperand(0));
|
|
|
|
|
}
|
|
|
|
|
auto getArguments() const {
|
|
|
|
|
return make_range(std::next(operands.begin()), operands.end());
|
|
|
|
|
}
|
|
|
|
|
}; // class UncondBrInst
|
|
|
|
|
|
|
|
|
|
class CondBrInst : public BranchInst {
|
|
|
|
|
//! Conditional branch
|
|
|
|
|
class CondBrInst : public Instruction {
|
|
|
|
|
friend class IRBuilder;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
CondBrInst(Value *condition, BasicBlock *thenBlock, BasicBlock *elseBlock,
|
|
|
|
|
const std::vector<Value *> &thenArgs,
|
|
|
|
|
const std::vector<Value *> &elseArgs, BasicBlock *parent = nullptr)
|
|
|
|
|
: BranchInst(kCondBr, Type::getVoidType(), parent, "") {
|
|
|
|
|
: Instruction(kCondBr, Type::getVoidType(), parent, "") {
|
|
|
|
|
assert(thenBlock->getNumArguments() == thenArgs.size() and
|
|
|
|
|
elseBlock->getNumArguments() == elseArgs.size());
|
|
|
|
|
addOperand(condition);
|
|
|
|
|
@ -464,10 +548,10 @@ protected:
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
BasicBlock *getThenBlock() const {
|
|
|
|
|
return dynamic_cast<BasicBlock *>(getOperand(0).getValue());
|
|
|
|
|
return dynamic_cast<BasicBlock *>(getOperand(0));
|
|
|
|
|
}
|
|
|
|
|
BasicBlock *getElseBlock() const {
|
|
|
|
|
return dynamic_cast<BasicBlock *>(getOperand(1).getValue());
|
|
|
|
|
return dynamic_cast<BasicBlock *>(getOperand(1));
|
|
|
|
|
}
|
|
|
|
|
auto getThenArguments() const {
|
|
|
|
|
auto begin = operands.begin() + 2;
|
|
|
|
|
@ -481,57 +565,53 @@ public:
|
|
|
|
|
}
|
|
|
|
|
}; // class CondBrInst
|
|
|
|
|
|
|
|
|
|
class MemoryInst : public Instruction {
|
|
|
|
|
protected:
|
|
|
|
|
using Instruction::Instruction;
|
|
|
|
|
}; // class MemoryInst
|
|
|
|
|
|
|
|
|
|
class AllocaInst : public MemoryInst {
|
|
|
|
|
//! Allocate memory for stack variables, used for non-global variable declartion
|
|
|
|
|
class AllocaInst : public Instruction {
|
|
|
|
|
friend class IRBuilder;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
AllocaInst(Type *type, const std::vector<Value *> &dims = {},
|
|
|
|
|
BasicBlock *parent = nullptr, const std::string &name = "")
|
|
|
|
|
: MemoryInst(kAlloca, type, parent, name) {
|
|
|
|
|
: Instruction(kAlloca, type, parent, name) {
|
|
|
|
|
addOperands(dims);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
int getNumDims() const { return getNumOperands(); }
|
|
|
|
|
auto &getDims() const { return operands; }
|
|
|
|
|
Value *getDim(int index) { return getOperand(index).getValue(); }
|
|
|
|
|
auto getDims() const { return getOperands(); }
|
|
|
|
|
Value *getDim(int index) { return getOperand(index); }
|
|
|
|
|
}; // class AllocaInst
|
|
|
|
|
|
|
|
|
|
class LoadInst : public MemoryInst {
|
|
|
|
|
//! Load a value from memory address specified by a pointer value
|
|
|
|
|
class LoadInst : public Instruction {
|
|
|
|
|
friend class IRBuilder;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
LoadInst(Value *pointer, const std::vector<Value *> &indices = {},
|
|
|
|
|
BasicBlock *parent = nullptr, const std::string &name = "")
|
|
|
|
|
: MemoryInst(
|
|
|
|
|
kLoad,
|
|
|
|
|
dynamic_cast<PointerType *>(pointer->getType())->getBaseType(),
|
|
|
|
|
parent, name) {
|
|
|
|
|
: Instruction(kLoad, pointer->getType()->as<PointerType>()->getBaseType(),
|
|
|
|
|
parent, name) {
|
|
|
|
|
addOperands(indices);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
int getNumIndices() const { return getNumOperands() - 1; }
|
|
|
|
|
Value *getPointer() const { return operands.front().getValue(); }
|
|
|
|
|
Value *getPointer() const { return getOperand(0); }
|
|
|
|
|
auto getIndices() const {
|
|
|
|
|
return make_range(std::next(operands.begin()), operands.end());
|
|
|
|
|
return make_range(std::next(operand_begin()), operand_end());
|
|
|
|
|
}
|
|
|
|
|
Value *getIndex(int index) const { return getOperand(index + 1).getValue(); }
|
|
|
|
|
Value *getIndex(int index) const { return getOperand(index + 1); }
|
|
|
|
|
}; // class LoadInst
|
|
|
|
|
|
|
|
|
|
class StoreInst : public MemoryInst {
|
|
|
|
|
//! Store a value to memory address specified by a pointer value
|
|
|
|
|
class StoreInst : public Instruction {
|
|
|
|
|
friend class IRBuilder;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
StoreInst(Value *value, Value *pointer,
|
|
|
|
|
const std::vector<Value *> &indices = {},
|
|
|
|
|
BasicBlock *parent = nullptr, const std::string &name = "")
|
|
|
|
|
: MemoryInst(kStore, Type::getVoidType(), parent, name) {
|
|
|
|
|
: Instruction(kStore, Type::getVoidType(), parent, name) {
|
|
|
|
|
addOperand(value);
|
|
|
|
|
addOperand(pointer);
|
|
|
|
|
addOperands(indices);
|
|
|
|
|
@ -539,15 +619,16 @@ protected:
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
int getNumIndices() const { return getNumOperands() - 2; }
|
|
|
|
|
Value *getValue() const { return operands[0].getValue(); }
|
|
|
|
|
Value *getPointer() const { return operands[1].getValue(); }
|
|
|
|
|
Value *getValue() const { return getOperand(0); }
|
|
|
|
|
Value *getPointer() const { return getOperand(1); }
|
|
|
|
|
auto getIndices() const {
|
|
|
|
|
return make_range(operands.begin() + 2, operands.end());
|
|
|
|
|
return make_range(operand_begin() + 2, operand_end());
|
|
|
|
|
}
|
|
|
|
|
Value *getIndex(int index) const { return getOperand(index + 2).getValue(); }
|
|
|
|
|
Value *getIndex(int index) const { return getOperand(index + 2); }
|
|
|
|
|
}; // class StoreInst
|
|
|
|
|
|
|
|
|
|
class Module;
|
|
|
|
|
//! Function definition
|
|
|
|
|
class Function : public Value {
|
|
|
|
|
friend class Module;
|
|
|
|
|
|
|
|
|
|
@ -566,12 +647,12 @@ protected:
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
Type *getReturnType() const {
|
|
|
|
|
return dynamic_cast<FunctionType *>(getType())->getReturnType();
|
|
|
|
|
return getType()->as<FunctionType>()->getReturnType();
|
|
|
|
|
}
|
|
|
|
|
auto &getParamTypes() const {
|
|
|
|
|
return dynamic_cast<FunctionType *>(getType())->getParamTypes();
|
|
|
|
|
auto getParamTypes() const {
|
|
|
|
|
return getType()->as<FunctionType>()->getParamTypes();
|
|
|
|
|
}
|
|
|
|
|
block_list &getBasicBlocks() { return blocks; }
|
|
|
|
|
auto getBasicBlocks() { return make_range(blocks); }
|
|
|
|
|
BasicBlock *getEntryBlock() { return blocks.front().get(); }
|
|
|
|
|
BasicBlock *addBasicBlock(const std::string &name = "") {
|
|
|
|
|
blocks.emplace_back(new BasicBlock(this, name));
|
|
|
|
|
@ -584,24 +665,31 @@ public:
|
|
|
|
|
}
|
|
|
|
|
}; // class Function
|
|
|
|
|
|
|
|
|
|
//! Global value declared at file scope
|
|
|
|
|
class GlobalValue : public User {
|
|
|
|
|
friend class Module;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
Module *parent;
|
|
|
|
|
bool hasInit;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
GlobalValue(Module *parent, Type *type, const std::string &name,
|
|
|
|
|
const std::vector<Value *> &dims = {})
|
|
|
|
|
: User(type, name), parent(parent) {
|
|
|
|
|
const std::vector<Value *> &dims = {}, Value *init = nullptr)
|
|
|
|
|
: User(type, name), parent(parent), hasInit(init) {
|
|
|
|
|
assert(type->isPointer());
|
|
|
|
|
addOperands(dims);
|
|
|
|
|
if (init)
|
|
|
|
|
addOperand(init);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
int getNumDims() const { return getNumOperands(); }
|
|
|
|
|
Value *getDim(int index) { return getOperand(index).getValue(); }
|
|
|
|
|
Value *init() const { return hasInit ? operands.back().getValue() : nullptr; }
|
|
|
|
|
int getNumDims() const { return getNumOperands() - (hasInit ? 1 : 0); }
|
|
|
|
|
Value *getDim(int index) { return getOperand(index); }
|
|
|
|
|
}; // class GlobalValue
|
|
|
|
|
|
|
|
|
|
//! IR unit for representing a SysY compile unit
|
|
|
|
|
class Module {
|
|
|
|
|
protected:
|
|
|
|
|
std::map<std::string, std::unique_ptr<Function>> functions;
|
|
|
|
|
@ -639,16 +727,7 @@ public:
|
|
|
|
|
}
|
|
|
|
|
}; // class Module
|
|
|
|
|
|
|
|
|
|
inline CallInst::CallInst(Function *callee, const std::vector<Value *> args,
|
|
|
|
|
BasicBlock *parent, const std::string &name)
|
|
|
|
|
: Instruction(kCall, callee->getReturnType(), parent, name) {
|
|
|
|
|
addOperand(callee);
|
|
|
|
|
for (auto arg : args)
|
|
|
|
|
addOperand(arg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline Function *CallInst::getCallee() {
|
|
|
|
|
return dynamic_cast<Function *>(getOperand(0).getValue());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* @}
|
|
|
|
|
*/
|
|
|
|
|
} // namespace sysy
|