[v4,1/2] libstdc++: Refactor _ScalarAbi<N> into _Abi<N, N>
Checks
| Context |
Check |
Description |
| linaro-tcwg-bot/tcwg_gcc_build--master-arm |
success
|
Build passed
|
Commit Message
From: Matthias Kretz <m.kretz@gsi.de>
Before this change _Ap::_S_is_bitmask would pick up false from
_ScalarAbi<N>. After __scalar_abi_tag now identifies any _Abi<N, N, V>,
where V can also identify bit-masks, the short-cut of setting
_S_use_bitmask to _Ap::_S_is_bitmask is wrong. It would be correct to
have it say _Ap::_S_is_bitmask && !__scalar_abi_tag<_Ap>. I decided to
implement the latter only in the _S_nreg == 1 specialization and have
the higher ups inherit the value from their vec/mask member. The
_S_is_bitmask bit is not erased for __scalar_abi_tag since it makes a
difference for __abi_rebind.
Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
libstdc++-v3/ChangeLog:
* include/bits/simd_details.h (_ScalarAbi): Remove.
(__scalar_abi_tag): Identify _Abi<N, N> as scalar now.
(__native_abi): Replace _ScalarAbi<1> with _Abi_t<1, 1, ...>.
(__abi_rebind): Refactor rebinding from/to __scalar_abi_tag.
* include/bits/simd_mask.h (_S_use_bitmask): Only true if
!_S_is_scalar.
(_M_and_neighbors, _M_or_neighbors): Add case for _S_is_scalar
where the and/or must be executed one step earlier.
(_M_reduce_min_index, _M_reduce_max_index): Delete dead code.
* include/bits/simd_vec.h (_S_use_bitmask): Inherit the value
from the first data member.
* testsuite/std/simd/traits_impl.cc: Adjust for the removal of
_ScalarAbi.
---
libstdc++-v3/include/bits/simd_details.h | 86 +++++++------------
libstdc++-v3/include/bits/simd_mask.h | 38 +++++---
libstdc++-v3/include/bits/simd_vec.h | 2 +-
.../testsuite/std/simd/traits_impl.cc | 11 +--
4 files changed, 58 insertions(+), 79 deletions(-)
Comments
On Wed, 03 Jun 2026 at 05:42 +0000, Matthias Kretz via Sourceware Forge wrote:
>From: Matthias Kretz <m.kretz@gsi.de>
>
>Before this change _Ap::_S_is_bitmask would pick up false from
>_ScalarAbi<N>. After __scalar_abi_tag now identifies any _Abi<N, N, V>,
>where V can also identify bit-masks, the short-cut of setting
>_S_use_bitmask to _Ap::_S_is_bitmask is wrong. It would be correct to
>have it say _Ap::_S_is_bitmask && !__scalar_abi_tag<_Ap>. I decided to
>implement the latter only in the _S_nreg == 1 specialization and have
>the higher ups inherit the value from their vec/mask member. The
>_S_is_bitmask bit is not erased for __scalar_abi_tag since it makes a
>difference for __abi_rebind.
>
>Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
>
>libstdc++-v3/ChangeLog:
>
> * include/bits/simd_details.h (_ScalarAbi): Remove.
> (__scalar_abi_tag): Identify _Abi<N, N> as scalar now.
> (__native_abi): Replace _ScalarAbi<1> with _Abi_t<1, 1, ...>.
> (__abi_rebind): Refactor rebinding from/to __scalar_abi_tag.
> * include/bits/simd_mask.h (_S_use_bitmask): Only true if
> !_S_is_scalar.
> (_M_and_neighbors, _M_or_neighbors): Add case for _S_is_scalar
> where the and/or must be executed one step earlier.
> (_M_reduce_min_index, _M_reduce_max_index): Delete dead code.
> * include/bits/simd_vec.h (_S_use_bitmask): Inherit the value
> from the first data member.
> * testsuite/std/simd/traits_impl.cc: Adjust for the removal of
> _ScalarAbi.
>---
> libstdc++-v3/include/bits/simd_details.h | 86 +++++++------------
> libstdc++-v3/include/bits/simd_mask.h | 38 +++++---
> libstdc++-v3/include/bits/simd_vec.h | 2 +-
> .../testsuite/std/simd/traits_impl.cc | 11 +--
> 4 files changed, 58 insertions(+), 79 deletions(-)
>
>diff --git a/libstdc++-v3/include/bits/simd_details.h b/libstdc++-v3/include/bits/simd_details.h
>index a1acc5bd9464..e6185fac64a3 100644
>--- a/libstdc++-v3/include/bits/simd_details.h
>+++ b/libstdc++-v3/include/bits/simd_details.h
>@@ -241,46 +241,21 @@ namespace simd
> #endif
>
> /** @internal
>- * This ABI tag describes basic_vec objects that store one element per data member and basic_mask
>- * objects that store one bool data members.
>+ * @brief This ABI tag determines the data member(s) of basic_vec and basic_mask.
> *
>- * @tparam _Np The number of elements, which also matches the number of data members in
>- * basic_vec and basic_mask.
>- */
>- template <int _Np = 1>
>- struct _ScalarAbi
>- {
>- static constexpr int _S_size = _Np;
>-
>- static constexpr int _S_nreg = _Np;
>-
>- static constexpr _AbiVariant _S_variant = {};
>-
>- template <typename _Tp>
>- using _DataType = __canonical_vec_type_t<_Tp>;
>-
>- static constexpr bool _S_is_vecmask = false;
>-
>- // in principle a bool is a 1-bit bitmask, but this is asking for an AVX512 bitmask
>- static constexpr bool _S_is_bitmask = false;
>-
>- template <size_t>
>- using _MaskDataType = bool;
>-
>- template <int _N2, int _Nreg2 = _N2>
>- static consteval _ScalarAbi<_N2>
>- _S_resize()
>- {
>- static_assert(_N2 == _Nreg2);
>- return {};
>- }
>- };
>-
>- /** @internal
>- * This ABI tag describes basic_vec objects that store one or more objects declared with the
>- * [[gnu::vector_size(N)]] attribute.
>- * Applied to basic_mask objects, this ABI tag either describes corresponding vector-mask objects
>- * or bit-mask objects. Which one is used is determined via @p _Var.
>+ * @p _Nreg determines the number of recursive basic_vec/basic_mask data members where @p _Nreg is
>+ * equal to 1. With @p _Nreg equal to 1, the basic_vec/basic_mask holds one vector builtin (@p
>+ * _Np greater than 1) or a scalar (@ _Np equal to 1).
Does this work correctly with Doxygen, having a line break between the
@p and the _Np name? There's certainly a 'p' missing in the last @p
here. You could drop the @p and use `_Np` instead.
The patch is OK for trunk with the missing 'p' fixed, and preferably
just switching to markdown.
>+ * @f$\lceil\frac{\mathtt{Np}}{\mathtt{Nreg}}\rceil@f$ therefore determines the number of elements
>+ * in a register (except for a remainder where it can be smaller). If @p _Np equals @p _Nreg, (the
>+ * aforementioned quotient is 1), then basic_vec (recursively) holds non-vector data members and
>+ * basic_mask holds bools.
>+ *
>+ * The @p _Var parameter determines details about the data member in the one register case. Masks
>+ * can be represented as vector masks (the default comparison result of GNU vector builtins),
>+ * bit-masks as used by AVX-512, bit-masks as used by ARM SVE (not yet implemented), or a single
>+ * bool (for the @p _Np equals 1 case). For basic_mask it determines the actual data layout and
>+ * for basic_mask it determines the result of compares.
> *
> * @tparam _Np The number of elements.
> * @tparam _Nreg The number of registers needed to store @p _Np elements.
>@@ -391,9 +366,13 @@ namespace simd
> { __x.template _S_resize<_Tp::_S_size, _Tp::_S_nreg>() } -> same_as<_Tp>;
> };
>
>+ /** @internal
>+ * Satisfied if @p _Tp is a valid simd ABI tag and one element is stored per register (number of
>+ * registers equals size).
>+ */
> template <typename _Tp>
> concept __scalar_abi_tag
>- = same_as<_Tp, _ScalarAbi<_Tp::_S_size>> && __abi_tag<_Tp>;
>+ = same_as<_Tp, _Abi_t<_Tp::_S_size, _Tp::_S_size, _Tp::_S_variant>> && __abi_tag<_Tp>;
>
> // Determine if math functions must *raise* floating-point exceptions.
> // math_errhandling may expand to an extern symbol, in which case we must assume fp exceptions
>@@ -760,7 +739,7 @@ namespace simd
> else if constexpr (_Traits._M_have_avx512f())
> return _Abi_t<64 / __adj_sizeof, 1, _AbiVariant::_BitMask>();
> else if constexpr (is_same_v<_Tp, _Float16> && !_Traits._M_have_f16c())
>- return _ScalarAbi<1>();
>+ return _Abi_t<1, 1>();
> else if constexpr (_Traits._M_have_avx2())
> return _Abi_t<32 / __adj_sizeof, 1>();
> else if constexpr (_Traits._M_have_avx() && is_floating_point_v<_Tp>)
>@@ -772,7 +751,7 @@ namespace simd
> return _Abi_t<16 / __adj_sizeof, 1>();
> // no MMX: we can't emit EMMS where it would be necessary
> else
>- return _ScalarAbi<1>();
>+ return _Abi_t<1, 1>();
> }
>
> #else
>@@ -794,7 +773,7 @@ namespace simd
> if constexpr (!__vectorizable<_Tp>)
> return _InvalidAbi();
> else
>- return _ScalarAbi<1>();
>+ return _Abi_t<1, 1>();
> }
>
> #endif
>@@ -850,17 +829,19 @@ namespace simd
> if constexpr (_Np <= 0 || !__vectorizable<_Tp>)
> return _InvalidAbi();
>
>- else if constexpr (__scalar_abi_tag<_A0>)
>- return _A0::template _S_resize<_Np>();
>-
> else
> {
> using _Native = remove_const_t<decltype(std::simd::__native_abi<_Tp>())>;
> static_assert(0 != _Native::_S_size);
> constexpr int __nreg = __div_ceil(_Np, _Native::_S_size);
>
>- if constexpr (__scalar_abi_tag<_Native>)
>- return _Native::template _S_resize<_Np>();
>+ // __scalar_abi_tag is sticky (unless we reach size 1, where we can't know whether it was
>+ // an explicit __scalar_abi_tag before some resize_t)
>+ if constexpr (__scalar_abi_tag<_Native> || (__scalar_abi_tag<_A0> && _A0::_S_size >= 2))
>+ {
>+ return _A0::template _S_resize<_Np, _Np>();
>+ }
>+
> else
> return _Abi_t<_Native::_S_size, 1, __filter_abi_variant(_A0::_S_variant,
> _AbiVariant::_MaskVariants)
>@@ -885,9 +866,6 @@ namespace simd
> if constexpr (_Bytes == 0 || _Np <= 0)
> return _InvalidAbi();
>
>- else if constexpr (__scalar_abi_tag<_A0>)
>- return _A0::template _S_resize<_Np>();
>-
> #if _GLIBCXX_X86
> // AVX w/o AVX2:
> // e.g. resize_t<8, mask<float, Whatever>> needs to be _Abi<8, 1> not _Abi<8, 2>
>@@ -939,12 +917,6 @@ namespace simd
> if (__b0 != __b1)
> return true;
>
>- // everything is better than _ScalarAbi, except when converting to a single bool
>- if constexpr (__scalar_abi_tag<_To>)
>- return __n > 1;
>- else if constexpr (__scalar_abi_tag<_From>)
>- return true;
>-
> // converting to a bit-mask is better
> else if constexpr (_To::_S_is_vecmask != _From::_S_is_vecmask)
> return _To::_S_is_vecmask; // to vector-mask is explicit
>diff --git a/libstdc++-v3/include/bits/simd_mask.h b/libstdc++-v3/include/bits/simd_mask.h
>index 0a7cfa03cedd..81a0825ec6ce 100644
>--- a/libstdc++-v3/include/bits/simd_mask.h
>+++ b/libstdc++-v3/include/bits/simd_mask.h
>@@ -543,7 +543,7 @@ namespace simd
>
> static constexpr bool _S_is_scalar = _S_has_bool_member;
>
>- static constexpr bool _S_use_bitmask = _Ap::_S_is_bitmask;
>+ static constexpr bool _S_use_bitmask = _Ap::_S_is_bitmask && !_S_is_scalar;
>
> static constexpr int _S_full_size = [] {
> if constexpr (_S_is_scalar)
>@@ -1519,8 +1519,16 @@ namespace simd
> constexpr basic_mask&
> _M_and_neighbors()
> {
>- _M_data0._M_and_neighbors();
>- _M_data1._M_and_neighbors();
>+ if constexpr (_S_size == 2)
>+ {
>+ static_assert(_S_is_scalar);
>+ _M_data0 = _M_data1 = _M_data0 && _M_data1;
>+ }
>+ else
>+ {
>+ _M_data0._M_and_neighbors();
>+ _M_data1._M_and_neighbors();
>+ }
> return *this;
> }
>
>@@ -1528,8 +1536,16 @@ namespace simd
> constexpr basic_mask&
> _M_or_neighbors()
> {
>- _M_data0._M_or_neighbors();
>- _M_data1._M_or_neighbors();
>+ if constexpr (_S_size == 2)
>+ {
>+ static_assert(_S_is_scalar);
>+ _M_data0 = _M_data1 = _M_data0 || _M_data1;
>+ }
>+ else
>+ {
>+ _M_data0._M_or_neighbors();
>+ _M_data1._M_or_neighbors();
>+ }
> return *this;
> }
>
>@@ -1650,7 +1666,7 @@ namespace simd
> else if constexpr (_M_data1._S_has_bool_member)
> // in some cases the last element can be 'bool' instead of bit-/vector-mask;
> // e.g. mask<short, 17> is {mask<short, 16>, mask<short, 1>}, where the latter uses
>- // _ScalarAbi<1>, which is stored as 'bool'
>+ // _Abi<1, 1>, which is stored as 'bool'
> return __i < _N0 ? _M_data0[__i] : _M_data1[__i - _N0];
> else if constexpr (abi_type::_S_is_bitmask)
> {
>@@ -1929,10 +1945,7 @@ namespace simd
> {
> const auto __bits = _M_to_uint();
> __glibcxx_simd_precondition(__bits, "An empty mask does not have a min_index.");
>- if constexpr (_S_size == 1)
>- return 0;
>- else
>- return __countr_zero(_M_to_uint());
>+ return __countr_zero(_M_to_uint());
> }
> else if (_M_data0._M_none_of())
> return _M_data1._M_reduce_min_index() + _N0;
>@@ -1948,10 +1961,7 @@ namespace simd
> {
> const auto __bits = _M_to_uint();
> __glibcxx_simd_precondition(__bits, "An empty mask does not have a max_index.");
>- if constexpr (_S_size == 1)
>- return 0;
>- else
>- return __highest_bit(_M_to_uint());
>+ return __highest_bit(_M_to_uint());
> }
> else if (_M_data1._M_none_of())
> return _M_data0._M_reduce_max_index();
>diff --git a/libstdc++-v3/include/bits/simd_vec.h b/libstdc++-v3/include/bits/simd_vec.h
>index 5f3bd7fd2f61..5624ec781426 100644
>--- a/libstdc++-v3/include/bits/simd_vec.h
>+++ b/libstdc++-v3/include/bits/simd_vec.h
>@@ -1776,7 +1776,7 @@ namespace simd
>
> _DataType1 _M_data1;
>
>- static constexpr bool _S_use_bitmask = _Ap::_S_is_bitmask;
>+ static constexpr bool _S_use_bitmask = _DataType0::_S_use_bitmask;
>
> static constexpr bool _S_is_partial = _DataType1::_S_is_partial;
>
>diff --git a/libstdc++-v3/testsuite/std/simd/traits_impl.cc b/libstdc++-v3/testsuite/std/simd/traits_impl.cc
>index 2f705c7df2f7..94c6843b6228 100644
>--- a/libstdc++-v3/testsuite/std/simd/traits_impl.cc
>+++ b/libstdc++-v3/testsuite/std/simd/traits_impl.cc
>@@ -49,24 +49,21 @@ void test()
> static_assert(sizeof(_Bitmask<3>) == 1);
> static_assert(sizeof(_Bitmask<30>) == 4);
>
>- static_assert(__scalar_abi_tag<_ScalarAbi<1>>);
>- static_assert(__scalar_abi_tag<_ScalarAbi<2>>);
>- static_assert(!__scalar_abi_tag<_Abi_t<1, 1>>);
>-
>- static_assert(__abi_tag<_ScalarAbi<1>>);
>- static_assert(__abi_tag<_ScalarAbi<2>>);
>+ static_assert(__scalar_abi_tag<_Abi_t<1, 1>>);
>+ static_assert(__scalar_abi_tag<_Abi_t<2, 2>>);
>+ static_assert(!__scalar_abi_tag<_Abi_t<2, 1>>);
>
> using AN = decltype(__native_abi<float>());
> using A1 = decltype(__native_abi<float>()._S_resize<1>());
> static_assert(A1::_S_size == 1);
> static_assert(A1::_S_nreg == 1);
> static_assert(A1::_S_variant == AN::_S_variant);
>- static_assert(__scalar_abi_tag<A1> == __scalar_abi_tag<AN>);
> static_assert(std::is_same_v<decltype(__abi_rebind<float, AN::_S_size, A1>()), AN>);
> if constexpr (AN::_S_size >= 2) // the target has SIMD support for float
> {
> {
> using A2 = decltype(__abi_rebind<float, 2, AN>());
>+ static_assert(__scalar_abi_tag<A2> == __scalar_abi_tag<AN>);
> static_assert(A2::_S_size == 2);
> static_assert(A2::_S_nreg == 1);
> static_assert(A2::_S_variant == AN::_S_variant);
>--
>2.54.0
>
>
Jonathan Wakely [Wednesday, 1 July 2026, 12:42:41 CEST]:
> >- /** @internal
> >- * This ABI tag describes basic_vec objects that store one or more
> >objects declared with the - * [[gnu::vector_size(N)]] attribute.
> >- * Applied to basic_mask objects, this ABI tag either describes
> >corresponding vector-mask objects - * or bit-mask objects. Which one is
> >used is determined via @p _Var. + * @p _Nreg determines the number of
> >recursive basic_vec/basic_mask data members where @p _Nreg is + * equal
> >to 1. With @p _Nreg equal to 1, the basic_vec/basic_mask holds one vector
> >builtin (@p + * _Np greater than 1) or a scalar (@ _Np equal to 1).
>
> Does this work correctly with Doxygen, having a line break between the
> @p and the _Np name? There's certainly a 'p' missing in the last @p
> here.
Good question. Right now I'm flying blind on Doxygen. All bits/simd_* and
bits/vec_ops.h are currently excluded.
1. because I don't have a @file doxygen comment in any of them
2. INTERNAL_DOCS is set to NO
OK, so I just checked how _Abi's HTML turns out... The line break between @p
and _Np is no problem.
> You could drop the @p and use `_Np` instead.
> The patch is OK for trunk with the missing 'p' fixed, and preferably
> just switching to markdown.
OK, I'll change the new comments blocks in this patch to use markdown then.
Jonathan Wakely [Wednesday, 1 July 2026, 12:42:41 CEST]:
> The patch is OK for trunk with the missing 'p' fixed, and preferably
> just switching to markdown.
Backport to 16?
With std::simd in GCC 16 being fairly experimental, I would like to backport
this patch.
1. This restores ABI compatibility between GCC 16.2 and up (so that we only
break between 16.1 and everything else).
2. Without it, backporting upcoming patches becomes harder.
On Wed, 1 Jul 2026 at 13:56, Matthias Kretz <MatthiasKretz@gmx.net> wrote:
>
> Jonathan Wakely [Wednesday, 1 July 2026, 12:42:41 CEST]:
> > The patch is OK for trunk with the missing 'p' fixed, and preferably
> > just switching to markdown.
>
> Backport to 16?
Yes, go ahead.
>
> With std::simd in GCC 16 being fairly experimental, I would like to backport
> this patch.
> 1. This restores ABI compatibility between GCC 16.2 and up (so that we only
> break between 16.1 and everything else).
> 2. Without it, backporting upcoming patches becomes harder.
>
> --
> ──────────────────────────────────────────────────────────────────────────
> Dr. Matthias Kretz https://mattkretz.github.io
> GSI Helmholtz Center for Heavy Ion Research https://gsi.de
> std::simd
> ──────────────────────────────────────────────────────────────────────────
>
@@ -241,46 +241,21 @@ namespace simd
#endif
/** @internal
- * This ABI tag describes basic_vec objects that store one element per data member and basic_mask
- * objects that store one bool data members.
+ * @brief This ABI tag determines the data member(s) of basic_vec and basic_mask.
*
- * @tparam _Np The number of elements, which also matches the number of data members in
- * basic_vec and basic_mask.
- */
- template <int _Np = 1>
- struct _ScalarAbi
- {
- static constexpr int _S_size = _Np;
-
- static constexpr int _S_nreg = _Np;
-
- static constexpr _AbiVariant _S_variant = {};
-
- template <typename _Tp>
- using _DataType = __canonical_vec_type_t<_Tp>;
-
- static constexpr bool _S_is_vecmask = false;
-
- // in principle a bool is a 1-bit bitmask, but this is asking for an AVX512 bitmask
- static constexpr bool _S_is_bitmask = false;
-
- template <size_t>
- using _MaskDataType = bool;
-
- template <int _N2, int _Nreg2 = _N2>
- static consteval _ScalarAbi<_N2>
- _S_resize()
- {
- static_assert(_N2 == _Nreg2);
- return {};
- }
- };
-
- /** @internal
- * This ABI tag describes basic_vec objects that store one or more objects declared with the
- * [[gnu::vector_size(N)]] attribute.
- * Applied to basic_mask objects, this ABI tag either describes corresponding vector-mask objects
- * or bit-mask objects. Which one is used is determined via @p _Var.
+ * @p _Nreg determines the number of recursive basic_vec/basic_mask data members where @p _Nreg is
+ * equal to 1. With @p _Nreg equal to 1, the basic_vec/basic_mask holds one vector builtin (@p
+ * _Np greater than 1) or a scalar (@ _Np equal to 1).
+ * @f$\lceil\frac{\mathtt{Np}}{\mathtt{Nreg}}\rceil@f$ therefore determines the number of elements
+ * in a register (except for a remainder where it can be smaller). If @p _Np equals @p _Nreg, (the
+ * aforementioned quotient is 1), then basic_vec (recursively) holds non-vector data members and
+ * basic_mask holds bools.
+ *
+ * The @p _Var parameter determines details about the data member in the one register case. Masks
+ * can be represented as vector masks (the default comparison result of GNU vector builtins),
+ * bit-masks as used by AVX-512, bit-masks as used by ARM SVE (not yet implemented), or a single
+ * bool (for the @p _Np equals 1 case). For basic_mask it determines the actual data layout and
+ * for basic_mask it determines the result of compares.
*
* @tparam _Np The number of elements.
* @tparam _Nreg The number of registers needed to store @p _Np elements.
@@ -391,9 +366,13 @@ namespace simd
{ __x.template _S_resize<_Tp::_S_size, _Tp::_S_nreg>() } -> same_as<_Tp>;
};
+ /** @internal
+ * Satisfied if @p _Tp is a valid simd ABI tag and one element is stored per register (number of
+ * registers equals size).
+ */
template <typename _Tp>
concept __scalar_abi_tag
- = same_as<_Tp, _ScalarAbi<_Tp::_S_size>> && __abi_tag<_Tp>;
+ = same_as<_Tp, _Abi_t<_Tp::_S_size, _Tp::_S_size, _Tp::_S_variant>> && __abi_tag<_Tp>;
// Determine if math functions must *raise* floating-point exceptions.
// math_errhandling may expand to an extern symbol, in which case we must assume fp exceptions
@@ -760,7 +739,7 @@ namespace simd
else if constexpr (_Traits._M_have_avx512f())
return _Abi_t<64 / __adj_sizeof, 1, _AbiVariant::_BitMask>();
else if constexpr (is_same_v<_Tp, _Float16> && !_Traits._M_have_f16c())
- return _ScalarAbi<1>();
+ return _Abi_t<1, 1>();
else if constexpr (_Traits._M_have_avx2())
return _Abi_t<32 / __adj_sizeof, 1>();
else if constexpr (_Traits._M_have_avx() && is_floating_point_v<_Tp>)
@@ -772,7 +751,7 @@ namespace simd
return _Abi_t<16 / __adj_sizeof, 1>();
// no MMX: we can't emit EMMS where it would be necessary
else
- return _ScalarAbi<1>();
+ return _Abi_t<1, 1>();
}
#else
@@ -794,7 +773,7 @@ namespace simd
if constexpr (!__vectorizable<_Tp>)
return _InvalidAbi();
else
- return _ScalarAbi<1>();
+ return _Abi_t<1, 1>();
}
#endif
@@ -850,17 +829,19 @@ namespace simd
if constexpr (_Np <= 0 || !__vectorizable<_Tp>)
return _InvalidAbi();
- else if constexpr (__scalar_abi_tag<_A0>)
- return _A0::template _S_resize<_Np>();
-
else
{
using _Native = remove_const_t<decltype(std::simd::__native_abi<_Tp>())>;
static_assert(0 != _Native::_S_size);
constexpr int __nreg = __div_ceil(_Np, _Native::_S_size);
- if constexpr (__scalar_abi_tag<_Native>)
- return _Native::template _S_resize<_Np>();
+ // __scalar_abi_tag is sticky (unless we reach size 1, where we can't know whether it was
+ // an explicit __scalar_abi_tag before some resize_t)
+ if constexpr (__scalar_abi_tag<_Native> || (__scalar_abi_tag<_A0> && _A0::_S_size >= 2))
+ {
+ return _A0::template _S_resize<_Np, _Np>();
+ }
+
else
return _Abi_t<_Native::_S_size, 1, __filter_abi_variant(_A0::_S_variant,
_AbiVariant::_MaskVariants)
@@ -885,9 +866,6 @@ namespace simd
if constexpr (_Bytes == 0 || _Np <= 0)
return _InvalidAbi();
- else if constexpr (__scalar_abi_tag<_A0>)
- return _A0::template _S_resize<_Np>();
-
#if _GLIBCXX_X86
// AVX w/o AVX2:
// e.g. resize_t<8, mask<float, Whatever>> needs to be _Abi<8, 1> not _Abi<8, 2>
@@ -939,12 +917,6 @@ namespace simd
if (__b0 != __b1)
return true;
- // everything is better than _ScalarAbi, except when converting to a single bool
- if constexpr (__scalar_abi_tag<_To>)
- return __n > 1;
- else if constexpr (__scalar_abi_tag<_From>)
- return true;
-
// converting to a bit-mask is better
else if constexpr (_To::_S_is_vecmask != _From::_S_is_vecmask)
return _To::_S_is_vecmask; // to vector-mask is explicit
@@ -543,7 +543,7 @@ namespace simd
static constexpr bool _S_is_scalar = _S_has_bool_member;
- static constexpr bool _S_use_bitmask = _Ap::_S_is_bitmask;
+ static constexpr bool _S_use_bitmask = _Ap::_S_is_bitmask && !_S_is_scalar;
static constexpr int _S_full_size = [] {
if constexpr (_S_is_scalar)
@@ -1519,8 +1519,16 @@ namespace simd
constexpr basic_mask&
_M_and_neighbors()
{
- _M_data0._M_and_neighbors();
- _M_data1._M_and_neighbors();
+ if constexpr (_S_size == 2)
+ {
+ static_assert(_S_is_scalar);
+ _M_data0 = _M_data1 = _M_data0 && _M_data1;
+ }
+ else
+ {
+ _M_data0._M_and_neighbors();
+ _M_data1._M_and_neighbors();
+ }
return *this;
}
@@ -1528,8 +1536,16 @@ namespace simd
constexpr basic_mask&
_M_or_neighbors()
{
- _M_data0._M_or_neighbors();
- _M_data1._M_or_neighbors();
+ if constexpr (_S_size == 2)
+ {
+ static_assert(_S_is_scalar);
+ _M_data0 = _M_data1 = _M_data0 || _M_data1;
+ }
+ else
+ {
+ _M_data0._M_or_neighbors();
+ _M_data1._M_or_neighbors();
+ }
return *this;
}
@@ -1650,7 +1666,7 @@ namespace simd
else if constexpr (_M_data1._S_has_bool_member)
// in some cases the last element can be 'bool' instead of bit-/vector-mask;
// e.g. mask<short, 17> is {mask<short, 16>, mask<short, 1>}, where the latter uses
- // _ScalarAbi<1>, which is stored as 'bool'
+ // _Abi<1, 1>, which is stored as 'bool'
return __i < _N0 ? _M_data0[__i] : _M_data1[__i - _N0];
else if constexpr (abi_type::_S_is_bitmask)
{
@@ -1929,10 +1945,7 @@ namespace simd
{
const auto __bits = _M_to_uint();
__glibcxx_simd_precondition(__bits, "An empty mask does not have a min_index.");
- if constexpr (_S_size == 1)
- return 0;
- else
- return __countr_zero(_M_to_uint());
+ return __countr_zero(_M_to_uint());
}
else if (_M_data0._M_none_of())
return _M_data1._M_reduce_min_index() + _N0;
@@ -1948,10 +1961,7 @@ namespace simd
{
const auto __bits = _M_to_uint();
__glibcxx_simd_precondition(__bits, "An empty mask does not have a max_index.");
- if constexpr (_S_size == 1)
- return 0;
- else
- return __highest_bit(_M_to_uint());
+ return __highest_bit(_M_to_uint());
}
else if (_M_data1._M_none_of())
return _M_data0._M_reduce_max_index();
@@ -1776,7 +1776,7 @@ namespace simd
_DataType1 _M_data1;
- static constexpr bool _S_use_bitmask = _Ap::_S_is_bitmask;
+ static constexpr bool _S_use_bitmask = _DataType0::_S_use_bitmask;
static constexpr bool _S_is_partial = _DataType1::_S_is_partial;
@@ -49,24 +49,21 @@ void test()
static_assert(sizeof(_Bitmask<3>) == 1);
static_assert(sizeof(_Bitmask<30>) == 4);
- static_assert(__scalar_abi_tag<_ScalarAbi<1>>);
- static_assert(__scalar_abi_tag<_ScalarAbi<2>>);
- static_assert(!__scalar_abi_tag<_Abi_t<1, 1>>);
-
- static_assert(__abi_tag<_ScalarAbi<1>>);
- static_assert(__abi_tag<_ScalarAbi<2>>);
+ static_assert(__scalar_abi_tag<_Abi_t<1, 1>>);
+ static_assert(__scalar_abi_tag<_Abi_t<2, 2>>);
+ static_assert(!__scalar_abi_tag<_Abi_t<2, 1>>);
using AN = decltype(__native_abi<float>());
using A1 = decltype(__native_abi<float>()._S_resize<1>());
static_assert(A1::_S_size == 1);
static_assert(A1::_S_nreg == 1);
static_assert(A1::_S_variant == AN::_S_variant);
- static_assert(__scalar_abi_tag<A1> == __scalar_abi_tag<AN>);
static_assert(std::is_same_v<decltype(__abi_rebind<float, AN::_S_size, A1>()), AN>);
if constexpr (AN::_S_size >= 2) // the target has SIMD support for float
{
{
using A2 = decltype(__abi_rebind<float, 2, AN>());
+ static_assert(__scalar_abi_tag<A2> == __scalar_abi_tag<AN>);
static_assert(A2::_S_size == 2);
static_assert(A2::_S_nreg == 1);
static_assert(A2::_S_variant == AN::_S_variant);