Program Listing for File op.h

Program Listing for File op.h#

↰ Return to documentation for file (tvm/ir/prim/op.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_PRIM_OP_H_
#define TVM_IR_PRIM_OP_H_
#include <tvm/ir/op.h>
#include <tvm/ir/prim/expr.h>

#include <algorithm>
#include <limits>
#include <type_traits>
#include <utility>
namespace tvm {

TVM_TIRX_DLL PrimExpr operator+(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator-(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator-(PrimExpr a);

TVM_TIRX_DLL PrimExpr operator*(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator/(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator<<(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator>>(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator>(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator>=(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator<(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator<=(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator==(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator!=(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator&&(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator||(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator!(PrimExpr a);

TVM_TIRX_DLL PrimExpr operator&(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator|(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator^(PrimExpr a, PrimExpr b);

TVM_TIRX_DLL PrimExpr operator~(PrimExpr a);

inline PrimExpr operator+=(PrimExpr& a, PrimExpr b) {
  a = a + b;
  return a;
}

inline PrimExpr operator-=(PrimExpr& a, PrimExpr b) {
  a = a - b;
  return a;
}

inline PrimExpr operator*=(PrimExpr& a, PrimExpr b) {
  a = a * b;
  return a;
}

namespace prim {

TVM_TIRX_DLL const PrimExpr& pos_inf();

TVM_TIRX_DLL const PrimExpr& neg_inf();

TVM_TIRX_DLL bool is_pos_inf(const PrimExpr& value);

TVM_TIRX_DLL bool is_neg_inf(const PrimExpr& value);

TVM_TIRX_DLL const Op& vscale_op();

TVM_TIRX_DLL const Op& ceil_op();

TVM_TIRX_DLL const Op& log2_op();

TVM_TIRX_DLL const Op& clz_op();

TVM_TIRX_DLL const Op& if_then_else_op();

TVM_TIRX_DLL const Op& likely_op();

}  // namespace prim

TVM_TIRX_DLL PrimExpr add(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr sub(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr neg(PrimExpr a, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr mul(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr left_shift(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr right_shift(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr greater(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr greater_equal(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr less(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr less_equal(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr equal(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr not_equal(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr logical_and(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr logical_or(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr logical_not(PrimExpr a, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr div(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr truncdiv(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr truncmod(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr indexdiv(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr shapediv(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr indexmod(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr floordiv(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());


TVM_TIRX_DLL PrimExpr ceildiv(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr floormod(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr max(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr min(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr bitwise_and(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr bitwise_or(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr bitwise_xor(PrimExpr a, PrimExpr b, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr bitwise_neg(PrimExpr a, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr if_then_else(PrimExpr cond, PrimExpr true_value, PrimExpr false_value,
                                   Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr likely(PrimExpr cond, Location loc = UnknownLoc());
namespace prim {
TVM_TIRX_DLL PrimExpr ceil(PrimExpr x, Location loc = UnknownLoc());
TVM_TIRX_DLL PrimExpr log2(PrimExpr x, Location loc = UnknownLoc());
TVM_TIRX_DLL PrimExpr clz(PrimExpr x, Location loc = UnknownLoc());
TVM_TIRX_DLL PrimExpr min_value(PrimType dtype, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr max_value(PrimType dtype, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr cast(PrimExpr value, PrimType t, Location loc = UnknownLoc());
TVM_TIRX_DLL PrimExpr IntegerAbs(PrimExpr x, Location loc = UnknownLoc());
template <typename ValueType,
          typename = typename std::enable_if<(std::is_standard_layout<ValueType>::value &&
                                              std::is_trivial<ValueType>::value) ||
                                             std::is_same<ValueType, ffi::BigInt>::value>::type>
inline PrimExpr MakeConst(PrimType dtype, ValueType value, Location loc = UnknownLoc());

template <typename ValueType>
inline PrimExpr MakeConstScalar(PrimType dtype, ValueType value, Location loc = UnknownLoc()) {
  if constexpr (std::is_enum_v<ValueType>) {
    return MakeConstScalar(dtype, static_cast<std::underlying_type_t<ValueType>>(value), loc);
  } else {
    DLDataTypeCode code = dtype.code();
    if (code == DLDataTypeCode::kDLInt || code == DLDataTypeCode::kDLBool) {
      return IntImm(dtype, ffi::BigInt(value), loc);
    }
    if (code == DLDataTypeCode::kDLUInt) {
      TVM_FFI_ICHECK(value >= 0) << "cannot make uint from negative value " << value;
      return IntImm(dtype, ffi::BigInt(value), loc);
    }
    if (dtype.MatchesCode(DLDataTypeCode::kDLFloat, DLDataTypeCode::kDLFloat8_e3m4,
                          DLDataTypeCode::kDLFloat8_e4m3, DLDataTypeCode::kDLFloat8_e4m3b11fnuz,
                          DLDataTypeCode::kDLFloat8_e4m3fn, DLDataTypeCode::kDLFloat8_e4m3fnuz,
                          DLDataTypeCode::kDLFloat8_e5m2, DLDataTypeCode::kDLFloat8_e5m2fnuz,
                          DLDataTypeCode::kDLFloat8_e8m0fnu, DLDataTypeCode::kDLFloat6_e2m3fn,
                          DLDataTypeCode::kDLFloat6_e3m2fn, DLDataTypeCode::kDLFloat4_e2m1fn) ||
        dtype.MatchesElementType(DLDataTypeCode::kDLBfloat, 16)) {
      return FloatImm(dtype, static_cast<double>(value), loc);
    }
    TVM_FFI_THROW(InternalError) << "cannot make const for type " << dtype;
    throw;
  }
}

template <>
inline PrimExpr MakeConstScalar(PrimType dtype, bool value, Location loc) {
  return MakeConstScalar(dtype, static_cast<int>(value), loc);
}

template <typename ValueType, typename>
inline PrimExpr MakeConst(PrimType dtype, ValueType value, Location loc) {
  if (!dtype.IsScalableVector() && !dtype.IsFixedLengthVector()) {
    return MakeConstScalar(dtype, value, loc);
  }
  PrimType elem_ty = dtype.WithLanes(1);
  if (dtype.IsFixedLengthVector()) {
    return prim::Broadcast(MakeConstScalar(elem_ty, value, loc), dtype.lanes(), loc);
  }
  PrimExpr lanes = prim::Mul(Call(PrimType::Int(32), prim::vscale_op(), {}).as_or_throw<PrimExpr>(),
                             dtype.VScaleFactor());
  return prim::Broadcast(MakeConstScalar(elem_ty, value, loc), lanes, loc);
}

}  // namespace prim

// additional const expression overloading
#define TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD(Name)                                                \
  inline PrimExpr Name(const PrimExpr& a, float b) { return Name(a, PrimExpr(b)); }              \
  inline PrimExpr Name(float a, const PrimExpr& b) { return Name(PrimExpr(a), b); }              \
  inline PrimExpr Name(int a, const PrimExpr& b) { return Name(prim::MakeConst(b.ty(), a), b); } \
  inline PrimExpr Name(const PrimExpr& a, int b) { return Name(a, prim::MakeConst(a.ty(), b)); } \
  inline PrimExpr Name(const PrimExpr& a, double b) {                                            \
    return Name(a, FloatImm(PrimType::Float(64), b));                                            \
  }

#define TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD_SPANNED(Name)                          \
  inline PrimExpr Name(const PrimExpr& a, float b, Location loc = UnknownLoc()) {  \
    return Name(a, PrimExpr(b), loc);                                              \
  }                                                                                \
  inline PrimExpr Name(float a, const PrimExpr& b, Location loc = UnknownLoc()) {  \
    return Name(PrimExpr(a), b, loc);                                              \
  }                                                                                \
  inline PrimExpr Name(int a, const PrimExpr& b, Location loc = UnknownLoc()) {    \
    return Name(prim::MakeConst(b.ty(), a), b, loc);                               \
  }                                                                                \
  inline PrimExpr Name(const PrimExpr& a, int b, Location loc = UnknownLoc()) {    \
    return Name(a, prim::MakeConst(a.ty(), b), loc);                               \
  }                                                                                \
  inline PrimExpr Name(const PrimExpr& a, double b, Location loc = UnknownLoc()) { \
    return Name(a, FloatImm(PrimType::Float(64), b), loc);                         \
  }

#define TVM_DEFINE_LOGICAL_OP_CONST_VAL_OVERLOAD(Name)                             \
  inline PrimExpr Name(const PrimExpr& a, bool b) { return Name(a, PrimExpr(b)); } \
  inline PrimExpr Name(bool a, const PrimExpr& b) { return Name(PrimExpr(a), b); }

#define TVM_DEFINE_LOGICAL_OP_CONST_VAL_OVERLOAD_SPANNED(Name)                   \
  inline PrimExpr Name(const PrimExpr& a, bool b, Location loc = UnknownLoc()) { \
    return Name(a, PrimExpr(b), loc);                                            \
  }                                                                              \
  inline PrimExpr Name(bool a, const PrimExpr& b, Location loc = UnknownLoc()) { \
    return Name(PrimExpr(a), b, loc);                                            \
  }

#define TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD(Name)                                               \
  inline PrimExpr Name(const PrimExpr& a, int b) { return Name(a, prim::MakeConst(a.ty(), b)); } \
  inline PrimExpr Name(int a, const PrimExpr& b) { return Name(prim::MakeConst(b.ty(), a), b); }

#define TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD_SPANNED(Name)                      \
  inline PrimExpr Name(const PrimExpr& a, int b, Location loc = UnknownLoc()) { \
    return Name(a, prim::MakeConst(a.ty(), b), loc);                            \
  }                                                                             \
  inline PrimExpr Name(int a, const PrimExpr& b, Location loc = UnknownLoc()) { \
    return Name(prim::MakeConst(b.ty(), a), b, loc);                            \
  }

TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD(operator+);
TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD(operator-);
TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD(operator*);
TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD(operator>);  // NOLINT(*)
TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD(operator>=);
TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD(operator<);  // NOLINT(*)
TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD(operator<=);
TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD_SPANNED(max);
TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD_SPANNED(min);
TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD_SPANNED(div);
TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD_SPANNED(add);
TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD_SPANNED(sub);
TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD_SPANNED(mul);
TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD_SPANNED(greater);
TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD_SPANNED(greater_equal);
TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD_SPANNED(less);
TVM_DEFINE_BINOP_CONST_VAL_OVERLOAD_SPANNED(less_equal);
// integer related ops
TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD_SPANNED(indexdiv);
TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD_SPANNED(indexmod);
TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD_SPANNED(truncdiv);
TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD_SPANNED(truncmod);
TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD_SPANNED(floordiv);
TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD_SPANNED(floormod);
TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD_SPANNED(right_shift);  // NOLINT(*)
TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD_SPANNED(left_shift);   // NOLINT(*)
TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD_SPANNED(bitwise_and);
TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD_SPANNED(bitwise_or);
TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD_SPANNED(bitwise_xor);
TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD(operator>>);  // NOLINT(*)
TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD(operator<<);  // NOLINT(*)
TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD(operator&);
TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD(operator|);
TVM_DEFINE_INT_OP_CONST_VAL_OVERLOAD(operator^);
// logical ops
TVM_DEFINE_LOGICAL_OP_CONST_VAL_OVERLOAD(operator&&);
TVM_DEFINE_LOGICAL_OP_CONST_VAL_OVERLOAD(operator||);
TVM_DEFINE_LOGICAL_OP_CONST_VAL_OVERLOAD_SPANNED(logical_and);
TVM_DEFINE_LOGICAL_OP_CONST_VAL_OVERLOAD_SPANNED(logical_or);

template <typename TA>
inline void DivAmbiguityError(const TA& a) {
  constexpr bool div_ambiguity = !std::is_class<TA>::value;
  static_assert(div_ambiguity,
                "TVM supports multiple types of integer divisions, "
                "please call div, indexdiv/indexmod, "
                "floordiv/floormod or truncdiv/truncmod directly "
                "to avoid ambiguity in the code. "
                "Checkout these functions in ir/prim/op.h.");
}

// The following code are not intended to be used in the codebase.
// Instead, they generate clear compiler errors that ask developers
// to use the specific division function.
// The second template argument is necessary to make sure the
// code compiles lazily by the compiler during invocation.
template <typename TB>
inline PrimExpr operator/(const PrimExpr& a, const TB& b) {
  DivAmbiguityError(a);
  return a;
}

template <typename TB>
inline PrimExpr operator/=(const PrimExpr& a, const TB& b) {
  DivAmbiguityError(a);
  return a;
}

template <typename TB>
inline PrimExpr operator%(const PrimExpr& a, const TB& b) {
  DivAmbiguityError(a);
  return a;
}

// Constant predicates
namespace prim {

inline bool IsConstInt(const PrimExpr& x, int64_t value);

inline bool IsOne(const PrimExpr& x);

inline bool IsZero(const PrimExpr& x);

inline bool IsConstInt(const PrimExpr& x);

inline bool IsConstNumber(const PrimExpr& x);

TVM_TIRX_DLL bool IsPowerOfTwoInt(const PrimExpr& x, int* shift);

inline bool IsPositiveConst(const PrimExpr& a);

inline bool IsNegativeConst(const PrimExpr& a);

// Inline constant predicate implementations.
inline bool IsConstInt(const PrimExpr& x) { return x.as<IntImmObj>() != nullptr; }

inline bool IsConstNumber(const PrimExpr& x) {
  if (x.as<IntImmObj>()) {
    return true;
  } else if (x.as<FloatImmObj>()) {
    return true;
  } else if (const auto* op = x.as<prim::BroadcastObj>()) {
    return (op->value->IsInstance<IntImmObj>() || op->value->IsInstance<FloatImmObj>());
  }
  return false;
}

inline bool IsPositiveConst(const PrimExpr& a) {
  const auto* as_int = a.as<IntImmObj>();
  return as_int && as_int->value > 0;
}

inline bool IsNegativeConst(const PrimExpr& a) {
  const auto* as_int = a.as<IntImmObj>();
  return as_int && as_int->value < 0;
}

inline bool IsConstInt(const PrimExpr& x, int64_t value) {
  const auto* as_int = x.as<IntImmObj>();
  return as_int && as_int->value == value;
}

inline bool IsOne(const PrimExpr& x) { return IsConstInt(x, 1); }

inline bool IsZero(const PrimExpr& x) { return IsConstInt(x, 0); }

TVM_TIRX_DLL PrimExpr infinity(PrimType dtype, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr pow(PrimExpr x, PrimExpr y, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr abs(PrimExpr x, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr isnan(PrimExpr x, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr isfinite(PrimExpr x, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr isinf(PrimExpr x, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr floor(PrimExpr x, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr round(PrimExpr x, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr nearbyint(PrimExpr x, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr trunc(PrimExpr x, Location loc = UnknownLoc());

TVM_TIRX_DLL PrimExpr fmod(PrimExpr x, PrimExpr y, Location loc = UnknownLoc());
TVM_TIRX_DLL const Op& pow_op();
TVM_TIRX_DLL const Op& fabs_op();
TVM_TIRX_DLL const Op& fmod_op();
TVM_TIRX_DLL const Op& floor_op();
TVM_TIRX_DLL const Op& round_op();
TVM_TIRX_DLL const Op& nearbyint_op();
TVM_TIRX_DLL const Op& trunc_op();
TVM_TIRX_DLL const Op& exp_op();
TVM_TIRX_DLL const Op& exp2_op();
TVM_TIRX_DLL const Op& exp10_op();
TVM_TIRX_DLL const Op& erf_op();
TVM_TIRX_DLL const Op& tanh_op();
TVM_TIRX_DLL const Op& sigmoid_op();
TVM_TIRX_DLL const Op& sqrt_op();
TVM_TIRX_DLL const Op& rsqrt_op();
TVM_TIRX_DLL const Op& log_op();
TVM_TIRX_DLL const Op& log1p_op();
TVM_TIRX_DLL const Op& log10_op();
TVM_TIRX_DLL const Op& tan_op();
TVM_TIRX_DLL const Op& cos_op();
TVM_TIRX_DLL const Op& cosh_op();
TVM_TIRX_DLL const Op& sin_op();
TVM_TIRX_DLL const Op& sinh_op();
TVM_TIRX_DLL const Op& asin_op();
TVM_TIRX_DLL const Op& acos_op();
TVM_TIRX_DLL const Op& atan_op();
TVM_TIRX_DLL const Op& acosh_op();
TVM_TIRX_DLL const Op& asinh_op();
TVM_TIRX_DLL const Op& atanh_op();
TVM_TIRX_DLL const Op& atan2_op();
TVM_TIRX_DLL const Op& nextafter_op();
TVM_TIRX_DLL const Op& hypot_op();
TVM_TIRX_DLL const Op& copysign_op();
TVM_TIRX_DLL const Op& ldexp_op();
TVM_TIRX_DLL const Op& isnan_op();
TVM_TIRX_DLL const Op& popcount_op();
TVM_TIRX_DLL const Op& fma_op();
TVM_TIRX_DLL const Op& assume_op();
TVM_TIRX_DLL PrimExpr assume(PrimExpr condition, Location loc = UnknownLoc());

inline PrimExpr fma(PrimExpr x, PrimExpr y, PrimExpr z, Location loc = UnknownLoc()) {
  return Call(x.ty(), fma_op(), {x, y, z}, {}, {}, loc).as_or_throw<PrimExpr>();
}

inline void CheckMathUnaryOpInputDType(const char* op_name, const PrimType& dtype) {
  TVM_FFI_CHECK(dtype.code() == DLDataTypeCode::kDLFloat ||
                    dtype.MatchesElementType(DLDataTypeCode::kDLBfloat, 16),
                TypeError)
      << "prim." << op_name << " only supports floating-point inputs, but got " << dtype;
}

// Intrinsic operators
#define TVM_DECLARE_INTRIN_UNARY_WITH_CHECK(OpName, CheckInputDType)                          \
  inline PrimExpr OpName(PrimExpr x, Location loc = UnknownLoc()) {                           \
    static const Op op = Op::Get("prim." #OpName);                                            \
    PrimType x_ty = x.ty();                                                                   \
    CheckInputDType(#OpName, x_ty);                                                           \
    if (x_ty.MatchesElementType(DLDataTypeCode::kDLBfloat, 16)) {                             \
      PrimType bf16_ty = x_ty;                                                                \
      PrimType f32_ty =                                                                       \
          x_ty.IsScalableVector()                                                             \
              ? PrimType::ScalableVector(DLDataTypeCode::kDLFloat, 32, x_ty.VScaleFactor())   \
              : PrimType::Float(32, x_ty.lanes());                                            \
      PrimExpr x_fp32 = prim::Cast(f32_ty, x, loc);                                           \
      PrimExpr result_fp32 = Call(f32_ty, op, {x_fp32}, {}, {}, loc).as_or_throw<PrimExpr>(); \
      return prim::Cast(bf16_ty, result_fp32, loc);                                           \
    } else {                                                                                  \
      return Call(x_ty, op, {x}, {}, {}, loc).as_or_throw<PrimExpr>();                        \
    }                                                                                         \
  }

#define TVM_DECLARE_INTRIN_UNARY(OpName) \
  TVM_DECLARE_INTRIN_UNARY_WITH_CHECK(OpName, [](const char*, const PrimType&) {})

#define TVM_DECLARE_FLOAT_INTRIN_UNARY(OpName) \
  TVM_DECLARE_INTRIN_UNARY_WITH_CHECK(OpName, CheckMathUnaryOpInputDType)

TVM_DECLARE_INTRIN_UNARY(exp);
TVM_DECLARE_INTRIN_UNARY(exp2);
TVM_DECLARE_INTRIN_UNARY(exp10);
TVM_DECLARE_INTRIN_UNARY(erf);
TVM_DECLARE_FLOAT_INTRIN_UNARY(tanh);
TVM_DECLARE_INTRIN_UNARY(sigmoid);
TVM_DECLARE_INTRIN_UNARY(sqrt);
TVM_DECLARE_INTRIN_UNARY(rsqrt);
TVM_DECLARE_INTRIN_UNARY(log);
TVM_DECLARE_INTRIN_UNARY(log10);
TVM_DECLARE_INTRIN_UNARY(log1p);
TVM_DECLARE_INTRIN_UNARY(popcount);
TVM_DECLARE_FLOAT_INTRIN_UNARY(tan);
TVM_DECLARE_FLOAT_INTRIN_UNARY(cos);
TVM_DECLARE_FLOAT_INTRIN_UNARY(cosh);
TVM_DECLARE_FLOAT_INTRIN_UNARY(sin);
TVM_DECLARE_FLOAT_INTRIN_UNARY(sinh);
TVM_DECLARE_FLOAT_INTRIN_UNARY(asin);
TVM_DECLARE_FLOAT_INTRIN_UNARY(acos);
TVM_DECLARE_FLOAT_INTRIN_UNARY(atan);
TVM_DECLARE_FLOAT_INTRIN_UNARY(acosh);
TVM_DECLARE_FLOAT_INTRIN_UNARY(asinh);
TVM_DECLARE_FLOAT_INTRIN_UNARY(atanh);

#define TVM_DECLARE_INTRIN_BINARY(OpName)                                       \
  inline PrimExpr OpName(PrimExpr x, PrimExpr y, Location loc = UnknownLoc()) { \
    static const Op op = Op::Get("prim." #OpName);                              \
    return Call(x.ty(), op, {x, y}, {}, {}, loc).as_or_throw<PrimExpr>();       \
  }

TVM_DECLARE_INTRIN_BINARY(atan2);
TVM_DECLARE_INTRIN_BINARY(nextafter);
TVM_DECLARE_INTRIN_BINARY(copysign);
TVM_DECLARE_INTRIN_BINARY(hypot);
TVM_DECLARE_INTRIN_BINARY(ldexp);

template <typename FReduce>
inline PrimExpr foldl(FReduce freduce, PrimExpr init_value, const ffi::Array<PrimExpr>& values,
                      Location loc = UnknownLoc()) {
  for (PrimExpr val : values) {
    init_value = freduce(init_value, val, loc);
  }
  return init_value;
}

}  // namespace prim

}  // namespace tvm
#endif  // TVM_IR_PRIM_OP_H_