//===----- hlsl_intrinsics.h - HLSL definitions for intrinsics ----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _HLSL_HLSL_INTRINSICS_H_
#define _HLSL_HLSL_INTRINSICS_H_

#include "hlsl/hlsl_intrinsic_helpers.h"

namespace hlsl {

// Generated by clang-tblgen from HLSLIntrinsics.td (detail/inline intrinsics).
#include "hlsl_inline_intrinsics_gen.inc"

//===----------------------------------------------------------------------===//
// asfloat builtins
//===----------------------------------------------------------------------===//

/// \fn float asfloat(T Val)
/// \brief Interprets the bit pattern of x as float point number.
/// \param Val The input value.

template <typename T, int R, int C>
constexpr matrix<float, R, C> asfloat(matrix<T, R, C> V) {
  return __detail::bit_cast<float, T, R, C>(V);
}

template <typename T, int N>
constexpr vector<float, N> asfloat(vector<T, N> V) {
  return __detail::bit_cast<float, T, N>(V);
}

template <typename T> constexpr float asfloat(T F) {
  return __detail::bit_cast<float, T>(F);
}

//===----------------------------------------------------------------------===//
// asint builtins
//===----------------------------------------------------------------------===//

/// \fn int asint(T Val)
/// \brief Interprets the bit pattern of x as an integer.
/// \param Val The input value.

template <typename T, int R, int C>
constexpr matrix<int, R, C> asint(matrix<T, R, C> V) {
  return __detail::bit_cast<int, T, R, C>(V);
}

template <typename T, int N> constexpr vector<int, N> asint(vector<T, N> V) {
  return __detail::bit_cast<int, T, N>(V);
}

template <typename T> constexpr int asint(T F) {
  return __detail::bit_cast<int, T>(F);
}

//===----------------------------------------------------------------------===//
// asint16 builtins
//===----------------------------------------------------------------------===//

/// \fn int16_t asint16(T X)
/// \brief Interprets the bit pattern of \a X as an 16-bit integer.
/// \param X The input value.

#ifdef __HLSL_ENABLE_16_BIT

template <typename T, int N>
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
constexpr __detail::enable_if_t<__detail::is_same<int16_t, T>::value ||
                                    __detail::is_same<uint16_t, T>::value ||
                                    __detail::is_same<half, T>::value,
                                vector<int16_t, N>> asint16(vector<T, N> V) {
  return __detail::bit_cast<int16_t, T, N>(V);
}

template <typename T>
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
constexpr __detail::enable_if_t<__detail::is_same<int16_t, T>::value ||
                                    __detail::is_same<uint16_t, T>::value ||
                                    __detail::is_same<half, T>::value,
                                int16_t> asint16(T F) {
  return __detail::bit_cast<int16_t, T>(F);
}
#endif

//===----------------------------------------------------------------------===//
// asuint builtins
//===----------------------------------------------------------------------===//

/// \fn uint asuint(T Val)
/// \brief Interprets the bit pattern of x as an unsigned integer.
/// \param Val The input value.

template <typename T, int R, int C>
constexpr matrix<uint, R, C> asuint(matrix<T, R, C> V) {
  return __detail::bit_cast<uint, T, R, C>(V);
}

template <typename T, int N> constexpr vector<uint, N> asuint(vector<T, N> V) {
  return __detail::bit_cast<uint, T, N>(V);
}

template <typename T> constexpr uint asuint(T F) {
  return __detail::bit_cast<uint, T>(F);
}

//===----------------------------------------------------------------------===//
// asuint splitdouble builtins
//===----------------------------------------------------------------------===//

/// \fn void asuint(double D, out uint lowbits, out int highbits)
/// \brief Split and interprets the lowbits and highbits of double D into uints.
/// \param D The input double.
/// \param lowbits The output lowbits of D.
/// \param highbits The output highbits of D.
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_splitdouble)
void asuint(double, out uint, out uint);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_splitdouble)
void asuint(double2, out uint2, out uint2);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_splitdouble)
void asuint(double3, out uint3, out uint3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_splitdouble)
void asuint(double4, out uint4, out uint4);
template <int R, int C>
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_splitdouble)
void asuint(matrix<double, R, C>, out matrix<uint, R, C>,
            out matrix<uint, R, C>);
//===----------------------------------------------------------------------===//
// asuint16 builtins
//===----------------------------------------------------------------------===//

/// \fn uint16_t asuint16(T X)
/// \brief Interprets the bit pattern of \a X as an 16-bit unsigned integer.
/// \param X The input value.

#ifdef __HLSL_ENABLE_16_BIT

template <typename T, int N>
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
constexpr __detail::enable_if_t<__detail::is_same<int16_t, T>::value ||
                                    __detail::is_same<uint16_t, T>::value ||
                                    __detail::is_same<half, T>::value,
                                vector<uint16_t, N>> asuint16(vector<T, N> V) {
  return __detail::bit_cast<uint16_t, T, N>(V);
}

template <typename T>
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
constexpr __detail::enable_if_t<__detail::is_same<int16_t, T>::value ||
                                    __detail::is_same<uint16_t, T>::value ||
                                    __detail::is_same<half, T>::value,
                                uint16_t> asuint16(T F) {
  return __detail::bit_cast<uint16_t, T>(F);
}
#endif

//===----------------------------------------------------------------------===//
// firstbithigh builtins
//===----------------------------------------------------------------------===//

/// \fn T firstbithigh(T Val)
/// \brief Returns the location of the first set bit starting from the lowest
/// order bit and working upward, per component.
/// \param Val the input value.

#ifdef __HLSL_ENABLE_16_BIT

template <typename T>
_HLSL_AVAILABILITY(shadermodel, 6.2)
const inline __detail::enable_if_t<__detail::is_same<int16_t, T>::value ||
                                       __detail::is_same<uint16_t, T>::value,
                                   uint> firstbithigh(T X) {
  return __detail::firstbithigh_impl<uint, T, 16>(X);
}

template <typename T, int N>
_HLSL_AVAILABILITY(shadermodel, 6.2)
const
    inline __detail::enable_if_t<__detail::is_same<int16_t, T>::value ||
                                     __detail::is_same<uint16_t, T>::value,
                                 vector<uint, N>> firstbithigh(vector<T, N> X) {
  return __detail::firstbithigh_impl<vector<uint, N>, vector<T, N>, 16>(X);
}

#endif

template <typename T>
const inline __detail::enable_if_t<
    __detail::is_same<int, T>::value || __detail::is_same<uint, T>::value, uint>
firstbithigh(T X) {
  return __detail::firstbithigh_impl<uint, T, 32>(X);
}

template <typename T, int N>
const inline __detail::enable_if_t<__detail::is_same<int, T>::value ||
                                       __detail::is_same<uint, T>::value,
                                   vector<uint, N>>
firstbithigh(vector<T, N> X) {
  return __detail::firstbithigh_impl<vector<uint, N>, vector<T, N>, 32>(X);
}

template <typename T>
const inline __detail::enable_if_t<__detail::is_same<int64_t, T>::value ||
                                       __detail::is_same<uint64_t, T>::value,
                                   uint>
firstbithigh(T X) {
  return __detail::firstbithigh_impl<uint, T, 64>(X);
}

template <typename T, int N>
const inline __detail::enable_if_t<__detail::is_same<int64_t, T>::value ||
                                       __detail::is_same<uint64_t, T>::value,
                                   vector<uint, N>>
firstbithigh(vector<T, N> X) {
  return __detail::firstbithigh_impl<vector<uint, N>, vector<T, N>, 64>(X);
}

//===----------------------------------------------------------------------===//
// mul builtins
//===----------------------------------------------------------------------===//

/// \fn R mul(X x, Y y)
/// \brief Multiplies x and y using matrix math.
/// \param x [in] The first input value. If x is a vector, it is treated as a
///   row vector.
/// \param y [in] The second input value. If y is a vector, it is treated as a
///   column vector.
///
/// The inner dimension x-columns and y-rows must be equal. The result has the
/// dimension x-rows x y-columns. When both x and y are vectors, the result is
/// a dot product (scalar). Scalar operands are multiplied element-wise.
///
/// This function supports 9 overloaded forms:
///   1. scalar * scalar -> scalar
///   2. scalar * vector -> vector
///   3. scalar * matrix -> matrix
///   4. vector * scalar -> vector
///   5. vector * vector -> scalar (dot product)
///   6. vector * matrix -> vector
///   7. matrix * scalar -> matrix
///   8. matrix * vector -> vector
///   9. matrix * matrix -> matrix

// Cases 1, 2, 3, 4, 5, and 7 of mul are defined below as
// header-only implementations because they are elementwise operations and dot
// products easily expressed in HLSL.

// Cases 6, 8, and 9 are defined in hlsl_alias_intrinsics.h to alias the mul
// builtin so that they can be lowered to the llvm.matrix.multiply intrinsic
// which is not exposed directly to HLSL.

// Case 1: scalar * scalar -> scalar
template <typename T>
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
constexpr __detail::enable_if_t<__detail::is_arithmetic<T>::Value &&
                                    __detail::is_same<half, T>::value,
                                T> mul(T x, T y) {
  return x * y;
}

template <typename T>
constexpr __detail::enable_if_t<
    __detail::is_arithmetic<T>::Value && !__detail::is_same<half, T>::value, T>
mul(T x, T y) {
  return x * y;
}

// Case 2: scalar * vector -> vector
template <int N>
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
constexpr vector<half, N> mul(half x, vector<half, N> y) {
  return x * y;
}

template <typename T, int N> constexpr vector<T, N> mul(T x, vector<T, N> y) {
  return x * y;
}

// Case 3: scalar * matrix -> matrix
template <int R, int C>
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
constexpr matrix<half, R, C> mul(half x, matrix<half, R, C> y) {
  return x * y;
}

template <typename T, int R, int C>
constexpr matrix<T, R, C> mul(T x, matrix<T, R, C> y) {
  return x * y;
}

// Case 4: vector * scalar -> vector
template <int N>
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
constexpr vector<half, N> mul(vector<half, N> x, half y) {
  return x * y;
}

template <typename T, int N> constexpr vector<T, N> mul(vector<T, N> x, T y) {
  return x * y;
}

// Case 5: vector * vector -> scalar (dot product)
template <int N>
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
half mul(vector<half, N> x, vector<half, N> y) {
  return __detail::mul_vec_impl(x, y);
}

template <typename T, int N> T mul(vector<T, N> x, vector<T, N> y) {
  return __detail::mul_vec_impl(x, y);
}

// Case 7: matrix * scalar -> matrix
template <int R, int C>
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
constexpr matrix<half, R, C> mul(matrix<half, R, C> x, half y) {
  return x * y;
}

template <typename T, int R, int C>
constexpr matrix<T, R, C> mul(matrix<T, R, C> x, T y) {
  return x * y;
}

} // namespace hlsl
#endif //_HLSL_HLSL_INTRINSICS_H_
