Skip to content

Commit

Permalink
[SYCL] Fix return type of relational functions on scalars (#7414)
Browse files Browse the repository at this point in the history
This fix updates scalar versions of relation functions that now return
"bool"
instead of a signed integer in SYCL 2020 mode.

The fix is ABI-breaking and is put under SYCL2020_CONFORMANT_APIS guard.

This is a revival of the reverted patch
#5975.

Signed-off-by: Larsen, Steffen <steffen.larsen@intel.com>
Co-authored-by: aelovikov-intel <andrei.elovikov@intel.com>
  • Loading branch information
steffenlarsen and aelovikov-intel committed Nov 18, 2022
1 parent 26619e0 commit 45d516c
Show file tree
Hide file tree
Showing 4 changed files with 433 additions and 94 deletions.
125 changes: 36 additions & 89 deletions sycl/include/sycl/builtins.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1109,198 +1109,145 @@ fast_normalize(T p) __NOEXC {
return __sycl_std::__invoke_fast_normalize<T>(p);
}

/* --------------- 4.13.7 Relational functions. Device version --------------*/
// int isequal (half x, half y)
// shortn isequal (halfn x, halfn y)
// igeninteger32bit isequal (genfloatf x, genfloatf y)
// int isequal (double x,double y);
// longn isequal (doublen x, doublen y)
/* SYCL 1.2.1 ---- 4.13.7 Relational functions. -----------------------------*/
/* SYCL 2020 ---- 4.17.9 Relational functions. -----------------------------*/

template <typename T,
typename = detail::enable_if_t<detail::is_genfloat<T>::value, T>>
detail::common_rel_ret_t<T> isequal(T x, T y) __NOEXC {
return detail::RelConverter<T>::apply(
__sycl_std::__invoke_FOrdEqual<detail::rel_ret_t<T>>(x, y));
__sycl_std::__invoke_FOrdEqual<detail::internal_rel_ret_t<T>>(x, y));
}

// int isnotequal (half x, half y)
// shortn isnotequal (halfn x, halfn y)
// igeninteger32bit isnotequal (genfloatf x, genfloatf y)
// int isnotequal (double x, double y)
// longn isnotequal (doublen x, doublen y)
template <typename T,
typename = detail::enable_if_t<detail::is_genfloat<T>::value, T>>
detail::common_rel_ret_t<T> isnotequal(T x, T y) __NOEXC {
return detail::RelConverter<T>::apply(
__sycl_std::__invoke_FUnordNotEqual<detail::rel_ret_t<T>>(x, y));
__sycl_std::__invoke_FUnordNotEqual<detail::internal_rel_ret_t<T>>(x, y));
}

// int isgreater (half x, half y)
// shortn isgreater (halfn x, halfn y)
// igeninteger32bit isgreater (genfloatf x, genfloatf y)
// int isgreater (double x, double y)
// longn isgreater (doublen x, doublen y)
template <typename T,
typename = detail::enable_if_t<detail::is_genfloat<T>::value, T>>
detail::common_rel_ret_t<T> isgreater(T x, T y) __NOEXC {
return detail::RelConverter<T>::apply(
__sycl_std::__invoke_FOrdGreaterThan<detail::rel_ret_t<T>>(x, y));
__sycl_std::__invoke_FOrdGreaterThan<detail::internal_rel_ret_t<T>>(x,
y));
}

// int isgreaterequal (half x, half y)
// shortn isgreaterequal (halfn x, halfn y)
// igeninteger32bit isgreaterequal (genfloatf x, genfloatf y)
// int isgreaterequal (double x, double y)
// longn isgreaterequal (doublen x, doublen y)
template <typename T,
typename = detail::enable_if_t<detail::is_genfloat<T>::value, T>>
detail::common_rel_ret_t<T> isgreaterequal(T x, T y) __NOEXC {
return detail::RelConverter<T>::apply(
__sycl_std::__invoke_FOrdGreaterThanEqual<detail::rel_ret_t<T>>(x, y));
__sycl_std::__invoke_FOrdGreaterThanEqual<detail::internal_rel_ret_t<T>>(
x, y));
}

// int isless (half x, half y)
// shortn isless (halfn x, halfn y)
// igeninteger32bit isless (genfloatf x, genfloatf y)
// int isless (long x, long y)
// longn isless (doublen x, doublen y)
template <typename T,
typename = detail::enable_if_t<detail::is_genfloat<T>::value, T>>
detail::common_rel_ret_t<T> isless(T x, T y) __NOEXC {
return detail::RelConverter<T>::apply(
__sycl_std::__invoke_FOrdLessThan<detail::rel_ret_t<T>>(x, y));
__sycl_std::__invoke_FOrdLessThan<detail::internal_rel_ret_t<T>>(x, y));
}

// int islessequal (half x, half y)
// shortn islessequal (halfn x, halfn y)
// igeninteger32bit islessequal (genfloatf x, genfloatf y)
// int islessequal (double x, double y)
// longn islessequal (doublen x, doublen y)
template <typename T,
typename = detail::enable_if_t<detail::is_genfloat<T>::value, T>>
detail::common_rel_ret_t<T> islessequal(T x, T y) __NOEXC {
return detail::RelConverter<T>::apply(
__sycl_std::__invoke_FOrdLessThanEqual<detail::rel_ret_t<T>>(x, y));
__sycl_std::__invoke_FOrdLessThanEqual<detail::internal_rel_ret_t<T>>(x,
y));
}

// int islessgreater (half x, half y)
// shortn islessgreater (halfn x, halfn y)
// igeninteger32bit islessgreater (genfloatf x, genfloatf y)
// int islessgreater (double x, double y)
// longn islessgreater (doublen x, doublen y)
template <typename T,
typename = detail::enable_if_t<detail::is_genfloat<T>::value, T>>
detail::common_rel_ret_t<T> islessgreater(T x, T y) __NOEXC {
return detail::RelConverter<T>::apply(
__sycl_std::__invoke_FOrdNotEqual<detail::rel_ret_t<T>>(x, y));
__sycl_std::__invoke_FOrdNotEqual<detail::internal_rel_ret_t<T>>(x, y));
}

// int isfinite (half x)
// shortn isfinite (halfn x)
// igeninteger32bit isfinite (genfloatf x)
// int isfinite (double x)
// longn isfinite (doublen x)
template <typename T,
typename = detail::enable_if_t<detail::is_genfloat<T>::value, T>>
detail::common_rel_ret_t<T> isfinite(T x) __NOEXC {
return detail::RelConverter<T>::apply(
__sycl_std::__invoke_IsFinite<detail::rel_ret_t<T>>(x));
__sycl_std::__invoke_IsFinite<detail::internal_rel_ret_t<T>>(x));
}

// int isinf (half x)
// shortn isinf (halfn x)
// igeninteger32bit isinf (genfloatf x)
// int isinf (double x)
// longn isinf (doublen x)
template <typename T,
typename = detail::enable_if_t<detail::is_genfloat<T>::value, T>>
detail::common_rel_ret_t<T> isinf(T x) __NOEXC {
return detail::RelConverter<T>::apply(
__sycl_std::__invoke_IsInf<detail::rel_ret_t<T>>(x));
__sycl_std::__invoke_IsInf<detail::internal_rel_ret_t<T>>(x));
}

// int isnan (half x)
// shortn isnan (halfn x)
// igeninteger32bit isnan (genfloatf x)
// int isnan (double x)
// longn isnan (doublen x)
template <typename T,
typename = detail::enable_if_t<detail::is_genfloat<T>::value, T>>
detail::common_rel_ret_t<T> isnan(T x) __NOEXC {
return detail::RelConverter<T>::apply(
__sycl_std::__invoke_IsNan<detail::rel_ret_t<T>>(x));
__sycl_std::__invoke_IsNan<detail::internal_rel_ret_t<T>>(x));
}

// int isnormal (half x)
// shortn isnormal (halfn x)
// igeninteger32bit isnormal (genfloatf x)
// int isnormal (double x)
// longn isnormal (doublen x)
template <typename T,
typename = detail::enable_if_t<detail::is_genfloat<T>::value, T>>
detail::common_rel_ret_t<T> isnormal(T x) __NOEXC {
return detail::RelConverter<T>::apply(
__sycl_std::__invoke_IsNormal<detail::rel_ret_t<T>>(x));
__sycl_std::__invoke_IsNormal<detail::internal_rel_ret_t<T>>(x));
}

// int isordered (half x)
// shortn isordered (halfn x, halfn y)
// igeninteger32bit isordered (genfloatf x, genfloatf y)
// int isordered (double x, double y)
// longn isordered (doublen x, doublen y)
template <typename T,
typename = detail::enable_if_t<detail::is_genfloat<T>::value, T>>
detail::common_rel_ret_t<T> isordered(T x, T y) __NOEXC {
return detail::RelConverter<T>::apply(
__sycl_std::__invoke_Ordered<detail::rel_ret_t<T>>(x, y));
__sycl_std::__invoke_Ordered<detail::internal_rel_ret_t<T>>(x, y));
}

// int isunordered (half x, half y)
// shortn isunordered (halfn x, halfn y)
// igeninteger32bit isunordered (genfloatf x, genfloatf y)
// int isunordered (double x, double y)
// longn isunordered (doublen x, doublen y)
template <typename T,
typename = detail::enable_if_t<detail::is_genfloat<T>::value, T>>
detail::common_rel_ret_t<T> isunordered(T x, T y) __NOEXC {
return detail::RelConverter<T>::apply(
__sycl_std::__invoke_Unordered<detail::rel_ret_t<T>>(x, y));
__sycl_std::__invoke_Unordered<detail::internal_rel_ret_t<T>>(x, y));
}

// int signbit (half x)
// shortn signbit (halfn x)
// igeninteger32bit signbit (genfloatf x)
// int signbit (double)
// longn signbit (doublen x)
template <typename T,
typename = detail::enable_if_t<detail::is_genfloat<T>::value, T>>
detail::common_rel_ret_t<T> signbit(T x) __NOEXC {
return detail::RelConverter<T>::apply(
__sycl_std::__invoke_SignBitSet<detail::rel_ret_t<T>>(x));
__sycl_std::__invoke_SignBitSet<detail::internal_rel_ret_t<T>>(x));
}

namespace detail {
#if defined(SYCL2020_CONFORMANT_APIS) && SYCL_LANGUAGE_VERSION >= 202001
using anyall_ret_t = bool;
#else
using anyall_ret_t = int;
#endif
} // namespace detail

// int any (sigeninteger x)
template <typename T>
detail::enable_if_t<detail::is_sigeninteger<T>::value, int> any(T x) __NOEXC {
detail::enable_if_t<detail::is_sigeninteger<T>::value, detail::anyall_ret_t>
any(T x) __NOEXC {
return detail::Boolean<1>(int(detail::msbIsSet(x)));
}

// int any (vigeninteger x)
template <typename T>
detail::enable_if_t<detail::is_vigeninteger<T>::value, int> any(T x) __NOEXC {
detail::enable_if_t<detail::is_vigeninteger<T>::value, detail::anyall_ret_t>
any(T x) __NOEXC {
return detail::rel_sign_bit_test_ret_t<T>(
__sycl_std::__invoke_Any<detail::rel_sign_bit_test_ret_t<T>>(
detail::rel_sign_bit_test_arg_t<T>(x)));
}

// int all (sigeninteger x)
template <typename T>
detail::enable_if_t<detail::is_sigeninteger<T>::value, int> all(T x) __NOEXC {
detail::enable_if_t<detail::is_sigeninteger<T>::value, detail::anyall_ret_t>
all(T x) __NOEXC {
return detail::Boolean<1>(int(detail::msbIsSet(x)));
}

// int all (vigeninteger x)
template <typename T>
detail::enable_if_t<detail::is_vigeninteger<T>::value, int> all(T x) __NOEXC {
detail::enable_if_t<detail::is_vigeninteger<T>::value, detail::anyall_ret_t>
all(T x) __NOEXC {
return detail::rel_sign_bit_test_ret_t<T>(
__sycl_std::__invoke_All<detail::rel_sign_bit_test_ret_t<T>>(
detail::rel_sign_bit_test_arg_t<T>(x)));
Expand Down
3 changes: 2 additions & 1 deletion sycl/include/sycl/detail/boolean.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ template <> struct Boolean<1> {

// Cast to a signed interger type
template <typename T> operator T() const {
static_assert(is_sgeninteger<T>::value, "Invalid conversion");
static_assert(std::is_same<T, bool>::value || is_sgeninteger<T>::value,
"Invalid conversion");
return value;
}

Expand Down
46 changes: 42 additions & 4 deletions sycl/include/sycl/detail/generic_type_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -572,9 +572,37 @@ template <typename T> inline constexpr bool msbIsSet(const T x) {
return (x & msbMask(x));
}

#if defined(SYCL2020_CONFORMANT_APIS) && SYCL_LANGUAGE_VERSION >= 202001
// SYCL 2020 4.17.9 (Relation functions), e.g. table 178
//
// genbool isequal (genfloatf x, genfloatf y)
// genbool isequal (genfloatd x, genfloatd y)
//
// TODO: marray support isn't implemented yet.
template <typename T>
using common_rel_ret_t =
conditional_t<is_vgentype<T>::value, make_singed_integer_t<T>, bool>;

// TODO: Remove this when common_rel_ret_t is promoted.
template <typename T>
using internal_host_rel_ret_t =
conditional_t<is_vgentype<T>::value, make_singed_integer_t<T>, int>;
#else
// SYCL 1.2.1 4.13.7 (Relation functions), e.g.
//
// igeninteger32bit isequal (genfloatf x, genfloatf y)
// igeninteger64bit isequal (genfloatd x, genfloatd y)
//
// However, we have pre-existing bug so
//
// igeninteger32bit isequal (genfloatd x, genfloatd y)
//
// Fixing it would be an ABI-breaking change so isn't done.
template <typename T>
using common_rel_ret_t =
conditional_t<is_vgentype<T>::value, make_singed_integer_t<T>, int>;
template <typename T> using internal_host_rel_ret_t = common_rel_ret_t<T>;
#endif

// forward declaration
template <int N> struct Boolean;
Expand All @@ -598,11 +626,21 @@ template <typename T> struct RelationalReturnType {
#ifdef __SYCL_DEVICE_ONLY__
using type = Boolean<TryToGetNumElements<T>::value>;
#else
using type = common_rel_ret_t<T>;
// After changing the return type of scalar relational operations to boolean
// we keep the old representation of the internal implementation of the
// host-side builtins to avoid ABI-breaks.
// TODO: Use common_rel_ret_t when ABI break is allowed and the boolean return
// type for relationals are promoted out of SYCL2020_CONFORMANT_APIS.
// The scalar relational builtins in
// sycl/source/detail/builtins_relational.cpp should likewise be updated
// to return boolean values.
using type = internal_host_rel_ret_t<T>;
#endif
};

template <typename T> using rel_ret_t = typename RelationalReturnType<T>::type;
// Type representing the internal return type of relational builtins.
template <typename T>
using internal_rel_ret_t = typename RelationalReturnType<T>::type;

// Used for any and all built-in functions
template <typename T> struct RelationalTestForSignBitType {
Expand Down Expand Up @@ -634,7 +672,7 @@ struct RelConverter<
using ret_t = common_rel_ret_t<T>;
#else
using bool_t = Boolean<N>;
using ret_t = rel_ret_t<T>;
using ret_t = internal_rel_ret_t<T>;
#endif

static ret_t apply(bool_t value) {
Expand All @@ -653,7 +691,7 @@ struct RelConverter<
template <typename T>
struct RelConverter<
T, typename detail::enable_if_t<!TryToGetElementType<T>::value>> {
using R = rel_ret_t<T>;
using R = internal_rel_ret_t<T>;
#ifdef __SYCL_DEVICE_ONLY__
using value_t = bool;
#else
Expand Down
Loading

0 comments on commit 45d516c

Please sign in to comment.