Skip to content

Commit

Permalink
[SYCL][COMPAT] Added compare and unordered compare operations (#12998)
Browse files Browse the repository at this point in the history
Adds the following ordered and unordered comparisons:

- compare
- undered_compare
- compare_both (wrapper for sycl::vec<ValueT, 2>)
- undered_compare_both (wrapper for sycl::vec<ValueT, 2>)
- compare_mask
- unordered_compare_mask
  • Loading branch information
Alcpz authored Apr 16, 2024
1 parent 4b14d70 commit af491ee
Show file tree
Hide file tree
Showing 4 changed files with 541 additions and 0 deletions.
44 changes: 44 additions & 0 deletions sycl/doc/syclcompat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Specifically, this library depends on the following SYCL extensions:
If available, the following extensions extend SYCLcompat functionality:

* [sycl_ext_intel_device_info](https://github.com/intel/llvm/blob/sycl/sycl/doc/extensions/supported/sycl_ext_intel_device_info.md) \[Optional\]
* [sycl_ext_oneapi_bfloat16_math_functions](../extensions/experimental/sycl_ext_oneapi_bfloat16_math_functions.asciidoc) \[Optional\]

## Usage

Expand Down Expand Up @@ -1275,6 +1276,10 @@ static kernel_function_info get_kernel_function_info(const void *function);
length. `syclcompat::length` provides a templated version that wraps over
`sycl::length`.

`compare`, `unordered_compare`, `compare_both`, `unordered_compare_both`,
`compare_mask`, and `unordered_compare_mask`, handle both ordered and unordered
comparisons.

`vectorized_max` and `vectorized_min` are binary operations returning the
max/min of two arguments, where each argument is treated as a `sycl::vec` type.
`vectorized_isgreater` performs elementwise `isgreater`, treating each argument
Expand All @@ -1292,6 +1297,45 @@ inline float fast_length(const float *a, int len);
template <typename ValueT>
inline ValueT length(const ValueT *a, const int len);

// The following definition is enabled when BinaryOperation(ValueT, ValueT) returns bool
// std::enable_if_t<std::is_same_v<std::invoke_result_t<BinaryOperation, ValueT, ValueT>, bool>, bool>
template <typename ValueT, class BinaryOperation>
inline bool
compare(const ValueT a, const ValueT b, const BinaryOperation binary_op);
template <typename ValueT, class BinaryOperation>
inline std::enable_if_t<ValueT::size() == 2, ValueT>
compare(const ValueT a, const ValueT b, const BinaryOperation binary_op);

// The following definition is enabled when BinaryOperation(ValueT, ValueT) returns bool
// std::enable_if_t<std::is_same_v<std::invoke_result_t<BinaryOperation, ValueT, ValueT>, bool>, bool>
template <typename ValueT, class BinaryOperation>
inline bool
unordered_compare(const ValueT a, const ValueT b,
const BinaryOperation binary_op);
template <typename ValueT, class BinaryOperation>
inline std::enable_if_t<ValueT::size() == 2, ValueT>
unordered_compare(const ValueT a, const ValueT b,
const BinaryOperation binary_op);

template <typename ValueT, class BinaryOperation>
inline std::enable_if_t<ValueT::size() == 2, bool>
compare_both(const ValueT a, const ValueT b, const BinaryOperation binary_op);
template <typename ValueT, class BinaryOperation>

inline std::enable_if_t<ValueT::size() == 2, bool>
unordered_compare_both(const ValueT a, const ValueT b,
const BinaryOperation binary_op);

template <typename ValueT, class BinaryOperation>
inline unsigned compare_mask(const sycl::vec<ValueT, 2> a,
const sycl::vec<ValueT, 2> b,
const BinaryOperation binary_op);

template <typename ValueT, class BinaryOperation>
inline unsigned unordered_compare_mask(const sycl::vec<ValueT, 2> a,
const sycl::vec<ValueT, 2> b,
const BinaryOperation binary_op);

template <typename S, typename T> inline T vectorized_max(T a, T b);

template <typename S, typename T> inline T vectorized_min(T a, T b);
Expand Down
124 changes: 124 additions & 0 deletions sycl/include/syclcompat/math.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ inline constexpr RetT extend_binary(AT a, BT b, CT c,
return second_op(extend_temp, extend_c);
}

template <typename ValueT> inline bool isnan(const ValueT a) {
return sycl::isnan(a);
}
#ifdef SYCL_EXT_ONEAPI_BFLOAT16_MATH_FUNCTIONS
inline bool isnan(const sycl::ext::oneapi::bfloat16 a) {
return sycl::ext::oneapi::experimental::isnan(a);
}
#endif

} // namespace detail

/// Compute fast_length for variable-length array
Expand Down Expand Up @@ -167,6 +176,121 @@ inline ValueT length(const ValueT *a, const int len) {
}
}

/// Performs comparison.
/// \param [in] a The first value
/// \param [in] b The second value
/// \param [in] binary_op functor that implements the binary operation
/// \returns the comparison result
template <typename ValueT, class BinaryOperation>
inline std::enable_if_t<
std::is_same_v<std::invoke_result_t<BinaryOperation, ValueT, ValueT>, bool>,
bool>
compare(const ValueT a, const ValueT b, const BinaryOperation binary_op) {
return binary_op(a, b);
}
template <typename ValueT>
inline std::enable_if_t<
std::is_same_v<std::invoke_result_t<std::not_equal_to<>, ValueT, ValueT>,
bool>,
bool>
compare(const ValueT a, const ValueT b, const std::not_equal_to<> binary_op) {
return !detail::isnan(a) && !detail::isnan(b) && binary_op(a, b);
}

/// Performs 2 element comparison.
/// \param [in] a The first value
/// \param [in] b The second value
/// \param [in] binary_op functor that implements the binary operation
/// \returns the comparison result
template <typename ValueT, class BinaryOperation>
inline std::enable_if_t<ValueT::size() == 2, ValueT>
compare(const ValueT a, const ValueT b, const BinaryOperation binary_op) {
return {compare(a[0], b[0], binary_op), compare(a[1], b[1], binary_op)};
}

/// Performs unordered comparison.
/// \param [in] a The first value
/// \param [in] b The second value
/// \param [in] binary_op functor that implements the binary operation
/// \returns the comparison result
template <typename ValueT, class BinaryOperation>
inline std::enable_if_t<
std::is_same_v<std::invoke_result_t<BinaryOperation, ValueT, ValueT>, bool>,
bool>
unordered_compare(const ValueT a, const ValueT b,
const BinaryOperation binary_op) {
return detail::isnan(a) || detail::isnan(b) || binary_op(a, b);
}

/// Performs 2 element unordered comparison.
/// \param [in] a The first value
/// \param [in] b The second value
/// \param [in] binary_op functor that implements the binary operation
/// \returns the comparison result
template <typename ValueT, class BinaryOperation>
inline std::enable_if_t<ValueT::size() == 2, ValueT>
unordered_compare(const ValueT a, const ValueT b,
const BinaryOperation binary_op) {
return {unordered_compare(a[0], b[0], binary_op),
unordered_compare(a[1], b[1], binary_op)};
}

/// Performs 2 element comparison and return true if both results are true.
/// \param [in] a The first value
/// \param [in] b The second value
/// \param [in] binary_op functor that implements the binary operation
/// \returns the comparison result
template <typename ValueT, class BinaryOperation>
inline std::enable_if_t<ValueT::size() == 2, bool>
compare_both(const ValueT a, const ValueT b, const BinaryOperation binary_op) {
return compare(a[0], b[0], binary_op) && compare(a[1], b[1], binary_op);
}

/// Performs 2 element unordered comparison and return true if both results are
/// true.
/// \param [in] a The first value
/// \param [in] b The second value
/// \param [in] binary_op functor that implements the binary operation
/// \returns the comparison result
template <typename ValueT, class BinaryOperation>
inline std::enable_if_t<ValueT::size() == 2, bool>
unordered_compare_both(const ValueT a, const ValueT b,
const BinaryOperation binary_op) {
return unordered_compare(a[0], b[0], binary_op) &&
unordered_compare(a[1], b[1], binary_op);
}

/// Performs 2 elements comparison, compare result of each element is 0 (false)
/// or 0xffff (true), returns an unsigned int by composing compare result of two
/// elements.
/// \param [in] a The first value
/// \param [in] b The second value
/// \param [in] binary_op functor that implements the binary operation
/// \returns the comparison result
template <typename ValueT, class BinaryOperation>
inline unsigned compare_mask(const sycl::vec<ValueT, 2> a,
const sycl::vec<ValueT, 2> b,
const BinaryOperation binary_op) {
// Since compare returns 0 or 1, -compare will be 0x00000000 or 0xFFFFFFFF
return ((-compare(a[0], b[0], binary_op)) << 16) |
((-compare(a[1], b[1], binary_op)) & 0xFFFF);
}

/// Performs 2 elements unordered comparison, compare result of each element is
/// 0 (false) or 0xffff (true), returns an unsigned int by composing compare
/// result of two elements.
/// \param [in] a The first value
/// \param [in] b The second value
/// \param [in] binary_op functor that implements the binary operation
/// \returns the comparison result
template <typename ValueT, class BinaryOperation>
inline unsigned unordered_compare_mask(const sycl::vec<ValueT, 2> a,
const sycl::vec<ValueT, 2> b,
const BinaryOperation binary_op) {
return ((-unordered_compare(a[0], b[0], binary_op)) << 16) |
((-unordered_compare(a[1], b[1], binary_op)) & 0xFFFF);
}

/// Compute vectorized max for two values, with each value treated as a vector
/// type \p S
/// \param [in] S The type of the vector
Expand Down
2 changes: 2 additions & 0 deletions sycl/test-e2e/syclcompat/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ void instantiate_all_types(Functor &&f) {
using value_type_list =
std::tuple<int, unsigned int, short, unsigned short, long, unsigned long,
long long, unsigned long long, float, double, sycl::half>;

using fp_type_list = std::tuple<float, double, sycl::half>;
Loading

0 comments on commit af491ee

Please sign in to comment.