Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce semantizer module for an operation tree verification #124

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include "compiler/optree/operation.hpp"
#include "compiler/optree/program.hpp"

namespace optree {
namespace semantizer {

class Semantizer {
public:
Semantizer() = delete;
Semantizer(const Semantizer &) = delete;
Semantizer(Semantizer &&) = delete;
~Semantizer() = delete;

static void process(const Program &program);
static void process(const Operation::Ptr &op);
};

} // namespace semantizer
} // namespace optree
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#pragma once

#include <optional>
#include <string>
#include <unordered_map>

#include "compiler/optree/adaptors.hpp"
#include "compiler/optree/operation.hpp"
#include "compiler/utils/error_buffer.hpp"

#include "compiler/backend/optree/semantizer/semantizer_error.hpp"

namespace optree {
namespace semantizer {

struct SemantizerContext {
ErrorBuffer errors;
std::unordered_map<std::string, FunctionOp> functions;

std::optional<FunctionOp> findFunction(const std::string &name) const {
auto it = functions.find(name);
if (it == functions.end())
return {};
return {it->second};
}

void pushError(const Operation::Ptr &op, const std::string &message = {}) {
errors.push<SemantizerError>(op, message);
}

SemantizerError &pushOpError(const Operation::Ptr &op, const std::string &message = {}) {
return errors.push<SemantizerError>(op, std::string(op->name) + " operation " + message);
}
};

} // namespace semantizer
} // namespace optree
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

#include <string>

#include "compiler/optree/operation.hpp"
#include "compiler/utils/base_error.hpp"

namespace optree {
namespace semantizer {

class SemantizerError : public BaseError {
public:
using BaseError::BaseError;

explicit SemantizerError(const Operation::Ptr &op, const std::string &message = {}) : BaseError(op->ref, message){};
~SemantizerError() override = default;
};

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

#include <algorithm>

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

#include "compiler/backend/optree/semantizer/semantizer_context.hpp"

namespace optree {
namespace semantizer {

class TraitVerifier {
const Operation::Ptr &op;
SemantizerContext &ctx;
bool acc;

public:
TraitVerifier() = delete;
TraitVerifier(const TraitVerifier &) = delete;
TraitVerifier(TraitVerifier &&) = delete;
~TraitVerifier() = default;

TraitVerifier(const Operation::Ptr &op, SemantizerContext &ctx) : op(op), ctx(ctx), acc(true){};

bool verified() const {
return acc;
}

operator bool() const {
return verified();
}

bool fail() {
acc = false;
return verified();
}

template <typename Trait, typename... Args>
requires std::same_as<decltype(Trait::verify(op, ctx, std::declval<Args>()...)), bool>
TraitVerifier &verify(Args... args) {
if (acc)
acc &= Trait::verify(op, ctx, std::forward<Args>(args)...);
return *this;
}
};

struct HasOperands {
static bool verify(const Operation::Ptr &op, SemantizerContext &ctx, size_t numOperands) {

Check failure

Code scanning / clang-tidy

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors] Error

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors]
if (op->numOperands() != numOperands) {
ctx.pushOpError(op) << "must have " << numOperands << " operands";
return false;
}
return true;
}
};

struct HasOperandsOfType {
static bool verify(const Operation::Ptr &op, SemantizerContext &ctx, size_t numOperands, const Type::Ptr &type) {

Check failure

Code scanning / clang-tidy

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors] Error

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors]
if (op->numOperands() == numOperands &&
std::all_of(op->operands.begin(), op->operands.end(),
[&](const Value::Ptr &operand) { return operand->hasType(type); }))

Check failure

Code scanning / clang-tidy

no header providing "optree::Value" is directly included [misc-include-cleaner,-warnings-as-errors] Error

no header providing "optree::Value" is directly included [misc-include-cleaner,-warnings-as-errors]
return true;
ctx.pushOpError(op) << "must have " << numOperands << " operands of " << type;
return false;
}
};

struct HasResults {
static bool verify(const Operation::Ptr &op, SemantizerContext &ctx, size_t numResults) {

Check failure

Code scanning / clang-tidy

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors] Error

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors]
if (op->numResults() == numResults)
return true;
ctx.pushOpError(op) << "must have " << numResults << " results";
return false;
}
};

struct HasResultOfType {
static bool verify(const Operation::Ptr &op, SemantizerContext &ctx, const Type::Ptr &type) {
if (op->numResults() == 1 && op->result(0)->hasType(type))
return true;
ctx.pushOpError(op) << "must have one result of " << type;
return false;
}
};

struct HasInwards {
static bool verify(const Operation::Ptr &op, SemantizerContext &ctx, size_t numInwards) {

Check failure

Code scanning / clang-tidy

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors] Error

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors]
if (op->numInwards() == numInwards)
return true;
ctx.pushOpError(op) << "must have " << numInwards << " inwards";
return false;
}
};

struct HasInwardsOfType {
static bool verify(const Operation::Ptr &op, SemantizerContext &ctx, size_t numInwards, const Type::Ptr &type) {

Check failure

Code scanning / clang-tidy

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors] Error

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors]
if (op->numInwards() == numInwards &&
std::all_of(op->inwards.begin(), op->inwards.end(),
[&](const Value::Ptr &inward) { return inward->hasType(type); }))

Check failure

Code scanning / clang-tidy

no header providing "optree::Value" is directly included [misc-include-cleaner,-warnings-as-errors] Error

no header providing "optree::Value" is directly included [misc-include-cleaner,-warnings-as-errors]
return true;
ctx.pushOpError(op) << "must have " << numInwards << " inwards of " << type;
return false;
}
};

struct HasAttributes {
static bool verify(const Operation::Ptr &op, SemantizerContext &ctx, size_t numAttrs) {

Check failure

Code scanning / clang-tidy

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors] Error

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors]
if (op->numAttrs() == numAttrs)
return true;
ctx.pushOpError(op) << "must have " << numAttrs << " attributes";
return false;
}
};

template <typename T>
struct HasNthAttrOfType {
static bool verify(const Operation::Ptr &op, SemantizerContext &ctx, size_t index) {

Check failure

Code scanning / clang-tidy

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors] Error

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors]
if constexpr (std::is_base_of_v<Type, T>) {
if (op->attr(index).isType<T>())
return true;
} else {
if (op->attr(index).is<T>())
return true;
}
ctx.pushOpError(op) << "must have attribute #" << index << " of other type";
return false;
}
};

} // namespace semantizer
} // namespace optree
3 changes: 3 additions & 0 deletions compiler/include/compiler/optree/adaptors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@

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

size_t numArguments() const;

Check failure

Code scanning / clang-tidy

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors] Error

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors]
size_t numResults() const;

Check failure

Code scanning / clang-tidy

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors] Error

no header providing "size_t" is directly included [misc-include-cleaner,-warnings-as-errors]
};

struct FunctionCallOp : Adaptor {
Expand Down
12 changes: 10 additions & 2 deletions compiler/include/compiler/optree/base_adaptor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@
#include "compiler/utils/source_ref.hpp"

#define OPTREE_ADAPTOR_HELPER(BASE_ADAPTOR_CLASS, OPERATION_NAME) \
using BASE_ADAPTOR_CLASS::BASE_ADAPTOR_CLASS; \
using BASE_ADAPTOR_CLASS::operator bool; \
using Base = BASE_ADAPTOR_CLASS; \
using Base::Base; \
using Base::operator bool; \
static std::string_view getOperationName() { \
return (OPERATION_NAME); \
} \
static Operation::SpecId getSpecId() { \
static char specId = 0; \
return &specId; \
} \
static bool implementsSpecById(Operation::SpecId specId) { \
return specId == getSpecId() || Base::implementsSpecById(specId); \
}

#define OPTREE_ADAPTOR_ATTRIBUTE(GET_NAME, SET_NAME, TYPE, NUMBER) \
Expand Down Expand Up @@ -89,6 +93,10 @@ struct Adaptor {
static Operation::SpecId getSpecId() {
return nullptr;
}

static bool implementsSpecById(Operation::SpecId) {
return false;
}
};

} // namespace optree
2 changes: 1 addition & 1 deletion compiler/include/compiler/optree/operation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ struct Operation : public std::enable_shared_from_this<Operation> {

template <typename AdaptorType>
bool is() const {
return specId == AdaptorType::getSpecId();
return AdaptorType::implementsSpecById(specId);
}

template <typename AdaptorType>
Expand Down
30 changes: 27 additions & 3 deletions compiler/include/compiler/utils/base_error.hpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,41 @@
#pragma once

#include <sstream>
#include <stdexcept>
#include <string>
#include <type_traits>

Check failure

Code scanning / clang-tidy

included header type_traits is not used directly [misc-include-cleaner,-warnings-as-errors] Error

included header type_traits is not used directly [misc-include-cleaner,-warnings-as-errors]

#include "compiler/utils/source_ref.hpp"

class BaseError : public std::exception {
std::string what_str;
std::stringstream messageStr;

public:
BaseError(const utils::SourceRef &ref, const std::string &message);
BaseError(const std::string &message);
BaseError(BaseError &&) = default;
~BaseError() = default;

BaseError(const BaseError &other);
explicit BaseError(const utils::SourceRef &ref, const std::string &message = {});
explicit BaseError(const std::string &message = {});

virtual const char *what() const noexcept;

template <typename T>
auto &operator<<(const T &value) {
return messageStr << value;
}

template <typename T>
requires std::same_as<decltype(std::declval<T>().dump(messageStr)), void>
std::stringstream &operator<<(const T &value) {
value.dump(messageStr);
return messageStr;
}

template <typename T>
requires std::same_as<decltype(std::declval<T>()->dump(messageStr)), void>
std::stringstream &operator<<(const T &value) {
value->dump(messageStr);
return messageStr;
}
};
5 changes: 3 additions & 2 deletions compiler/include/compiler/utils/error_buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ class ErrorBuffer : public std::exception {
~ErrorBuffer() = default;

template <typename ErrorT, typename... Args>
void push(Args... args) {
buffer.emplace_back(std::make_shared<ErrorT>(args...));
ErrorT &push(Args... args) {
auto error = buffer.emplace_back(std::make_shared<ErrorT>(args...));
return dynamic_cast<ErrorT &>(*error.get());
}

std::string message() const {
Expand Down
Loading
Loading