Skip to content

[libc++] <experimental/simd> Add unary operators for class simd #104764

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libcxx/docs/Status/ParallelismProjects.csv
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Section,Description,Dependencies,Assignee,Complete
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd load constructor <https://github.com/llvm/llvm-project/pull/76610>`_", None, Yin Zhang, |Complete|
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd subscript operators <https://github.com/llvm/llvm-project/pull/68960>`_", None, Yin Zhang, |Complete|
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd copy functions <https://github.com/llvm/llvm-project/pull/78935>`_", None, Yin Zhang, |Complete|
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd unary operators <https://github.com/llvm/llvm-project/pull/104764>`_", None, Yin Zhang, |Complete|
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "Class template simd implementation", None, Yin Zhang, |In Progress|
| `[parallel.simd.nonmembers] <https://wg21.link/N4808>`_, "simd non-member operations", None, Yin Zhang, |In Progress|
| `[parallel.simd.mask.class] <https://wg21.link/N4808>`_, "`Class template simd_mask declaration and alias <https://reviews.llvm.org/D144362>`_", [parallel.simd.abi], Yin Zhang, |Complete|
Expand Down
14 changes: 14 additions & 0 deletions libcxx/include/experimental/__simd/scalar.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,20 @@ struct __simd_operations<_Tp, simd_abi::__scalar> {
static _LIBCPP_HIDE_FROM_ABI void __store(_SimdStorage __s, _Up* __mem) noexcept {
*__mem = static_cast<_Up>(__s.__data);
}

static _LIBCPP_HIDE_FROM_ABI void __increment(_SimdStorage& __s) noexcept { ++__s.__data; }

static _LIBCPP_HIDE_FROM_ABI void __decrement(_SimdStorage& __s) noexcept { --__s.__data; }

static _LIBCPP_HIDE_FROM_ABI _MaskStorage __negate(_SimdStorage __s) noexcept { return {!__s.__data}; }

static _LIBCPP_HIDE_FROM_ABI _SimdStorage __bitwise_not(_SimdStorage __s) noexcept {
return {static_cast<_Tp>(~__s.__data)};
}

static _LIBCPP_HIDE_FROM_ABI _SimdStorage __unary_minus(_SimdStorage __s) noexcept {
return {static_cast<_Tp>(-__s.__data)};
}
};

template <class _Tp>
Expand Down
54 changes: 53 additions & 1 deletion libcxx/include/experimental/__simd/simd.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include <__config>
#include <__type_traits/enable_if.h>
#include <__type_traits/is_integral.h>
#include <__type_traits/is_same.h>
#include <__type_traits/remove_cvref.h>
#include <__utility/forward.h>
Expand All @@ -26,15 +27,29 @@
_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL
inline namespace parallelism_v2 {

template <class _Simd, class _Impl, bool>
class __simd_int_operators {};

template <class _Simd, class _Impl>
class __simd_int_operators<_Simd, _Impl, true> {
public:
// unary operators for integral _Tp
_LIBCPP_HIDE_FROM_ABI _Simd operator~() const noexcept {
return _Simd(_Impl::__bitwise_not((*static_cast<const _Simd*>(this)).__s_), _Simd::__storage_tag);
}
};

// class template simd [simd.class]
// TODO: implement simd class
template <class _Tp, class _Abi>
class simd {
class simd : public __simd_int_operators<simd<_Tp, _Abi>, __simd_operations<_Tp, _Abi>, is_integral_v<_Tp>> {
using _Impl = __simd_operations<_Tp, _Abi>;
using _Storage = typename _Impl::_SimdStorage;

_Storage __s_;

friend class __simd_int_operators<simd, _Impl, true>;

public:
using value_type = _Tp;
using reference = __simd_reference<_Tp, _Storage, value_type>;
Expand All @@ -45,6 +60,12 @@ class simd {

_LIBCPP_HIDE_FROM_ABI simd() noexcept = default;

// explicit conversion from and to implementation-defined types
struct __storage_tag_t {};
static constexpr __storage_tag_t __storage_tag{};
explicit _LIBCPP_HIDE_FROM_ABI operator _Storage() const { return __s_; }
explicit _LIBCPP_HIDE_FROM_ABI simd(const _Storage& __s, __storage_tag_t) : __s_(__s) {}

// broadcast constructor
template <class _Up, enable_if_t<__can_broadcast_v<value_type, __remove_cvref_t<_Up>>, int> = 0>
_LIBCPP_HIDE_FROM_ABI simd(_Up&& __v) noexcept : __s_(_Impl::__broadcast(static_cast<value_type>(__v))) {}
Expand Down Expand Up @@ -85,6 +106,37 @@ class simd {
// scalar access [simd.subscr]
_LIBCPP_HIDE_FROM_ABI reference operator[](size_t __i) noexcept { return reference(__s_, __i); }
_LIBCPP_HIDE_FROM_ABI value_type operator[](size_t __i) const noexcept { return __s_.__get(__i); }

// simd unary operators
_LIBCPP_HIDE_FROM_ABI simd& operator++() noexcept {
_Impl::__increment(__s_);
return *this;
}

_LIBCPP_HIDE_FROM_ABI simd operator++(int) noexcept {
simd __r = *this;
_Impl::__increment(__s_);
return __r;
}

_LIBCPP_HIDE_FROM_ABI simd& operator--() noexcept {
_Impl::__decrement(__s_);
return *this;
}

_LIBCPP_HIDE_FROM_ABI simd operator--(int) noexcept {
simd __r = *this;
_Impl::__decrement(__s_);
return __r;
}

_LIBCPP_HIDE_FROM_ABI mask_type operator!() const noexcept {
return mask_type(_Impl::__negate(__s_), mask_type::__storage_tag);
}

_LIBCPP_HIDE_FROM_ABI simd operator+() const noexcept { return *this; }

_LIBCPP_HIDE_FROM_ABI simd operator-() const noexcept { return simd(_Impl::__unary_minus(__s_), __storage_tag); }
};

template <class _Tp, class _Abi>
Expand Down
6 changes: 6 additions & 0 deletions libcxx/include/experimental/__simd/simd_mask.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ class simd_mask {

_LIBCPP_HIDE_FROM_ABI simd_mask() noexcept = default;

// explicit conversion from and to implementation-defined types
struct __storage_tag_t {};
static constexpr __storage_tag_t __storage_tag{};
explicit _LIBCPP_HIDE_FROM_ABI operator _Storage() const { return __s_; }
explicit _LIBCPP_HIDE_FROM_ABI simd_mask(const _Storage& __s, __storage_tag_t) : __s_(__s) {}

// broadcast constructor
_LIBCPP_HIDE_FROM_ABI explicit simd_mask(value_type __v) noexcept : __s_(_Impl::__broadcast(__v)) {}

Expand Down
10 changes: 10 additions & 0 deletions libcxx/include/experimental/__simd/vec_ext.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ struct __simd_operations<_Tp, simd_abi::__vec_ext<_Np>> {
for (size_t __i = 0; __i < _Np; __i++)
__mem[__i] = static_cast<_Up>(__s.__data[__i]);
}

static _LIBCPP_HIDE_FROM_ABI void __increment(_SimdStorage& __s) noexcept { __s.__data = __s.__data + 1; }

static _LIBCPP_HIDE_FROM_ABI void __decrement(_SimdStorage& __s) noexcept { __s.__data = __s.__data - 1; }

static _LIBCPP_HIDE_FROM_ABI _MaskStorage __negate(_SimdStorage __s) noexcept { return {!__s.__data}; }

static _LIBCPP_HIDE_FROM_ABI _SimdStorage __bitwise_not(_SimdStorage __s) noexcept { return {~__s.__data}; }

static _LIBCPP_HIDE_FROM_ABI _SimdStorage __unary_minus(_SimdStorage __s) noexcept { return {-__s.__data}; }
};

template <class _Tp, int _Np>
Expand Down
191 changes: 191 additions & 0 deletions libcxx/test/std/experimental/simd/simd.class/simd_unary.pass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14

// Older versions of clang may encounter a backend error (see 0295c2ad):
// Pass-by-value arguments with alignment greater than register width are not supported.
// XFAIL: target=powerpc{{.*}}-ibm-{{.*}} && (clang-17 || clang-18)

// FIXME: The following issue occurs on macOS 13.7 with Apple clang 15.0.0:
// clang: error: unable to execute command: Illegal instruction: 4
// XFAIL: target=x86_64-apple-macosx13.7

// <experimental/simd>
//
// [simd.class]
// simd& operator++() noexcept;
// simd operator++(int) noexcept;
// simd& operator--() noexcept;
// simd operator--(int) noexcept;
// mask_type operator!() const noexcept;
// simd operator~() const noexcept;
// simd operator+() const noexcept;
// simd operator-() const noexcept;

#include "../test_utils.h"
#include <experimental/simd>

namespace ex = std::experimental::parallelism_v2;

template <class T, std::size_t>
struct CheckSimdPrefixIncrementOperator {
template <class SimdAbi>
void operator()() {
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
static_assert(noexcept(++origin_simd));
std::array<T, array_size> expected_return_value, expected_value;
for (size_t i = 0; i < array_size; ++i) {
expected_return_value[i] = static_cast<T>(i) + 1;
expected_value[i] = static_cast<T>(i) + 1;
}
assert_simd_values_equal<array_size>(++origin_simd, expected_return_value);
assert_simd_values_equal<array_size>(origin_simd, expected_value);
}
};

template <class T, std::size_t>
struct CheckSimdPostfixIncrementOperator {
template <class SimdAbi>
void operator()() {
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
static_assert(noexcept(origin_simd++));
std::array<T, array_size> expected_return_value, expected_value;
for (size_t i = 0; i < array_size; ++i) {
expected_return_value[i] = static_cast<T>(i);
expected_value[i] = static_cast<T>(i) + 1;
}
assert_simd_values_equal<array_size>(origin_simd++, expected_return_value);
assert_simd_values_equal<array_size>(origin_simd, expected_value);
}
};

template <class T, std::size_t>
struct CheckSimdPrefixDecrementOperator {
template <class SimdAbi>
void operator()() {
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
static_assert(noexcept(--origin_simd));
std::array<T, array_size> expected_return_value, expected_value;
for (size_t i = 0; i < array_size; ++i) {
expected_return_value[i] = static_cast<T>(i) - 1;
expected_value[i] = static_cast<T>(i) - 1;
}
assert_simd_values_equal<array_size>(--origin_simd, expected_return_value);
assert_simd_values_equal<array_size>(origin_simd, expected_value);
}
};

template <class T, std::size_t>
struct CheckSimdPostfixDecrementOperator {
template <class SimdAbi>
void operator()() {
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
static_assert(noexcept(origin_simd--));
std::array<T, array_size> expected_return_value, expected_value;
for (size_t i = 0; i < array_size; ++i) {
expected_return_value[i] = static_cast<T>(i);
expected_value[i] = static_cast<T>(i) - 1;
}
assert_simd_values_equal<array_size>(origin_simd--, expected_return_value);
assert_simd_values_equal<array_size>(origin_simd, expected_value);
}
};

template <class T, std::size_t>
struct CheckSimdNegationOperator {
template <class SimdAbi>
void operator()() {
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
static_assert(noexcept(!origin_simd));
std::array<bool, array_size> expected_value;
for (size_t i = 0; i < array_size; ++i)
expected_value[i] = !static_cast<bool>(i);
assert_simd_mask_values_equal<array_size>(!origin_simd, expected_value);
}
};

template <class T, std::size_t>
struct CheckSimdBitwiseNotOperator {
template <class SimdAbi>
void operator()() {
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
static_assert(noexcept(~origin_simd));
std::array<T, array_size> expected_value;
for (size_t i = 0; i < array_size; ++i)
expected_value[i] = ~static_cast<T>(i);
assert_simd_values_equal<array_size>(~origin_simd, expected_value);
}
};

template <class T, std::size_t>
struct CheckSimdPositiveSignOperator {
template <class SimdAbi>
void operator()() {
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
static_assert(noexcept(+origin_simd));
std::array<T, array_size> expected_value;
for (size_t i = 0; i < array_size; ++i)
expected_value[i] = +static_cast<T>(i);
assert_simd_values_equal<array_size>(+origin_simd, expected_value);
}
};

template <class T, std::size_t>
struct CheckSimdNegativeSignOperator {
template <class SimdAbi>
void operator()() {
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
static_assert(noexcept(-origin_simd));
std::array<T, array_size> expected_value;
for (size_t i = 0; i < array_size; ++i)
expected_value[i] = -static_cast<T>(i);
assert_simd_values_equal<array_size>(-origin_simd, expected_value);
}
};

template <class T, class SimdAbi = ex::simd_abi::compatible<T>, class = void>
struct has_bitwise_not_op : std::false_type {};

template <class T, class SimdAbi>
struct has_bitwise_not_op<T, SimdAbi, std::void_t<decltype(~std::declval<ex::simd<T, SimdAbi>>())>> : std::true_type {};

template <class T, std::size_t>
struct CheckSimdBitwiseNotTraits {
template <class SimdAbi>
void operator()() {
// This function shall not participate in overload resolution unless
// T is an integral type.
if constexpr (std::is_integral_v<T>)
static_assert(has_bitwise_not_op<T, SimdAbi>::value);
// T is not an integral type.
else
static_assert(!has_bitwise_not_op<T, SimdAbi>::value);
}
};

int main(int, char**) {
test_all_simd_abi<CheckSimdPrefixIncrementOperator>();
test_all_simd_abi<CheckSimdPostfixIncrementOperator>();
test_all_simd_abi<CheckSimdPrefixDecrementOperator>();
test_all_simd_abi<CheckSimdPostfixDecrementOperator>();
test_all_simd_abi<CheckSimdNegationOperator>();
types::for_each(types::integer_types(), TestAllSimdAbiFunctor<CheckSimdBitwiseNotOperator>());
test_all_simd_abi<CheckSimdPositiveSignOperator>();
test_all_simd_abi<CheckSimdNegativeSignOperator>();
test_all_simd_abi<CheckSimdBitwiseNotTraits>();
return 0;
}
Loading