Program Listing for File pattern_match.h

Program Listing for File pattern_match.h#

↰ Return to documentation for file (tvm/ir/pattern_match.h)

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

#ifndef TVM_IR_PATTERN_MATCH_H_
#define TVM_IR_PATTERN_MATCH_H_

#include <tvm/ffi/cast.h>

#include <optional>
#include <tuple>
#include <type_traits>
#include <utility>

namespace tvm {
template <typename Derived>
class Pattern {
 public:
  using Nested = Derived;

  template <typename NodeType>
  inline bool Match(const NodeType& value) const {
    return Match(value, []() { return true; });
  }

  template <typename NodeType, typename Condition>
  bool Match(const NodeType& value, Condition cond) const {
    derived().InitMatch_();
    return derived().Match_(value) && cond();
  }
  const Derived& derived() const { return *static_cast<const Derived*>(this); }
};

template <typename T>
class PEqualChecker {
 public:
  bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; }
};

template <typename T>
class PVar : public Pattern<PVar<T>> {
 public:
  // Store PVars by reference in the expression.
  using Nested = const PVar<T>&;

  void InitMatch_() const { value_.reset(); }

  bool Match_(const T& value) const {
    if (!value_) {
      value_ = value;
      return true;
    } else {
      return PEqualChecker<T>()(*value_, value);
    }
  }

  template <typename NodeRefType, typename = typename std::enable_if<
                                      !std::is_same<NodeRefType, T>::value &&
                                      std::is_base_of<ffi::ObjectRef, NodeRefType>::value &&
                                      std::is_base_of<ffi::ObjectRef, T>::value>::type>
  bool Match_(const NodeRefType& value) const {
    if (auto typed_value = value.template as<T>()) {
      return Match_(*typed_value);
    } else {
      return false;
    }
  }

  T Eval() const {
    TVM_FFI_ICHECK(value_.has_value());
    return *value_;
  }

  T EvalOr(const T& default_value) const { return value_.value_or(default_value); }

 protected:
  mutable std::optional<T> value_;
};

template <typename Derived, typename T>
class PVarWithCheck : public Pattern<PVarWithCheck<Derived, T>> {
 public:
  // Store by reference in the expression.
  using Nested = const PVarWithCheck<Derived, T>&;

  void InitMatch_() const { pvar_.InitMatch_(); }

  bool Match_(const T& value) const {
    if (!static_cast<const Derived*>(this)->Match_(value)) return false;
    return pvar_.Match_(value);
  }

  template <typename NodeRefType,
            typename = typename std::enable_if<std::is_base_of<NodeRefType, T>::value>::type>
  bool Match_(const NodeRefType& value) const {
    if (auto typed_value = value.template as<T>()) {
      return Match_(*typed_value);
    } else {
      return false;
    }
  }

  T Eval() const { return pvar_.Eval(); }

 protected:
  PVar<T> pvar_;
};

template <typename T>
class PConst : public Pattern<PConst<T>> {
 public:
  PConst(T value)  // NOLINT(*)
      : value_(value) {}

  void InitMatch_() const {}

  bool Match_(const T& value) const { return PEqualChecker<T>()(value_, value); }

  T Eval() const { return value_; }

 private:
  const T value_;
};

template <typename... TPattern>
class PMatchesOneOf {
 public:
  explicit PMatchesOneOf(const TPattern&... patterns) : patterns_{patterns...} {}

  template <typename NodeType>
  inline bool Match(const NodeType& value) const {
    return Match(value, []() { return true; });
  }

  template <typename NodeType, typename Condition>
  inline bool Match(const NodeType& value, Condition cond) const {
    return MatchImpl(value, cond, std::make_index_sequence<sizeof...(TPattern)>());
  }

 private:
  template <typename NodeType, typename Condition>
  inline bool MatchImpl(const NodeType& value, Condition cond, std::index_sequence<>) const {
    return false;
  }

  template <typename NodeType, typename Condition, size_t FirstIndex, size_t... RemainingIndices>
  inline bool MatchImpl(const NodeType& value, Condition cond,
                        std::index_sequence<FirstIndex, RemainingIndices...>) const {
    return std::get<FirstIndex>(patterns_).Match(value, cond) ||
           MatchImpl(value, cond, std::index_sequence<RemainingIndices...>());
  }

  // Hold the patterns by const&.  This follows the same usage as both
  // the `PVar`, which occurs as `const PVar<T>&` when it appears
  // inside other patterns.  Because the `PVar<T>::value_` field is
  // mutable, it can still be updated through these const references.
  // So long as the call to `Match()` occurs within the same
  // expression as created the patterns, this avoids accidental copies
  // without creating dangling references.  This may be improved in
  // the future by use of `constexpr` constructors/operators, allowing
  // more typical value semantics.
  std::tuple<const TPattern&...> patterns_;
};

/* \brief Return a proxy object that returns true after the first match
 *
 * In the RewriteSimplifier, there are often several expressions that
 * simplify to the same resulting expression.  This utility allows
 * them to be specified as a single rule, reducing duplication of the
 * result/condition of a rewrite.
 */
template <typename... TPattern>
inline std::enable_if_t<(std::is_base_of_v<Pattern<TPattern>, TPattern> && ... && true),
                        PMatchesOneOf<TPattern...>>
MatchesOneOf(const TPattern&... patterns) {
  return PMatchesOneOf<TPattern...>(patterns...);
}
}  // namespace tvm
#endif  // TVM_IR_PATTERN_MATCH_H_