| /* | |
| * Copyright 2020 WebAssembly Community Group participants | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| // | |
| // match.h - Provides an easily extensible layered API for matching expression | |
| // patterns and extracting their components. The low-level API provides modular | |
| // building blocks for creating matchers for any data type and the high-level | |
| // API provides a succinct and flexible interface for matching expressions and | |
| // extracting useful information from them. | |
| #ifndef wasm_ir_match_h | |
| #define wasm_ir_match_h | |
| #include "ir/abstract.h" | |
| #include "wasm.h" | |
| namespace wasm::Match { | |
| // The available matchers are: | |
| // | |
| // i32, i64, f32, f64 | |
| // | |
| // Match constants of the corresponding type. Takes zero or one argument. The | |
| // argument can be a specific value to match or it can be a pointer to a | |
| // value, Literal, or Const* at which to store the matched entity. | |
| // | |
| // bval, ival, fval | |
| // | |
| // Match any boolean, any integer or any floating point constant. Takes | |
| // neither, either, or both of two possible arguments: first, a pointer to a | |
| // value, Literal, or Const* at which to store the matched entity and second, | |
| // a specific value to match. | |
| // | |
| // constant | |
| // | |
| // Matches any numeric Const expression. Takes neither, either, or both of | |
| // two possible arguments: first, a pointer to either Literal or Const* at | |
| // which to store the matched entity and second, a specific value (given as | |
| // an int32_t) to match.. | |
| // | |
| // any | |
| // | |
| // Matches any Expression. Optionally takes as an argument a pointer to | |
| // Expression* at which to store the matched Expression*. | |
| // | |
| // unary | |
| // | |
| // Matches Unary expressions. Takes an optional pointer to Unary* at which to | |
| // store the matched Unary*, followed by either a UnaryOp or an Abstract::Op | |
| // describing which unary expressions to match, followed by a matcher to | |
| // apply to the unary expression's operand. | |
| // | |
| // binary | |
| // | |
| // Matches Binary expressions. Takes an optional pointer to Binary* at which | |
| // to store the matched Binary*, followed by either a BinaryOp or an | |
| // Abstract::Op describing which binary expressions to match, followed by | |
| // matchers to apply to the binary expression's left and right operands. | |
| // | |
| // select | |
| // | |
| // Matches Select expressions. Takes an optional pointer to Select* at which | |
| // to store the matched Select*, followed by matchers to apply to the ifTrue, | |
| // ifFalse, and condition operands. | |
| // | |
| // | |
| // How to create new matchers: | |
| // | |
| // Lets add a matcher for an expression type that is declared in wasm.h: | |
| // | |
| // class Frozzle : public SpecificExpression<Expression::FrozzleId> { | |
| // public: | |
| // Expression* foo; | |
| // Expression* bar; | |
| // Expression* baz; | |
| // }; | |
| // | |
| // This expression is very simple; in order to match it, all we need to do is | |
| // apply other matchers to its subexpressions. The matcher infrastructure will | |
| // handle this automatically once we tell it how to access the subexpressions. | |
| // To tell the matcher infrastructure how many subexpressions there are we need | |
| // to specialize `NumComponents`. | |
| // | |
| // template<> struct NumComponents<Frozzle*> { | |
| // static constexpr size_t value = 3; | |
| // }; | |
| // | |
| // And to tell the matcher infrastructure how to access those three | |
| // subexpressions, we need to specialize `GetComponent` three times. | |
| // | |
| // template<> struct GetComponent<Frozzle*, 0> { | |
| // Expression* operator()(Frozzle* curr) { return curr->foo; } | |
| // }; | |
| // template<> struct GetComponent<Frozzle*, 1> { | |
| // Expression* operator()(Frozzle* curr) { return curr->bar; } | |
| // }; | |
| // template<> struct GetComponent<Frozzle*, 2> { | |
| // Expression* operator()(Frozzle* curr) { return curr->baz; } | |
| // }; | |
| // | |
| // For simple expressions, that's all we need to do to get a fully functional | |
| // matcher that we can construct and use like this, where S1, S2, and S3 are | |
| // the types of the submatchers to use and s1, s2, and s3 are instances of | |
| // those types: | |
| // | |
| // Frozzle* extracted; | |
| // auto matcher = Matcher<Frozzle*, S1, S2, S3>(&extracted, {}, s1, s2, s3); | |
| // if (matches(expr, matcher)) { | |
| // // `extracted` set to `expr` here | |
| // } | |
| // | |
| // It's annoying to have to write out the types S1, S2, and S3 and we don't get | |
| // class template argument deduction (CTAD) until C++17, so it's useful to | |
| // create a wrapper function so can take advantage of function template | |
| // argument deduction. We can also take this opportunity to make the interface | |
| // more compact. | |
| // | |
| // template<class S1, class S2, class S3> | |
| // inline decltype(auto) frozzle(Frozzle** binder, | |
| // S1&& s1, S2&& s2, S3&& s3) { | |
| // return Matcher<Frozzle*, S1, S2, S3>(binder, {}, s1, s2, s3); | |
| // } | |
| // template<class S1, class S2, class S3> | |
| // inline decltype(auto) frozzle(S1&& s1, S2&& s2, S3&& s3) { | |
| // return Matcher<Frozzle*, S1, S2, S3>(nullptr, {}, s1, s2, s3); | |
| // } | |
| // | |
| // Notice that we make the interface more compact by providing overloads with | |
| // and without the binder. Here is the final matcher usage: | |
| // | |
| // Frozzle* extracted; | |
| // if (matches(expr, frozzle(&extracted, s1, s2, s3))) { | |
| // // `extracted` set to `expr` here | |
| // } | |
| // | |
| // Some matchers are more complicated, though, because they need to do | |
| // something besides just applying submatchers to the components of an | |
| // expression. These matchers require slightly more work. | |
| // | |
| // | |
| // Complex matchers: | |
| // | |
| // Lets add a matcher that will match calls to functions whose names start with | |
| // certain prefixes. Since this is not a normal matcher for Call expressions, | |
| // we can't identify it by the Call* type. Instead, we have to create a new | |
| // identifier type, called a "Kind" for it. | |
| // | |
| // struct PrefixCallKind {}; | |
| // | |
| // Next, since we're not in the common case of using a specific expression | |
| // pointer as our kind, we have to tell the matcher infrastructure what type of | |
| // thing this matcher matches. Since we want this matcher to be able to match | |
| // any given prefix, we also need the matcher to contain the given prefix as | |
| // state, and we need to tell the matcher infrastructure what type that state | |
| // is as well. To specify these types, we need to specialize | |
| // `KindTypeRegistry` for `PrefixCallKind`. | |
| // | |
| // template<> struct KindTypeRegistry<PrefixCallKind> { | |
| // using matched_t = Call*; | |
| // using data_t = Name; | |
| // }; | |
| // | |
| // Note that because `matched_t` is set to a specific expression pointer, this | |
| // matcher will automatically be able to be applied to any `Expression*`, not | |
| // just `Call*`. If `matched_t` were not a specific expression pointer, this | |
| // matcher would only be able to be applied to types compatible with | |
| // `matched_t`. Also note that if a matcher does not need to store any state, | |
| // its `data_t` should be set to `unused_t`. | |
| // | |
| // Now we need to tell the matcher infrastructure what custom logic to apply | |
| // for this matcher. We do this by specializing `MatchSelf`. | |
| // | |
| // template<> struct MatchSelf<PrefixCallKind> { | |
| // bool operator()(Call* curr, Name prefix) { | |
| // return curr->name.startsWith(prefix); | |
| // } | |
| // }; | |
| // | |
| // Note that the first parameter to `MatchSelf<Kind>::operator()` will be that | |
| // kind's `matched_t` and the second parameter will be that kind's `data_t`, | |
| // which may be `unused_t`. (TODO: detect if `data_t` is `unused_t` and don't | |
| // expose it in the Matcher interface if so.) | |
| // | |
| // After this, everything is the same as in the simple matcher case. This | |
| // particular matcher doesn't need to recurse into any subcomponents, so we can | |
| // skip straight to creating the wrapper function. | |
| // | |
| // decltype(auto) prefixCall(Call** binder, Name prefix) { | |
| // return Matcher<PrefixCallKind>(binder, prefix); | |
| // } | |
| // | |
| // Now we can use the new matcher: | |
| // | |
| // Call* call; | |
| // if (matches(expr, prefixCall(&call, "__foo"))) { | |
| // // `call` set to `expr` here | |
| // } | |
| // | |
| // The main entrypoint for matching. If the match succeeds, all variables bound | |
| // in the matcher will be set to their corresponding matched values. Otherwise, | |
| // the value of the bound variables is unspecified and may have changed. | |
| template<class Matcher> inline bool matches(Expression* expr, Matcher matcher) { | |
| return matcher.matches(expr); | |
| } | |
| namespace Internal { | |
| struct unused_t {}; | |
| // Each matcher has a `Kind`, which controls how candidate values are | |
| // destructured and inspected. For most matchers, `Kind` is a pointer to the | |
| // matched subtype of Expression, but when there are multiple matchers for the | |
| // same kind of expression, they are disambiguated by having different `Kind`s. | |
| // In this case, or if the matcher matches something besides a pointer to a | |
| // subtype of Expression, or if the matcher requires additional state, the | |
| // matched type and the type of additional state must be associated with the | |
| // `Kind` via a specialization of `KindTypeRegistry`. | |
| template<class Kind> struct KindTypeRegistry { | |
| // The matched type | |
| using matched_t = void; | |
| // The type of additional state needed to perform a match. Can be set to | |
| // `unused_t` if it's not needed. | |
| using data_t = unused_t; | |
| }; | |
| // Given a `Kind`, produce the type `matched_t` that is matched by that Kind and | |
| // the type `candidate_t` that is the type of the parameter of the `matches` | |
| // method. These types are only different if `matched_t` is a pointer to a | |
| // subtype of Expression, in which case `candidate_t` is Expression*. | |
| 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; | |
| // Defined if the matched type is a specific expression pointer, so can be | |
| // `dynCast`ed to from Expression*. | |
| 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; | |
| // Opposite of above | |
| 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; | |
| // Do a normal dynCast from Expression* to the subtype, storing the result in | |
| // `out` and returning `true` iff the cast succeeded. | |
| 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; | |
| } | |
| // Otherwise we are not matching an Expression, so this is infallible. | |
| 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; | |
| } | |
| // Matchers can optionally specialize this to perform custom matching logic | |
| // before recursing into submatchers, potentially short-circuiting the match. | |
| // Uses a struct because partial specialization of functions is not allowed. | |
| template<class Kind> struct MatchSelf { | |
| bool operator()(matched_t<Kind>, data_t<Kind>) { return true; } | |
| }; | |
| // Used to statically ensure that each matcher has the correct number of | |
| // submatchers. This needs to be specialized for each kind of matcher that has | |
| // submatchers. | |
| template<class Kind> struct NumComponents { | |
| static constexpr size_t value = 0; | |
| }; | |
| // Every kind of matcher needs to partially specialize this for each of its | |
| // components. Each specialization should define | |
| // | |
| // T operator()(matched_t<Kind>) | |
| // | |
| // where T is the component's type. Components will be matched from first to | |
| // last. Uses a struct instead of a function because partial specialization of | |
| // functions is not allowed. | |
| template<class Kind, int pos> struct GetComponent; | |
| // A type-level linked list to hold an arbitrary number of matchers. | |
| 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...) {}; | |
| }; | |
| // Iterates through the components of the candidate, applying a submatcher to | |
| // each component. Uses a struct instead of a function because partial | |
| // specialization of functions is not allowed. | |
| 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<>) { | |
| // Base case when there are no components left; trivially true. | |
| 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; | |
| } | |
| }; | |
| // Concrete low-level matcher implementations. Not intended for direct external | |
| // use. | |
| // Any<T>: matches any value of the expected type | |
| 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, {}); | |
| } | |
| // Exact<T>: matches exact values of the expected type | |
| 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); | |
| } | |
| // {Bool,I32,I64,Int,F32,F64,Float,Number}Lit: | |
| // match `Literal` of the expected `Type` | |
| 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); | |
| } | |
| // Const | |
| 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); | |
| } | |
| // Unary, UnaryOp and AbstractUnaryOp | |
| 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); | |
| } | |
| // Binary, BinaryOp and AbstractBinaryOp | |
| 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); | |
| } | |
| // Select | |
| 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); | |
| } | |
| } // namespace Internal | |
| // Public matching API | |
| 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))); | |
| } | |
| // Use int rather than int32_t to disambiguate literal 0, which otherwise could | |
| // be resolved to either the int32_t overload or any of the pointer overloads. | |
| 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))); | |
| } | |
| // Disambiguate literal 0, which could otherwise be interpreted as a pointer | |
| 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))); | |
| } | |
| // Disambiguate literal 0, which could otherwise be interpreted as a pointer | |
| 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))); | |
| } | |
| // Disambiguate literal 0, which could otherwise be interpreted as a pointer | |
| 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))); | |
| } | |
| // Disambiguate literal 0, which could otherwise be interpreted as a pointer | |
| 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))); | |
| } | |
| // Disambiguate literal 0, which could otherwise be interpreted as a pointer | |
| 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); | |
| } | |
| } // namespace wasm::Match | |
| #endif // wasm_ir_match_h |