From d04d4da652cebc65020035822b79c6afc6da8c09 Mon Sep 17 00:00:00 2001
From: Bine Brank <b.brank@fz-juelich.de>
Date: Mon, 9 Mar 2020 14:34:33 +0100
Subject: [PATCH] bitwise ops

---
 .ycm_extra_conf.py                 |   4 +
 Makefile                           |   1 +
 backend/sve/sve_complex_double.hpp |  21 +++++
 backend/sve/sve_complex_float.hpp  |  21 +++++
 backend/sve/sve_double.hpp         |   9 +++
 backend/sve/sve_float.hpp          |   9 +++
 backend/sve/sve_int.hpp            |   9 +++
 bitwise/generic/gen_bit_ops.hpp    |  76 ++++++++++++++++++
 bitwise/sve/sve_bit_ops.hpp        | 122 ++++++++++++++++++++++++++++-
 simd.hpp                           |   2 -
 vector_test.x                      | Bin 0 -> 72080 bytes
 11 files changed, 271 insertions(+), 3 deletions(-)
 create mode 100644 .ycm_extra_conf.py
 create mode 100755 vector_test.x

diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py
new file mode 100644
index 0000000..1072f83
--- /dev/null
+++ b/.ycm_extra_conf.py
@@ -0,0 +1,4 @@
+def Settings( **kwargs ):
+  return {
+    'flags': [ '-x', 'c++', '-march=armv8-a_sve', 'O3', '-Wall', '-Wextra', '-Werror'],
+  }
diff --git a/Makefile b/Makefile
index de5e44d..38f15a2 100644
--- a/Makefile
+++ b/Makefile
@@ -12,6 +12,7 @@ HEADERS = simd.hpp \
 	  matrix_type.hpp matrix_overloading.hpp \
 	  arithmetics/generic/generic_vector_ops.hpp arithmetics/generic/generic.hpp arithmetics/generic/generic_matrix_ops.hpp \
 	  arithmetics/sve/sve_vector_ops.hpp backend/sve/sve_backend.hpp arithmetics/sve/sve.hpp arithmetics/sve/sve_matrix_ops.hpp \
+	  backend/sve/sve_complex_double.hpp backend/sve/sve_complex_float.hpp backend/sve/sve_double.hpp backend/sve/sve_float.hpp \
 	  loadstore/sve/sve_load.hpp
 
 all: ${TARGET}
diff --git a/backend/sve/sve_complex_double.hpp b/backend/sve/sve_complex_double.hpp
index 7292b48..04a58e3 100644
--- a/backend/sve/sve_complex_double.hpp
+++ b/backend/sve/sve_complex_double.hpp
@@ -94,6 +94,13 @@ struct svetype<complex<double>>{
         return {re, im};
     }
 
+    static inline vec and_z(svbool_t pg, vec op1, vec op2)
+    {
+        svuint64_t re = svand_z(pg, svreinterpret_u64(op1.v0), svreinterpret_u64(op2.v0));
+        svuint64_t im = svand_z(pg, svreinterpret_u64(op1.v1), svreinterpret_u64(op2.v1));
+        return {svreinterpret_f64(re), svreinterpret_f64(im)};
+    }
+
     static inline vec orr_z(svbool_t pg, vec op1, vec op2)
     {
         svuint64_t re = svorr_z(pg, svreinterpret_u64(op1.v0), svreinterpret_u64(op2.v0));
@@ -101,6 +108,20 @@ struct svetype<complex<double>>{
         return {svreinterpret_f64(re), svreinterpret_f64(im)};
     }
 
+    static inline vec eor_z(svbool_t pg, vec op1, vec op2)
+    {
+        svuint64_t re = sveor_z(pg, svreinterpret_u64(op1.v0), svreinterpret_u64(op2.v0));
+        svuint64_t im = sveor_z(pg, svreinterpret_u64(op1.v1), svreinterpret_u64(op2.v1));
+        return {svreinterpret_f64(re), svreinterpret_f64(im)};
+    }
+
+    static inline vec not_z(svbool_t pg, vec op1, vec op2)
+    {
+        svuint64_t re = svnot_z(pg, svreinterpret_u64(op1.v0), svreinterpret_u64(op2.v0));
+        svuint64_t im = svnot_z(pg, svreinterpret_u64(op1.v1), svreinterpret_u64(op2.v1));
+        return {svreinterpret_f64(re), svreinterpret_f64(im)};
+    }
+
     // vector vector operations
     static inline vec add_z(svbool_t pg, vec op1, vec op2)
     {
diff --git a/backend/sve/sve_complex_float.hpp b/backend/sve/sve_complex_float.hpp
index d14e338..aeef9b1 100644
--- a/backend/sve/sve_complex_float.hpp
+++ b/backend/sve/sve_complex_float.hpp
@@ -94,6 +94,13 @@ struct svetype<complex<float>>{
         return {re, im};
     }
 
+    static inline vec and_z(svbool_t pg, vec op1, vec op2)
+    {
+        svuint32_t re = svand_z(pg, svreinterpret_u32(op1.v0), svreinterpret_u32(op2.v0));
+        svuint32_t im = svand_z(pg, svreinterpret_u32(op1.v1), svreinterpret_u32(op2.v1));
+        return {svreinterpret_f32(re), svreinterpret_f32(im)};
+    }
+
     static inline vec orr_z(svbool_t pg, vec op1, vec op2)
     {
         svuint32_t re = svorr_z(pg, svreinterpret_u32(op1.v0), svreinterpret_u32(op2.v0));
@@ -101,6 +108,20 @@ struct svetype<complex<float>>{
         return {svreinterpret_f32(re), svreinterpret_f32(im)};
     }
 
+    static inline vec eor_z(svbool_t pg, vec op1, vec op2)
+    {
+        svuint32_t re = sveor_z(pg, svreinterpret_u32(op1.v0), svreinterpret_u32(op2.v0));
+        svuint32_t im = sveor_z(pg, svreinterpret_u32(op1.v1), svreinterpret_u32(op2.v1));
+        return {svreinterpret_f32(re), svreinterpret_f32(im)};
+    }
+
+    static inline vec not_z(svbool_t pg, vec op1, vec op2)
+    {
+        svuint32_t re = svnot_z(pg, svreinterpret_u32(op1.v0), svreinterpret_u32(op2.v0));
+        svuint32_t im = svnot_z(pg, svreinterpret_u32(op1.v1), svreinterpret_u32(op2.v1));
+        return {svreinterpret_f32(re), svreinterpret_f32(im)};
+    }
+
     // vector vector operations
     static inline vec add_z(svbool_t pg, vec op1, vec op2)
     {
diff --git a/backend/sve/sve_double.hpp b/backend/sve/sve_double.hpp
index 2040924..a4f9b97 100644
--- a/backend/sve/sve_double.hpp
+++ b/backend/sve/sve_double.hpp
@@ -78,9 +78,18 @@ struct svetype<double>{
     static inline vec conj_z(svbool_t pg, vec op1)
     { return op1; }
 
+    static inline vec and_z(svbool_t pg, vec op1, vec op2)
+    { return svreinterpret_f64(svand_z(pg, svreinterpret_u64(op1), svreinterpret_u64(op2))); }
+
     static inline vec orr_z(svbool_t pg, vec op1, vec op2)
     { return svreinterpret_f64(svorr_z(pg, svreinterpret_u64(op1), svreinterpret_u64(op2))); }
 
+    static inline vec eor_z(svbool_t pg, vec op1, vec op2)
+    { return svreinterpret_f64(sveor_z(pg, svreinterpret_u64(op1), svreinterpret_u64(op2))); }
+
+    static inline vec not_z(svbool_t pg, vec op1, vec op2)
+    { return svreinterpret_f64(svnot_z(pg, svreinterpret_u64(op1), svreinterpret_u64(op2))); }
+
     // vector vector operations
     static inline vec add_z(svbool_t pg, vec op1, vec op2)
     { return svadd_f64_z(pg, op1, op2); }
diff --git a/backend/sve/sve_float.hpp b/backend/sve/sve_float.hpp
index 0694342..525cd43 100644
--- a/backend/sve/sve_float.hpp
+++ b/backend/sve/sve_float.hpp
@@ -79,9 +79,18 @@ struct svetype<float>{
     static inline vec conj_z(svbool_t pg, vec op1)
     { return op1; }
 
+    static inline vec and_z(svbool_t pg, vec op1, vec op2)
+    { return svreinterpret_f32(svand_z(pg, svreinterpret_u32(op1), svreinterpret_u32(op2))); }
+
     static inline vec orr_z(svbool_t pg, vec op1, vec op2)
     { return svreinterpret_f32(svorr_z(pg, svreinterpret_u32(op1), svreinterpret_u32(op2))); }
 
+    static inline vec eor_z(svbool_t pg, vec op1, vec op2)
+    { return svreinterpret_f32(sveor_z(pg, svreinterpret_u32(op1), svreinterpret_u32(op2))); }
+
+    static inline vec not_z(svbool_t pg, vec op1, vec op2)
+    { return svreinterpret_f32(svnot_z(pg, svreinterpret_u32(op1), svreinterpret_u32(op2))); }
+
     // vector vector operations
     static inline vec add_z(svbool_t pg, vec op1, vec op2)
     { return svadd_f32_z(pg, op1, op2); }
diff --git a/backend/sve/sve_int.hpp b/backend/sve/sve_int.hpp
index cf3a5fd..7d3e4a3 100644
--- a/backend/sve/sve_int.hpp
+++ b/backend/sve/sve_int.hpp
@@ -76,9 +76,18 @@ struct svetype<int>{
     static inline vec mulscal_z(svbool_t pg, vec op1, scal op2)
     { return svmul_n_s32_z(pg, op1, op2); }
 
+    static inline vec and_z(svbool_t pg, vec op1, vec op2)
+    { return svand_z(pg, op1, op2); }
+
     static inline vec orr_z(svbool_t pg, vec op1, vec op2)
     { return svorr_z(pg, op1, op2); }
 
+    static inline vec eor_z(svbool_t pg, vec op1, vec op2)
+    { return sveor_z(pg, op1, op2); }
+
+    static inline vec not_z(svbool_t pg, vec op1, vec op2)
+    { return svnot_z(pg, op1, op2); }
+
     // vector vector operations
     static inline vec add_z(svbool_t pg, vec op1, vec op2)
     { return svadd_s32_z(pg, op1, op2); }
diff --git a/bitwise/generic/gen_bit_ops.hpp b/bitwise/generic/gen_bit_ops.hpp
index e6a0bdd..842e1e2 100644
--- a/bitwise/generic/gen_bit_ops.hpp
+++ b/bitwise/generic/gen_bit_ops.hpp
@@ -40,6 +40,25 @@
 #define VECTOR_FOR(i, w, inc) \
 for (unsigned int i = 0; i < w; i += inc)
 
+// bitwise and two vectors
+struct bitwise_and_vector_vector{
+  template <typename T>
+  inline vector_t<T> operator()(const vector_t<T> &a, const vector_t<T> &b)
+  {
+    assert(a.size == b.size);
+
+    vector_t<T> vres(a.size);
+
+    std::cout << "[Generic vector (bitwise and) used]" << endl;
+
+    VECTOR_FOR(i, vres.size, 1)
+    {
+      vres.elem[i] = a.elem[i] & b.elem[i];
+    }
+  	return vres;
+  }
+};
+
 // bitwise or two vectors
 struct bitwise_or_vector_vector{
   template <typename T>
@@ -59,6 +78,63 @@ struct bitwise_or_vector_vector{
   }
 };
 
+// bitwise xor two vectors
+struct bitwise_xor_vector_vector{
+  template <typename T>
+  inline vector_t<T> operator()(const vector_t<T> &a, const vector_t<T> &b)
+  {
+    assert(a.size == b.size);
+
+    vector_t<T> vres(a.size);
+
+    std::cout << "[Generic vector (bitwise xor) used]" << endl;
+
+    VECTOR_FOR(i, vres.size, 1)
+    {
+      vres.elem[i] = a.elem[i] ^ b.elem[i];
+    }
+  	return vres;
+  }
+};
+
+// bitwise not two vectors
+struct bitwise_not_vector_vector{
+  template <typename T>
+  inline vector_t<T> operator()(const vector_t<T> &a, const vector_t<T> &b)
+  {
+    assert(a.size == b.size);
+
+    vector_t<T> vres(a.size);
+
+    std::cout << "[Generic vector (bitwise not) used]" << endl;
+
+    VECTOR_FOR(i, vres.size, 1)
+    {
+      vres.elem[i] = a.elem[i] ~ b.elem[i];
+    }
+  	return vres;
+  }
+};
+
+// bitwise logical shift left two vectors
+struct bitwise_logical_shift_left_vector_vector{ template <typename T>
+  inline vector_t<T> operator()(const vector_t<T> &a, const int &b)
+  {
+    assert(a.size == b.size);
+
+    vector_t<T> vres(a.size);
+
+    std::cout << "[Generic vector (bitwise not) used]" << endl;
+
+    VECTOR_FOR(i, vres.size, 1)
+    {
+      vres.elem[i] = a.elem[i] << b;
+    }
+  	return vres;
+  }
+};
+
+
 
 
 #endif // GEN_BIT_OPS_HPP
diff --git a/bitwise/sve/sve_bit_ops.hpp b/bitwise/sve/sve_bit_ops.hpp
index c108629..6adad5b 100644
--- a/bitwise/sve/sve_bit_ops.hpp
+++ b/bitwise/sve/sve_bit_ops.hpp
@@ -37,6 +37,36 @@
 
 #include "sve_bitwise.hpp"
 
+// bitwise_and two vectors
+struct bitwise_and_vector_vector{
+  template <typename T>
+  inline vector_t<T> operator()(const vector_t<T> &a, const vector_t<T> &b)
+  {
+    assert(a.size == b.size);
+
+    int64_t i = 0;
+    vector_t<T> vres(a.size);
+    svbool_t ig = svetype<T>::svwhile(i, vres.size);
+
+    std::cout << "[SVE vector (bitwise and) used]" << endl;
+  	do
+  	{
+  		typename svetype<T>::vec x0 = svetype<T>::vload(ig, (const T *) &a.elem[i]);
+  		typename svetype<T>::vec x1 = svetype<T>::vload(ig, (const T *) &b.elem[i]);
+
+  		typename svetype<T>::vec res = svetype<T>::and_z(ig, x0, x1);
+
+		svetype<T>::vstore(ig, (T *) &vres.elem[i], res);
+
+  		i += svetype<T>::count();
+  		ig = svetype<T>::svwhile(i, vres.size);
+  	}
+  	while (svptest_any(svetype<T>::svtrue(), ig));
+
+  	return vres;
+  }
+};
+
 // bitwise_or two vectors
 struct bitwise_or_vector_vector{
   template <typename T>
@@ -48,7 +78,7 @@ struct bitwise_or_vector_vector{
     vector_t<T> vres(a.size);
     svbool_t ig = svetype<T>::svwhile(i, vres.size);
 
-    std::cout << "[SVE vector (SUM) used]" << endl;
+    std::cout << "[SVE vector (bitwise or) used]" << endl;
   	do
   	{
   		typename svetype<T>::vec x0 = svetype<T>::vload(ig, (const T *) &a.elem[i]);
@@ -67,6 +97,96 @@ struct bitwise_or_vector_vector{
   }
 };
 
+// bitwise_xor two vectors
+struct bitwise_xor_vector_vector{
+  template <typename T>
+  inline vector_t<T> operator()(const vector_t<T> &a, const vector_t<T> &b)
+  {
+    assert(a.size == b.size);
+
+    int64_t i = 0;
+    vector_t<T> vres(a.size);
+    svbool_t ig = svetype<T>::svwhile(i, vres.size);
+
+    std::cout << "[SVE vector (bitwise xor) used]" << endl;
+  	do
+  	{
+  		typename svetype<T>::vec x0 = svetype<T>::vload(ig, (const T *) &a.elem[i]);
+  		typename svetype<T>::vec x1 = svetype<T>::vload(ig, (const T *) &b.elem[i]);
+
+  		typename svetype<T>::vec res = svetype<T>::eor_z(ig, x0, x1);
+
+		svetype<T>::vstore(ig, (T *) &vres.elem[i], res);
+
+  		i += svetype<T>::count();
+  		ig = svetype<T>::svwhile(i, vres.size);
+  	}
+  	while (svptest_any(svetype<T>::svtrue(), ig));
+
+  	return vres;
+  }
+};
+
+// bitwise_not two vectors
+struct bitwise_not_vector_vector{
+  template <typename T>
+  inline vector_t<T> operator()(const vector_t<T> &a, const vector_t<T> &b)
+  {
+    assert(a.size == b.size);
+
+    int64_t i = 0;
+    vector_t<T> vres(a.size);
+    svbool_t ig = svetype<T>::svwhile(i, vres.size);
+
+    std::cout << "[SVE vector (bitwise not) used]" << endl;
+  	do
+  	{
+  		typename svetype<T>::vec x0 = svetype<T>::vload(ig, (const T *) &a.elem[i]);
+  		typename svetype<T>::vec x1 = svetype<T>::vload(ig, (const T *) &b.elem[i]);
+
+  		typename svetype<T>::vec res = svetype<T>::not_z(ig, x0, x1);
+
+		svetype<T>::vstore(ig, (T *) &vres.elem[i], res);
+
+  		i += svetype<T>::count();
+  		ig = svetype<T>::svwhile(i, vres.size);
+  	}
+  	while (svptest_any(svetype<T>::svtrue(), ig));
+
+  	return vres;
+  }
+};
+
+// bitwise_not two vectors
+struct bitwise_not_vector_vector{
+  template <typename T>
+  inline vector_t<T> operator()(const vector_t<T> &a, const vector_t<T> &b)
+  {
+    assert(a.size == b.size);
+
+    int64_t i = 0;
+    vector_t<T> vres(a.size);
+    svbool_t ig = svetype<T>::svwhile(i, vres.size);
+
+    std::cout << "[SVE vector (bitwise not) used]" << endl;
+  	do
+  	{
+  		typename svetype<T>::vec x0 = svetype<T>::vload(ig, (const T *) &a.elem[i]);
+  		typename svetype<T>::vec x1 = svetype<T>::vload(ig, (const T *) &b.elem[i]);
+
+  		typename svetype<T>::vec res = svetype<T>::not_z(ig, x0, x1);
+
+		svetype<T>::vstore(ig, (T *) &vres.elem[i], res);
+
+  		i += svetype<T>::count();
+  		ig = svetype<T>::svwhile(i, vres.size);
+  	}
+  	while (svptest_any(svetype<T>::svtrue(), ig));
+
+  	return vres;
+  }
+};
+
 
 #endif
 #endif // SVE_BIT_OPS_HPP
diff --git a/simd.hpp b/simd.hpp
index fb0dba3..3bbee82 100644
--- a/simd.hpp
+++ b/simd.hpp
@@ -72,6 +72,4 @@ using namespace std;
 #include "vector_overloading.hpp"
 #include "matrix_overloading.hpp"
 
-
-
 #endif // SIMD_HPP
diff --git a/vector_test.x b/vector_test.x
new file mode 100755
index 0000000000000000000000000000000000000000..6a93d8592d73df0bc002bce37f23492ae8660a42
GIT binary patch
literal 72080
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
YA>}VZ1j3pr2vPX~!iAF%Q8<?Y0B8&1eE<Le

literal 0
HcmV?d00001

-- 
GitLab