[libcxx-commits] [libcxx] [libc++] <experimental/simd> Add unary operators for class simd (PR #104764)

via libcxx-commits libcxx-commits at lists.llvm.org
Mon Sep 9 22:00:19 PDT 2024


https://github.com/joy2myself updated https://github.com/llvm/llvm-project/pull/104764

>From e55dee32143cd6ed6fe5ac4e3b7b7af59111ebf8 Mon Sep 17 00:00:00 2001
From: Yin Zhang <zhangyin2018 at iscas.ac.cn>
Date: Mon, 19 Aug 2024 18:48:33 +0800
Subject: [PATCH 1/2] [libc++] <experimental/simd> Add explicit conversion from
 and to implementation-defined types

---
 libcxx/include/experimental/__simd/simd.h      | 4 ++++
 libcxx/include/experimental/__simd/simd_mask.h | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/include/experimental/__simd/simd.h b/libcxx/include/experimental/__simd/simd.h
index 37e334aad6da07..b494111b504407 100644
--- a/libcxx/include/experimental/__simd/simd.h
+++ b/libcxx/include/experimental/__simd/simd.h
@@ -44,6 +44,10 @@ class simd {
 
   _LIBCPP_HIDE_FROM_ABI simd() noexcept = default;
 
+  // explicit conversion from and to implementation-defined types
+  explicit _LIBCPP_HIDE_FROM_ABI operator _Storage() const;
+  explicit _LIBCPP_HIDE_FROM_ABI simd(const _Storage& __s) : __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))) {}
diff --git a/libcxx/include/experimental/__simd/simd_mask.h b/libcxx/include/experimental/__simd/simd_mask.h
index fd6dee2e28ee91..890edfc4d4d729 100644
--- a/libcxx/include/experimental/__simd/simd_mask.h
+++ b/libcxx/include/experimental/__simd/simd_mask.h
@@ -41,6 +41,10 @@ class simd_mask {
 
   _LIBCPP_HIDE_FROM_ABI simd_mask() noexcept = default;
 
+  // explicit conversion from and to implementation-defined types
+  explicit _LIBCPP_HIDE_FROM_ABI operator _Storage() const;
+  explicit _LIBCPP_HIDE_FROM_ABI simd_mask(const _Storage& __s) : __s_(__s) {}
+
   // broadcast constructor
   _LIBCPP_HIDE_FROM_ABI explicit simd_mask(value_type __v) noexcept : __s_(_Impl::__broadcast(__v)) {}
 

>From 3604ec9c6b03edf62082bdc159aaa64921d3479a Mon Sep 17 00:00:00 2001
From: Yin Zhang <zhangyin2018 at iscas.ac.cn>
Date: Tue, 9 Apr 2024 15:31:52 +0800
Subject: [PATCH 2/2] [libc++] <experimental/simd> Add unary operators for
 class simd

---
 libcxx/include/experimental/__simd/scalar.h   |  14 ++
 libcxx/include/experimental/__simd/simd.h     |  54 ++++-
 .../include/experimental/__simd/simd_mask.h   |   6 +-
 libcxx/include/experimental/__simd/vec_ext.h  |  10 +
 .../simd/simd.class/simd_unary.pass.cpp       | 187 ++++++++++++++++++
 5 files changed, 266 insertions(+), 5 deletions(-)
 create mode 100644 libcxx/test/std/experimental/simd/simd.class/simd_unary.pass.cpp

diff --git a/libcxx/include/experimental/__simd/scalar.h b/libcxx/include/experimental/__simd/scalar.h
index 1add4653209ace..789270a42083ee 100644
--- a/libcxx/include/experimental/__simd/scalar.h
+++ b/libcxx/include/experimental/__simd/scalar.h
@@ -67,6 +67,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>
diff --git a/libcxx/include/experimental/__simd/simd.h b/libcxx/include/experimental/__simd/simd.h
index b494111b504407..3657c29fe01bbd 100644
--- a/libcxx/include/experimental/__simd/simd.h
+++ b/libcxx/include/experimental/__simd/simd.h
@@ -10,6 +10,7 @@
 #ifndef _LIBCPP_EXPERIMENTAL___SIMD_SIMD_H
 #define _LIBCPP_EXPERIMENTAL___SIMD_SIMD_H
 
+#include <__type_traits/is_integral.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/forward.h>
@@ -25,15 +26,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>;
@@ -45,8 +60,10 @@ class simd {
   _LIBCPP_HIDE_FROM_ABI simd() noexcept = default;
 
   // explicit conversion from and to implementation-defined types
-  explicit _LIBCPP_HIDE_FROM_ABI operator _Storage() const;
-  explicit _LIBCPP_HIDE_FROM_ABI simd(const _Storage& __s) : __s_(__s) {}
+  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>
@@ -88,6 +105,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>
diff --git a/libcxx/include/experimental/__simd/simd_mask.h b/libcxx/include/experimental/__simd/simd_mask.h
index 890edfc4d4d729..a8bb49265ef0ff 100644
--- a/libcxx/include/experimental/__simd/simd_mask.h
+++ b/libcxx/include/experimental/__simd/simd_mask.h
@@ -42,8 +42,10 @@ class simd_mask {
   _LIBCPP_HIDE_FROM_ABI simd_mask() noexcept = default;
 
   // explicit conversion from and to implementation-defined types
-  explicit _LIBCPP_HIDE_FROM_ABI operator _Storage() const;
-  explicit _LIBCPP_HIDE_FROM_ABI simd_mask(const _Storage& __s) : __s_(__s) {}
+  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)) {}
diff --git a/libcxx/include/experimental/__simd/vec_ext.h b/libcxx/include/experimental/__simd/vec_ext.h
index 316866b84873dd..1a6cc2fb84fae6 100644
--- a/libcxx/include/experimental/__simd/vec_ext.h
+++ b/libcxx/include/experimental/__simd/vec_ext.h
@@ -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 _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>
diff --git a/libcxx/test/std/experimental/simd/simd.class/simd_unary.pass.cpp b/libcxx/test/std/experimental/simd/simd.class/simd_unary.pass.cpp
new file mode 100644
index 00000000000000..fc992f47f2b07c
--- /dev/null
+++ b/libcxx/test/std/experimental/simd/simd.class/simd_unary.pass.cpp
@@ -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
+
+// 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)
+
+// <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;
+}



More information about the libcxx-commits mailing list