#ifndef wasm_ir_match_h
#define wasm_ir_match_h
#include "ir/abstract.h"
#include "wasm.h"
namespace wasm::Match {
template<class Matcher> inline bool matches(Expression* expr, Matcher matcher) {
return matcher.matches(expr);
}
namespace Internal {
struct unused_t {};
template<class Kind> struct KindTypeRegistry {
using matched_t = void;
using data_t = unused_t;
};
template<class Kind> struct MatchTypes {
using matched_t = typename std::conditional_t<
std::is_base_of<Expression, std::remove_pointer_t<Kind>>::value,
Kind,
typename KindTypeRegistry<Kind>::matched_t>;
static constexpr bool isExpr =
std::is_base_of<Expression, std::remove_pointer_t<matched_t>>::value;
using candidate_t =
typename std::conditional_t<isExpr, Expression*, matched_t>;
};
template<class Kind> using matched_t = typename MatchTypes<Kind>::matched_t;
template<class Kind> using candidate_t = typename MatchTypes<Kind>::candidate_t;
template<class Kind> using data_t = typename KindTypeRegistry<Kind>::data_t;
template<class Kind>
using enable_if_castable_t = typename std::enable_if<
std::is_base_of<Expression, std::remove_pointer_t<matched_t<Kind>>>::value &&
!std::is_same<Expression*, matched_t<Kind>>::value,
int>::type;
template<class Kind>
using enable_if_not_castable_t = typename std::enable_if<
!std::is_base_of<Expression, std::remove_pointer_t<matched_t<Kind>>>::value ||
std::is_same<Expression*, matched_t<Kind>>::value,
int>::type;
template<class Kind, enable_if_castable_t<Kind> = 0>
inline bool dynCastCandidate(candidate_t<Kind> candidate,
matched_t<Kind>& out) {
out = candidate->template dynCast<std::remove_pointer_t<matched_t<Kind>>>();
return out != nullptr;
}
template<class Kind, enable_if_not_castable_t<Kind> = 0>
inline bool dynCastCandidate(candidate_t<Kind> candidate,
matched_t<Kind>& out) {
out = candidate;
return true;
}
template<class Kind> struct MatchSelf {
bool operator()(matched_t<Kind>, data_t<Kind>) { return true; }
};
template<class Kind> struct NumComponents {
static constexpr size_t value = 0;
};
template<class Kind, int pos> struct GetComponent;
template<class...> struct SubMatchers {};
template<class CurrMatcher, class... NextMatchers>
struct SubMatchers<CurrMatcher, NextMatchers...> {
CurrMatcher curr;
SubMatchers<NextMatchers...> next;
SubMatchers(CurrMatcher curr, NextMatchers... next)
: curr(curr), next(next...){};
};
template<class Kind, int pos, class CurrMatcher = void, class... NextMatchers>
struct Components {
static inline bool
match(matched_t<Kind> candidate,
SubMatchers<CurrMatcher, NextMatchers...>& matchers) {
return matchers.curr.matches(GetComponent<Kind, pos>{}(candidate)) &&
Components<Kind, pos + 1, NextMatchers...>::match(candidate,
matchers.next);
}
};
template<class Kind, int pos> struct Components<Kind, pos> {
static_assert(pos == NumComponents<Kind>::value,
"Unexpected number of submatchers");
static inline bool match(matched_t<Kind>, SubMatchers<>) {
return true;
}
};
template<class Kind, class... Matchers> struct Matcher {
matched_t<Kind>* binder;
data_t<Kind> data;
SubMatchers<Matchers...> submatchers;
Matcher(matched_t<Kind>* binder, data_t<Kind> data, Matchers... submatchers)
: binder(binder), data(data), submatchers(submatchers...) {}
inline bool matches(candidate_t<Kind> candidate) {
matched_t<Kind> casted;
if (dynCastCandidate<Kind>(candidate, casted)) {
if (binder != nullptr) {
*binder = casted;
}
return MatchSelf<Kind>{}(casted, data) &&
Components<Kind, 0, Matchers...>::match(casted, submatchers);
}
return false;
}
};
template<class T> struct AnyKind {};
template<class T> struct KindTypeRegistry<AnyKind<T>> {
using matched_t = T;
using data_t = unused_t;
};
template<class T> inline decltype(auto) Any(T* binder) {
return Matcher<AnyKind<T>>(binder, {});
}
template<class T> struct ExactKind {};
template<class T> struct KindTypeRegistry<ExactKind<T>> {
using matched_t = T;
using data_t = T;
};
template<class T> struct MatchSelf<ExactKind<T>> {
bool operator()(T self, T expected) { return self == expected; }
};
template<class T> inline decltype(auto) Exact(T* binder, T data) {
return Matcher<ExactKind<T>>(binder, data);
}
struct BoolLK {
static bool matchType(Literal lit) {
return lit.type == Type::i32 && (uint32_t)lit.geti32() <= 1U;
}
static int32_t getVal(Literal lit) { return lit.geti32(); }
};
struct I32LK {
static bool matchType(Literal lit) { return lit.type == Type::i32; }
static int32_t getVal(Literal lit) { return lit.geti32(); }
};
struct I64LK {
static bool matchType(Literal lit) { return lit.type == Type::i64; }
static int64_t getVal(Literal lit) { return lit.geti64(); }
};
struct IntLK {
static bool matchType(Literal lit) { return lit.type.isInteger(); }
static int64_t getVal(Literal lit) { return lit.getInteger(); }
};
struct F32LK {
static bool matchType(Literal lit) { return lit.type == Type::f32; }
static float getVal(Literal lit) { return lit.getf32(); }
};
struct F64LK {
static bool matchType(Literal lit) { return lit.type == Type::f64; }
static double getVal(Literal lit) { return lit.getf64(); }
};
struct FloatLK {
static bool matchType(Literal lit) { return lit.type.isFloat(); }
static double getVal(Literal lit) { return lit.getFloat(); }
};
template<class T> struct LitKind {};
template<class T> struct KindTypeRegistry<LitKind<T>> {
using matched_t = Literal;
using data_t = unused_t;
};
template<class T> struct MatchSelf<LitKind<T>> {
bool operator()(Literal lit, unused_t) { return T::matchType(lit); }
};
template<class T> struct NumComponents<LitKind<T>> {
static constexpr size_t value = 1;
};
template<class T> struct GetComponent<LitKind<T>, 0> {
decltype(auto) operator()(Literal lit) { return T::getVal(lit); }
};
template<class S> inline decltype(auto) BoolLit(Literal* binder, S&& s) {
return Matcher<LitKind<BoolLK>, S>(binder, {}, s);
}
template<class S> inline decltype(auto) I32Lit(Literal* binder, S&& s) {
return Matcher<LitKind<I32LK>, S>(binder, {}, s);
}
template<class S> inline decltype(auto) I64Lit(Literal* binder, S&& s) {
return Matcher<LitKind<I64LK>, S>(binder, {}, s);
}
template<class S> inline decltype(auto) IntLit(Literal* binder, S&& s) {
return Matcher<LitKind<IntLK>, S>(binder, {}, s);
}
template<class S> inline decltype(auto) F32Lit(Literal* binder, S&& s) {
return Matcher<LitKind<F32LK>, S>(binder, {}, s);
}
template<class S> inline decltype(auto) F64Lit(Literal* binder, S&& s) {
return Matcher<LitKind<F64LK>, S>(binder, {}, s);
}
template<class S> inline decltype(auto) FloatLit(Literal* binder, S&& s) {
return Matcher<LitKind<FloatLK>, S>(binder, {}, s);
}
struct NumberLitKind {};
template<> struct KindTypeRegistry<NumberLitKind> {
using matched_t = Literal;
using data_t = int32_t;
};
template<> struct MatchSelf<NumberLitKind> {
bool operator()(Literal lit, int32_t expected) {
return lit.type.isNumber() &&
Literal::makeFromInt32(expected, lit.type) == lit;
}
};
inline decltype(auto) NumberLit(Literal* binder, int32_t expected) {
return Matcher<NumberLitKind>(binder, expected);
}
template<> struct NumComponents<Const*> { static constexpr size_t value = 1; };
template<> struct GetComponent<Const*, 0> {
Literal operator()(Const* c) { return c->value; }
};
template<class S> inline decltype(auto) ConstMatcher(Const** binder, S&& s) {
return Matcher<Const*, S>(binder, {}, s);
}
template<> struct NumComponents<Unary*> { static constexpr size_t value = 2; };
template<> struct GetComponent<Unary*, 0> {
UnaryOp operator()(Unary* curr) { return curr->op; }
};
template<> struct GetComponent<Unary*, 1> {
Expression* operator()(Unary* curr) { return curr->value; }
};
struct UnaryOpK {
using Op = UnaryOp;
static UnaryOp getOp(Type, Op op) { return op; }
};
struct AbstractUnaryOpK {
using Op = Abstract::Op;
static UnaryOp getOp(Type type, Abstract::Op op) {
return Abstract::getUnary(type, op);
}
};
template<class T> struct UnaryOpKind {};
template<class T> struct KindTypeRegistry<UnaryOpKind<T>> {
using matched_t = Unary*;
using data_t = typename T::Op;
};
template<class T> struct MatchSelf<UnaryOpKind<T>> {
bool operator()(Unary* curr, typename T::Op op) {
return curr->op == T::getOp(curr->value->type, op);
}
};
template<class T> struct NumComponents<UnaryOpKind<T>> {
static constexpr size_t value = 1;
};
template<class T> struct GetComponent<UnaryOpKind<T>, 0> {
Expression* operator()(Unary* curr) { return curr->value; }
};
template<class S1, class S2>
inline decltype(auto) UnaryMatcher(Unary** binder, S1&& s1, S2&& s2) {
return Matcher<Unary*, S1, S2>(binder, {}, s1, s2);
}
template<class S>
inline decltype(auto) UnaryOpMatcher(Unary** binder, UnaryOp op, S&& s) {
return Matcher<UnaryOpKind<UnaryOpK>, S>(binder, op, s);
}
template<class S>
inline decltype(auto)
AbstractUnaryOpMatcher(Unary** binder, Abstract::Op op, S&& s) {
return Matcher<UnaryOpKind<AbstractUnaryOpK>, S>(binder, op, s);
}
template<> struct NumComponents<Binary*> { static constexpr size_t value = 3; };
template<> struct GetComponent<Binary*, 0> {
BinaryOp operator()(Binary* curr) { return curr->op; }
};
template<> struct GetComponent<Binary*, 1> {
Expression* operator()(Binary* curr) { return curr->left; }
};
template<> struct GetComponent<Binary*, 2> {
Expression* operator()(Binary* curr) { return curr->right; }
};
struct BinaryOpK {
using Op = BinaryOp;
static BinaryOp getOp(Type, Op op) { return op; }
};
struct AbstractBinaryOpK {
using Op = Abstract::Op;
static BinaryOp getOp(Type type, Abstract::Op op) {
return Abstract::getBinary(type, op);
}
};
template<class T> struct BinaryOpKind {};
template<class T> struct KindTypeRegistry<BinaryOpKind<T>> {
using matched_t = Binary*;
using data_t = typename T::Op;
};
template<class T> struct MatchSelf<BinaryOpKind<T>> {
bool operator()(Binary* curr, typename T::Op op) {
return curr->op == T::getOp(curr->left->type, op);
}
};
template<class T> struct NumComponents<BinaryOpKind<T>> {
static constexpr size_t value = 2;
};
template<class T> struct GetComponent<BinaryOpKind<T>, 0> {
Expression* operator()(Binary* curr) { return curr->left; }
};
template<class T> struct GetComponent<BinaryOpKind<T>, 1> {
Expression* operator()(Binary* curr) { return curr->right; }
};
template<class S1, class S2, class S3>
inline decltype(auto)
BinaryMatcher(Binary** binder, S1&& s1, S2&& s2, S3&& s3) {
return Matcher<Binary*, S1, S2, S3>(binder, {}, s1, s2, s3);
}
template<class S1, class S2>
inline decltype(auto)
BinaryOpMatcher(Binary** binder, BinaryOp op, S1&& s1, S2&& s2) {
return Matcher<BinaryOpKind<BinaryOpK>, S1, S2>(binder, op, s1, s2);
}
template<class S1, class S2>
inline decltype(auto)
AbstractBinaryOpMatcher(Binary** binder, Abstract::Op op, S1&& s1, S2&& s2) {
return Matcher<BinaryOpKind<AbstractBinaryOpK>, S1, S2>(binder, op, s1, s2);
}
template<> struct NumComponents<Select*> { static constexpr size_t value = 3; };
template<> struct GetComponent<Select*, 0> {
Expression* operator()(Select* curr) { return curr->ifTrue; }
};
template<> struct GetComponent<Select*, 1> {
Expression* operator()(Select* curr) { return curr->ifFalse; }
};
template<> struct GetComponent<Select*, 2> {
Expression* operator()(Select* curr) { return curr->condition; }
};
template<class S1, class S2, class S3>
inline decltype(auto)
SelectMatcher(Select** binder, S1&& s1, S2&& s2, S3&& s3) {
return Matcher<Select*, S1, S2, S3>(binder, {}, s1, s2, s3);
}
}
inline decltype(auto) bval() {
return Internal::ConstMatcher(
nullptr, Internal::BoolLit(nullptr, Internal::Any<bool>(nullptr)));
}
inline decltype(auto) bval(bool x) {
return Internal::ConstMatcher(
nullptr, Internal::BoolLit(nullptr, Internal::Exact<bool>(nullptr, x)));
}
inline decltype(auto) bval(bool* binder) {
return Internal::ConstMatcher(
nullptr, Internal::BoolLit(nullptr, Internal::Any(binder)));
}
inline decltype(auto) bval(Literal* binder) {
return Internal::ConstMatcher(
nullptr, Internal::BoolLit(binder, Internal::Any<bool>(nullptr)));
}
inline decltype(auto) bval(Const** binder) {
return Internal::ConstMatcher(
binder, Internal::BoolLit(nullptr, Internal::Any<bool>(nullptr)));
}
inline decltype(auto) i32() {
return Internal::ConstMatcher(
nullptr, Internal::I32Lit(nullptr, Internal::Any<int32_t>(nullptr)));
}
inline decltype(auto) i32(int x) {
return Internal::ConstMatcher(
nullptr, Internal::I32Lit(nullptr, Internal::Exact<int32_t>(nullptr, x)));
}
inline decltype(auto) i32(int32_t* binder) {
return Internal::ConstMatcher(
nullptr, Internal::I32Lit(nullptr, Internal::Any(binder)));
}
inline decltype(auto) i32(Literal* binder) {
return Internal::ConstMatcher(
nullptr, Internal::I32Lit(binder, Internal::Any<int32_t>(nullptr)));
}
inline decltype(auto) i32(Const** binder) {
return Internal::ConstMatcher(
binder, Internal::I32Lit(nullptr, Internal::Any<int32_t>(nullptr)));
}
inline decltype(auto) i64() {
return Internal::ConstMatcher(
nullptr, Internal::I64Lit(nullptr, Internal::Any<int64_t>(nullptr)));
}
inline decltype(auto) i64(int64_t x) {
return Internal::ConstMatcher(
nullptr, Internal::I64Lit(nullptr, Internal::Exact<int64_t>(nullptr, x)));
}
inline decltype(auto) i64(int x) { return i64(int64_t(x)); }
inline decltype(auto) i64(int64_t* binder) {
return Internal::ConstMatcher(
nullptr, Internal::I64Lit(nullptr, Internal::Any(binder)));
}
inline decltype(auto) i64(Literal* binder) {
return Internal::ConstMatcher(
nullptr, Internal::I64Lit(binder, Internal::Any<int64_t>(nullptr)));
}
inline decltype(auto) i64(Const** binder) {
return Internal::ConstMatcher(
binder, Internal::I64Lit(nullptr, Internal::Any<int64_t>(nullptr)));
}
inline decltype(auto) f32() {
return Internal::ConstMatcher(
nullptr, Internal::F32Lit(nullptr, Internal::Any<float>(nullptr)));
}
inline decltype(auto) f32(float x) {
return Internal::ConstMatcher(
nullptr, Internal::F32Lit(nullptr, Internal::Exact<float>(nullptr, x)));
}
inline decltype(auto) f32(int x) { return f32(float(x)); }
inline decltype(auto) f32(float* binder) {
return Internal::ConstMatcher(
nullptr, Internal::F32Lit(nullptr, Internal::Any(binder)));
}
inline decltype(auto) f32(Literal* binder) {
return Internal::ConstMatcher(
nullptr, Internal::F32Lit(binder, Internal::Any<float>(nullptr)));
}
inline decltype(auto) f32(Const** binder) {
return Internal::ConstMatcher(
binder, Internal::F32Lit(nullptr, Internal::Any<float>(nullptr)));
}
inline decltype(auto) f64() {
return Internal::ConstMatcher(
nullptr, Internal::F64Lit(nullptr, Internal::Any<double>(nullptr)));
}
inline decltype(auto) f64(double x) {
return Internal::ConstMatcher(
nullptr, Internal::F64Lit(nullptr, Internal::Exact<double>(nullptr, x)));
}
inline decltype(auto) f64(int x) { return f64(double(x)); }
inline decltype(auto) f64(double* binder) {
return Internal::ConstMatcher(
nullptr, Internal::F64Lit(nullptr, Internal::Any(binder)));
}
inline decltype(auto) f64(Literal* binder) {
return Internal::ConstMatcher(
nullptr, Internal::F64Lit(binder, Internal::Any<double>(nullptr)));
}
inline decltype(auto) f64(Const** binder) {
return Internal::ConstMatcher(
binder, Internal::F64Lit(nullptr, Internal::Any<double>(nullptr)));
}
inline decltype(auto) ival() {
return Internal::ConstMatcher(
nullptr, Internal::IntLit(nullptr, Internal::Any<int64_t>(nullptr)));
}
inline decltype(auto) ival(int64_t x) {
return Internal::ConstMatcher(
nullptr, Internal::IntLit(nullptr, Internal::Exact<int64_t>(nullptr, x)));
}
inline decltype(auto) ival(int x) { return ival(int64_t(x)); }
inline decltype(auto) ival(int64_t* binder) {
return Internal::ConstMatcher(
nullptr, Internal::IntLit(nullptr, Internal::Any(binder)));
}
inline decltype(auto) ival(Literal* binder) {
return Internal::ConstMatcher(
nullptr, Internal::IntLit(binder, Internal::Any<int64_t>(nullptr)));
}
inline decltype(auto) ival(Const** binder) {
return Internal::ConstMatcher(
binder, Internal::IntLit(nullptr, Internal::Any<int64_t>(nullptr)));
}
inline decltype(auto) ival(Literal* binder, int64_t x) {
return Internal::ConstMatcher(
nullptr, Internal::IntLit(binder, Internal::Exact<int64_t>(nullptr, x)));
}
inline decltype(auto) ival(Const** binder, int64_t x) {
return Internal::ConstMatcher(
binder, Internal::IntLit(nullptr, Internal::Exact<int64_t>(nullptr, x)));
}
inline decltype(auto) fval() {
return Internal::ConstMatcher(
nullptr, Internal::FloatLit(nullptr, Internal::Any<double>(nullptr)));
}
inline decltype(auto) fval(double x) {
return Internal::ConstMatcher(
nullptr, Internal::FloatLit(nullptr, Internal::Exact<double>(nullptr, x)));
}
inline decltype(auto) fval(int x) { return fval(double(x)); }
inline decltype(auto) fval(double* binder) {
return Internal::ConstMatcher(
nullptr, Internal::FloatLit(nullptr, Internal::Any(binder)));
}
inline decltype(auto) fval(Literal* binder) {
return Internal::ConstMatcher(
nullptr, Internal::FloatLit(binder, Internal::Any<double>(nullptr)));
}
inline decltype(auto) fval(Const** binder) {
return Internal::ConstMatcher(
binder, Internal::FloatLit(nullptr, Internal::Any<double>(nullptr)));
}
inline decltype(auto) fval(Literal* binder, double x) {
return Internal::ConstMatcher(
nullptr, Internal::FloatLit(binder, Internal::Exact<double>(nullptr, x)));
}
inline decltype(auto) fval(Const** binder, double x) {
return Internal::ConstMatcher(
binder, Internal::FloatLit(nullptr, Internal::Exact<double>(nullptr, x)));
}
inline decltype(auto) constant() {
return Internal::ConstMatcher(nullptr, Internal::Any<Literal>(nullptr));
}
inline decltype(auto) constant(int x) {
return Internal::ConstMatcher(nullptr, Internal::NumberLit(nullptr, x));
}
inline decltype(auto) constant(Literal* binder) {
return Internal::ConstMatcher(nullptr, Internal::Any(binder));
}
inline decltype(auto) constant(Const** binder) {
return Internal::ConstMatcher(binder, Internal::Any<Literal>(nullptr));
}
inline decltype(auto) constant(Literal* binder, int32_t x) {
return Internal::ConstMatcher(nullptr, Internal::NumberLit(binder, x));
}
inline decltype(auto) constant(Const** binder, int32_t x) {
return Internal::ConstMatcher(binder, Internal::NumberLit(nullptr, x));
}
inline decltype(auto) any() { return Internal::Any<Expression*>(nullptr); }
inline decltype(auto) any(Expression** binder) { return Internal::Any(binder); }
template<class S> inline decltype(auto) unary(S&& s) {
return Internal::UnaryMatcher(nullptr, Internal::Any<UnaryOp>(nullptr), s);
}
template<class S> inline decltype(auto) unary(Unary** binder, S&& s) {
return Internal::UnaryMatcher(binder, Internal::Any<UnaryOp>(nullptr), s);
}
template<class S> inline decltype(auto) unary(UnaryOp* binder, S&& s) {
return Internal::UnaryMatcher(nullptr, Internal::Any<UnaryOp>(binder), s);
}
template<class S> inline decltype(auto) unary(UnaryOp op, S&& s) {
return Internal::UnaryOpMatcher(nullptr, op, s);
}
template<class S> inline decltype(auto) unary(Abstract::Op op, S&& s) {
return Internal::AbstractUnaryOpMatcher(nullptr, op, s);
}
template<class S>
inline decltype(auto) unary(Unary** binder, UnaryOp op, S&& s) {
return Internal::UnaryOpMatcher(binder, op, s);
}
template<class S>
inline decltype(auto) unary(Unary** binder, Abstract::Op op, S&& s) {
return Internal::AbstractUnaryOpMatcher(binder, op, s);
}
template<class S1, class S2> inline decltype(auto) binary(S1&& s1, S2&& s2) {
return Internal::BinaryMatcher(
nullptr, Internal::Any<BinaryOp>(nullptr), s1, s2);
}
template<class S1, class S2>
inline decltype(auto) binary(Binary** binder, S1&& s1, S2&& s2) {
return Internal::BinaryMatcher(
binder, Internal::Any<BinaryOp>(nullptr), s1, s2);
}
template<class S1, class S2>
inline decltype(auto) binary(BinaryOp* binder, S1&& s1, S2&& s2) {
return Internal::BinaryMatcher(
nullptr, Internal::Any<BinaryOp>(binder), s1, s2);
}
template<class S1, class S2>
inline decltype(auto) binary(BinaryOp op, S1&& s1, S2&& s2) {
return Internal::BinaryOpMatcher(nullptr, op, s1, s2);
}
template<class S1, class S2>
inline decltype(auto) binary(Abstract::Op op, S1&& s1, S2&& s2) {
return Internal::AbstractBinaryOpMatcher(nullptr, op, s1, s2);
}
template<class S1, class S2>
inline decltype(auto) binary(Binary** binder, BinaryOp op, S1&& s1, S2&& s2) {
return Internal::BinaryOpMatcher(binder, op, s1, s2);
}
template<class S1, class S2>
inline decltype(auto)
binary(Binary** binder, Abstract::Op op, S1&& s1, S2&& s2) {
return Internal::AbstractBinaryOpMatcher(binder, op, s1, s2);
}
template<class S1, class S2, class S3>
inline decltype(auto) select(S1&& s1, S2&& s2, S3&& s3) {
return Internal::SelectMatcher(nullptr, s1, s2, s3);
}
template<class S1, class S2, class S3>
inline decltype(auto) select(Select** binder, S1&& s1, S2&& s2, S3&& s3) {
return Internal::SelectMatcher(binder, s1, s2, s3);
}
}
#endif