Skip to content

Commit

Permalink
Introduce operations tree as a new optree module (#110)
Browse files Browse the repository at this point in the history
An operations tree (optree) will be used as *an alternative backend
representation for a program* instead of a syntax tree (ast). The latter
is relatively easy to build (since it is bijective with a program source
code), but hard to analyze. An optree, conversely, is intended to be
walked through and used for a program analysis, but it is also can be
built easily from a syntax tree.

Each node of an optree represents an abstract operation that comes from
a language domain. *Operation* may have multiple operands (input
values), results (output values), and nested operations of any kind
inside its body. *Value*, along with a parent operation, holds its data
type and a list of uses (to memoize which operations has the given value
as an operand).

It is important to notice that values are simple SSA registers, which
means that being defined once, it cannot be redefined, and this fact
makes it easy to track definition-use chain per each value. This concept
is widely used in such frameworks like LLVM IR, MLIR.
  • Loading branch information
vla5924 authored Apr 6, 2024
1 parent 958aa61 commit c140a1f
Show file tree
Hide file tree
Showing 19 changed files with 1,600 additions and 1 deletion.
1 change: 0 additions & 1 deletion .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ Checks: >
bugprone-throw-keyword-missing,
bugprone-unused-raii,
bugprone-unused-return-value,
cppcoreguidelines-macro-usage,
llvm-include-order,
llvm-namespace-comment,
misc-confusable-identifiers,
Expand Down
265 changes: 265 additions & 0 deletions compiler/include/compiler/optree/adaptors.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
#pragma once

#include <cstdint>
#include <string>
#include <vector>

#include "compiler/optree/base_adaptor.hpp"
#include "compiler/optree/definitions.hpp"
#include "compiler/optree/operation.hpp"
#include "compiler/optree/types.hpp"
#include "compiler/optree/value.hpp"

namespace optree {

// ----------------------------------------------------------------------------
// Fundamental operations
// ----------------------------------------------------------------------------

struct ModuleOp;
struct FunctionOp;
struct FunctionCallOp;
struct ReturnOp;
struct ConstantOp;

struct ModuleOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "Module")

void init();

template <typename AdaptorType>
requires std::convertible_to<decltype(std::declval<AdaptorType>().name()), std::string>
AdaptorType lookup(const std::string &name) const {
for (const auto &childOp : op->body) {
if (AdaptorType adapted = Operation::as<AdaptorType>(childOp)) {
if (adapted.name() == name)
return adapted;
}
}
return {};
}
};

struct FunctionOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "Function")

void init(const std::string &name, const Type::Ptr &funcType);

OPTREE_ADAPTOR_ATTRIBUTE(name, setName, std::string, 0)
OPTREE_ADAPTOR_ATTRIBUTE_TYPE(type, FunctionType, 1)
};

struct FunctionCallOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "FunctionCall")

void init(const std::string &name, const Type::Ptr &resultType, const std::vector<Value::Ptr> &arguments);
void init(const FunctionOp &callee, const std::vector<Value::Ptr> &arguments);

OPTREE_ADAPTOR_ATTRIBUTE(name, setName, std::string, 0)
OPTREE_ADAPTOR_RESULT(result, 0)
};

struct ReturnOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "Return")

void init();
void init(const Value::Ptr &value);

OPTREE_ADAPTOR_OPERAND(value, setValue, 0)
};

struct ConstantOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "Constant")

void init(const Type::Ptr &type, int64_t value);
void init(const Type::Ptr &type, bool value);
void init(const Type::Ptr &type, double value);
void init(const Type::Ptr &type, const std::string &value);

OPTREE_ADAPTOR_ATTRIBUTE_OPAQUE(value, 0)
OPTREE_ADAPTOR_RESULT(result, 0)
};

// ----------------------------------------------------------------------------
// Computation operations
// ----------------------------------------------------------------------------

struct BinaryOp;
struct ArithBinaryOp;
struct LogicBinaryOp;
struct UnaryOp;
struct ArithCastOp;
struct LogicUnaryOp;

struct BinaryOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "Binary")

void init(const Type::Ptr &resultType, const Value::Ptr &lhs, const Value::Ptr &rhs);

OPTREE_ADAPTOR_OPERAND(lhs, setLhs, 0)
OPTREE_ADAPTOR_OPERAND(rhs, setRhs, 1)
OPTREE_ADAPTOR_RESULT(result, 0)
};

struct ArithBinaryOp : BinaryOp {
OPTREE_ADAPTOR_HELPER(BinaryOp, "ArithBinary")

void init(ArithBinOpKind kind, const Type::Ptr &resultType, const Value::Ptr &lhs, const Value::Ptr &rhs);
void init(ArithBinOpKind kind, const Value::Ptr &lhs, const Value::Ptr &rhs);

OPTREE_ADAPTOR_ATTRIBUTE(kind, setKind, ArithBinOpKind, 0)
};

struct LogicBinaryOp : BinaryOp {
OPTREE_ADAPTOR_HELPER(BinaryOp, "LogicBinary")

void init(LogicBinOpKind kind, const Value::Ptr &lhs, const Value::Ptr &rhs);

OPTREE_ADAPTOR_ATTRIBUTE(kind, setKind, LogicBinOpKind, 0)
};

struct UnaryOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "Unary")

void init(const Type::Ptr &resultType, const Value::Ptr &value);

OPTREE_ADAPTOR_OPERAND(value, setValue, 0)
OPTREE_ADAPTOR_RESULT(result, 0)
};

struct ArithCastOp : UnaryOp {
OPTREE_ADAPTOR_HELPER(UnaryOp, "ArithCast")

void init(ArithCastOpKind kind, const Type::Ptr &resultType, const Value::Ptr &value);

OPTREE_ADAPTOR_ATTRIBUTE(kind, setKind, ArithCastOpKind, 0)
};

struct LogicUnaryOp : UnaryOp {
OPTREE_ADAPTOR_HELPER(UnaryOp, "LogicUnary")

void init(LogicUnaryOpKind kind, const Value::Ptr &value);

OPTREE_ADAPTOR_ATTRIBUTE(kind, setKind, LogicUnaryOpKind, 0)
};

// ----------------------------------------------------------------------------
// Memory access operations
// ----------------------------------------------------------------------------

struct AllocateOp;
struct LoadOp;
struct StoreOp;

struct AllocateOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "Allocate")

void init(const Type::Ptr &type);

OPTREE_ADAPTOR_RESULT(result, 0)
};

struct LoadOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "Load")

void init(const Type::Ptr &resultType, const Value::Ptr &src);
void init(const Value::Ptr &src);

OPTREE_ADAPTOR_OPERAND(src, setSrc, 0)
OPTREE_ADAPTOR_RESULT(result, 0)
};

struct StoreOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "Store")

void init(const Value::Ptr &dst, const Value::Ptr &valueToStore);

OPTREE_ADAPTOR_OPERAND(dst, setDst, 0)
OPTREE_ADAPTOR_OPERAND(valueToStore, setValueToStore, 1)
};

// ----------------------------------------------------------------------------
// Control flow operations
// ----------------------------------------------------------------------------

struct IfOp;
struct ThenOp;
struct ElseOp;
struct WhileOp;
struct ConditionOp;
struct ForOp;

struct IfOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "If")

void init(const Value::Ptr &cond, bool withElse = false);

OPTREE_ADAPTOR_OPERAND(cond, setCond, 0);

ThenOp thenOp() const;
ElseOp elseOp() const;
};

struct ThenOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "Then")

void init();
};

struct ElseOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "Else")

void init();
};

struct WhileOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "While")

void init();

ConditionOp conditionOp() const;
};

struct ConditionOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "Condition")

void init();

Value::Ptr terminator() const;
};

struct ForOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "For")

void init(const Type::Ptr &iteratorType, const Value::Ptr &start, const Value::Ptr &stop, const Value::Ptr &step);

OPTREE_ADAPTOR_OPERAND(start, setStart, 0)
OPTREE_ADAPTOR_OPERAND(stop, setStop, 1)
OPTREE_ADAPTOR_OPERAND(step, setStep, 2)
OPTREE_ADAPTOR_INWARD(iterator, 0)
};

// ----------------------------------------------------------------------------
// Special operations
// ----------------------------------------------------------------------------

struct InputOp;
struct PrintOp;

struct InputOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "Input")

void init(const Type::Ptr &inputType);

OPTREE_ADAPTOR_RESULT(value, 0)
};

struct PrintOp : Adaptor {
OPTREE_ADAPTOR_HELPER(Adaptor, "Print")

void init(const Value::Ptr &valueToPrint);

OPTREE_ADAPTOR_OPERAND(value, setValue, 0)
};

} // namespace optree
74 changes: 74 additions & 0 deletions compiler/include/compiler/optree/attribute.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#pragma once

#include <cstdint>
#include <memory>
#include <ostream>
#include <string>
#include <variant>

#include "compiler/optree/definitions.hpp"
#include "compiler/optree/types.hpp"

namespace optree {

struct Attribute {
std::variant<std::monostate, int64_t, double, bool, std::string, Type::Ptr, ArithBinOpKind, ArithCastOpKind,
LogicBinOpKind, LogicUnaryOpKind>
storage;

Attribute() = default;
Attribute(const Attribute &) = default;
Attribute(Attribute &&) = default;
~Attribute() = default;

template <typename VariantType>
explicit Attribute(const VariantType &value) : storage(value){};

template <typename VariantType>
bool is() const noexcept {
return std::holds_alternative<VariantType>(storage);
}

template <typename ConcreteType>
bool isType() const {
return is<Type::Ptr>() && as<Type::Ptr>()->is<ConcreteType>();
}

template <typename VariantType>
const VariantType &as() const {
return std::get<VariantType>(storage);
}

template <typename VariantType>
VariantType &as() {
return std::get<VariantType>(storage);
}

template <typename ConcreteType>
const ConcreteType &asType() const {
return as<Type::Ptr>()->as<ConcreteType>();
}

template <typename VariantType>
void set(const VariantType &value) {
storage = value;
}

template <typename VariantType>
requires std::derived_from<VariantType, Type>
void set(const std::shared_ptr<const VariantType> &value) {
storage = std::dynamic_pointer_cast<const Type>(value);
}

operator bool() const {
return is<std::monostate>();
}

void clear() {
storage = std::monostate();
}

void dump(std::ostream &stream) const;
};

} // namespace optree
Loading

0 comments on commit c140a1f

Please sign in to comment.