Skip to content

Commit

Permalink
[libc++] <experimental/simd> Add unary operators for class simd
Browse files Browse the repository at this point in the history
  • Loading branch information
joy2myself committed Sep 3, 2024
1 parent e55dee3 commit e66d3a1
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 1 deletion.
10 changes: 10 additions & 0 deletions libcxx/include/experimental/__simd/scalar.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ 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 void __increment(_SimdStorage& __s) noexcept { ++__s.__data; }

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

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

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

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

template <class _Tp>
Expand Down
45 changes: 44 additions & 1 deletion libcxx/include/experimental/__simd/simd.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,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 static_cast<_Simd>(_Impl::__bitwise_not((*static_cast<const _Simd*>(this)).__s_));
}
};

// 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 Down Expand Up @@ -88,6 +102,35 @@ 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 static_cast<mask_type>(_Impl::__negate(__s_)); }

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

_LIBCPP_HIDE_FROM_ABI simd operator-() const noexcept { return static_cast<simd>(_Impl::__unary_minus(__s_)); }
};

template <class _Tp, class _Abi>
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 @@ -86,6 +86,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 void __increment(_SimdStorage& __s) noexcept { __s.__data = __s.__data + 1; }

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

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

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

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

template <class _Tp, int _Np>
Expand Down
187 changes: 187 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,187 @@
//===----------------------------------------------------------------------===//
//
// 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

// FIXME: Fatal error with following targets (remove XFAIL when fixed):
// Pass-by-value arguments with alignment greater than register width are not supported.
// XFAIL: target=powerpc{{.*}}-ibm-aix7.2.5.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>(!static_cast<T>(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;
}

0 comments on commit e66d3a1

Please sign in to comment.