Skip to content

Commit f628276

Browse files
committed
feat: Add batch::raw
1 parent 1bbcdae commit f628276

3 files changed

Lines changed: 77 additions & 0 deletions

File tree

docs/source/vectorized_code.rst

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,40 @@ architecture and pass the appropriate flag to the compiler. For instance:
8181

8282
This can be useful to implement runtime dispatching, based on the instruction set detected at runtime. `xsimd` provides a generic machinery :cpp:func:`xsimd::dispatch()` to implement
8383
this pattern. Based on the above example, instead of calling ``mean{}(arch, a, b, res, tag)``, one can use ``xsimd::dispatch(mean{})(a, b, res, tag)``. More about this can be found in the :ref:`Arch Dispatching` section.
84+
85+
Breaking out of xsimd
86+
---------------------
87+
88+
Sometimes ``xsimd`` may not give you the whole availability of the instruction set you
89+
are targeting. This is not specific to this library but to all libraries that
90+
abstract something.
91+
92+
``xsimd`` give you the possibility to break out of its :cpp:class:`~xsimd::batch` class
93+
and getting the underlying intrinsic register type.
94+
This is useful when an instruction set expose some intrinsicts for applications (*e.g.* video
95+
processing, cryptography...) that are too specific to include in ``xsimd``, or when some
96+
instructions are currently missing.
97+
98+
There are many ways a user could add a special cases in their arch-independent SIMD code.
99+
One that is simple and compiles on all platforms is using C++17 ``if constexpr``.
100+
101+
.. code:: cpp
102+
103+
template<typename Arch>
104+
auto sign_i8(
105+
xsimd::batch<int8_t, Arch> const& x,
106+
xsimd::batch<int8_t, Arch> const& y
107+
) -> xsimd::batch<uint8_t, Arch> {
108+
// Dedicated instruction dispatch at compile time
109+
if constexpr(std::is_same_v<Arch, xsimd::avx2>){
110+
// Automatic conversion back and forth between xsimd::batch and native types
111+
return _mm256_sign_epi8(x, y);
112+
// When compiler complains we can be more explicit
113+
return xsimd::batch<int8_t, Arch>(_mm256_sign_epi8(x.to_native(), y.to_native()));
114+
}
115+
116+
// General xsimd implementation
117+
auto const zero = xsimd::batch<uint8_t, Arch>(0);
118+
auto const c = xsimd::select(b < 0, -a, a);
119+
return xsimd::select(b == zero, zero, c);
120+
}

include/xsimd/types/xsimd_batch.hpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ namespace xsimd
151151
return this->data;
152152
}
153153

154+
XSIMD_INLINE register_type to_native() const noexcept;
155+
154156
template <class U>
155157
XSIMD_NO_DISCARD static XSIMD_INLINE batch broadcast(U val) noexcept;
156158

@@ -343,6 +345,8 @@ namespace xsimd
343345
template <class Tp>
344346
XSIMD_INLINE batch_bool(Tp const*) = delete;
345347

348+
XSIMD_INLINE register_type to_native() const noexcept;
349+
346350
// memory operators
347351
XSIMD_INLINE void store_aligned(bool* mem) const noexcept;
348352
XSIMD_INLINE void store_unaligned(bool* mem) const noexcept;
@@ -836,6 +840,15 @@ namespace xsimd
836840
return kernel::first(*this, A {});
837841
}
838842

843+
/**
844+
* Cast to the underlying native intrinsic register type.
845+
*/
846+
template <class T, class A>
847+
XSIMD_INLINE auto batch<T, A>::to_native() const noexcept -> register_type
848+
{
849+
return static_cast<register_type>(*this);
850+
}
851+
839852
/******************************
840853
* batch comparison operators *
841854
******************************/
@@ -1167,6 +1180,15 @@ namespace xsimd
11671180
return kernel::first(*this, A {});
11681181
}
11691182

1183+
/**
1184+
* Cast to the underlying native intrinsic register type.
1185+
*/
1186+
template <class T, class A>
1187+
XSIMD_INLINE auto batch_bool<T, A>::to_native() const noexcept -> register_type
1188+
{
1189+
return static_cast<register_type>(*this);
1190+
}
1191+
11701192
/***********************************
11711193
* batch_bool comparison operators *
11721194
***********************************/

test/test_batch.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,19 @@ struct batch_test
100100
CHECK_EQ(res_dump.str(), b_dump.str());
101101
}
102102

103+
void test_register_conversion() const
104+
{
105+
batch_type b1 = batch_type::load_unaligned(lhs.data());
106+
107+
auto b_converted = static_cast<typename batch_type::register_type>(b1);
108+
auto b_native = b1.to_native();
109+
// Check via decltype as register_type raises warning for ignored attributes
110+
CHECK(std::is_same<decltype(b_native), decltype(b_converted)>::value);
111+
112+
batch_type b2 = b_native; // Converting ctor
113+
CHECK_BATCH_EQ(b1, b2);
114+
}
115+
103116
void test_load_store() const
104117
{
105118
array_type res;
@@ -1161,6 +1174,11 @@ TEST_CASE_TEMPLATE("[batch]", B, BATCH_TYPES)
11611174
Test.test_stream_dump();
11621175
}
11631176

1177+
SUBCASE("register_conversion")
1178+
{
1179+
Test.test_register_conversion();
1180+
}
1181+
11641182
SUBCASE("load_store")
11651183
{
11661184
Test.test_load_store();

0 commit comments

Comments
 (0)