This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
Section: 29.10.9.4 [simd.mask.unary] Status: New Submitter: Matthias Kretz Opened: 2025-09-15 Last modified: 2025-09-20
Priority: Not Prioritized
View other active issues in [simd.mask.unary].
View all other issues in [simd.mask.unary].
View all issues with New status.
Discussion:
29.10.9.4 [simd.mask.unary] spells out the return type with the ABI tag of
the basic_mask
specialization. That's problematic / overconstrained.
Consider Intel SandyBridge/IvyBridge-like targets:
vec<float>::size() -> 8 vec<int>::size() -> 4 mask<float>::size() -> 8
The ABI tag in this case encodes for vec<float>
that one object holds 8
elements and is passed via one register. vec<int>
uses a
different ABI tag that says 4 elements passed via one register.
vec<int, 8>
's ABI tag says 8 elements passed via two registers.
+mask<float>()
return? The working draft says it must
return a basic_vec<int, mask<float>::abi_type>
. And
mask<float>::abi_type
is constrained to be the same as
vec<float>::abi_type
. The working draft thus makes it
impossible to implement ABI tags that encode number of elements + number of
registers (+ bit-masks vs. vector-masks, but that's irrelevant for this
issue). Instead, an ABI tag would have to encode the native SIMD width of all
vectorizable types. And that's unnecessarily making compatible types
incompatible. Also we make it harder to add to the set of vectorizable types
in the future.The issue is even worse for an implementation that implements
vec<complex<T>>
using different ABI tags. Encoding
whether the value-type is complex into the ABI is useful because it impacts
how the mask is stored (mask<complex<float>, 8>
is
internally stored as a 16-element bit-mask (for interleaved complex
), while
mask<double, 8>
is stored as an 8-element bit-mask). The ABI
tag can also be used to implement interleaved vs. contiguous storage, which
is useful for different architectures. If we require
+mask<complex<float>>()
to be of a different type than
any vec<long long>
would ever be, that's just brittle and
unnecessary template bloat.
Proposed resolution:
This wording is relative to N5014.
Modify 29.10.2 [simd.expos] as indicated:
using simd-size-type = see below; // exposition only template<size_t Bytes> using integer-from = see below; // exposition only template<class T, class Abi> constexpr simd-size-type simd-size-v = see below; // exposition only template<class T> constexpr size_t mask-element-size = see below; // exposition only template <size_t Bytes, class Abi> using simd-vec-from-mask-t = see below; // exposition only […]
Modify 29.10.2.1 [simd.expos.defn] as indicated:
template<class T> constexpr size_t mask-element-size = see below; // exposition only-4-
mask-element-size<basic_mask<Bytes, Abi>>
has the valueBytes
.template <size_t Bytes, class Abi> using simd-vec-from-mask-t = see below;-?-
-?-simd-vec-from-mask-t<Bytes, Abi>
is an alias for an enabled specialization ofbasic_vec
if and only ifbasic_mask<Bytes, Abi>
is a data-parallel type andinteger-from<Bytes>
is valid and a vectorizable type.simd-vec-from-mask-t<Bytes, Abi>::size() == basic_mask<Bytes, Abi>::size()
istrue
. -?-typename simd-vec-from-mask-t<Bytes, Abi>::value_type
isinteger-from<Bytes>
Modify 29.10.9.1 [simd.mask.overview], class template basic_mask overview
synopsis, as indicated:
namespace std::simd { template<size_t Bytes, class Abi> class basic_mask { public: […] // 29.10.9.4 [simd.mask.unary], basic_mask unary operators constexpr basic_mask operator!() const noexcept; constexprbasic_vec<integer-from<Bytes>, Abi>simd-vec-from-mask-t<Bytes, Abi> operator+() const noexcept; constexprbasic_vec<integer-from<Bytes>, Abi>simd-vec-from-mask-t<Bytes, Abi> operator-() const noexcept; constexprbasic_vec<integer-from<Bytes>, Abi>simd-vec-from-mask-t<Bytes, Abi> operator~() const noexcept; […] }
Modify 29.10.9.4 [simd.mask.unary] as indicated:
constexpr basic_mask operator!() const noexcept; constexprbasic_vec<integer-from<Bytes>, Abi>simd-vec-from-mask-t<Bytes, Abi> operator+() const noexcept; constexprbasic_vec<integer-from<Bytes>, Abi>simd-vec-from-mask-t<Bytes, Abi> operator-() const noexcept; constexprbasic_vec<integer-from<Bytes>, Abi>simd-vec-from-mask-t<Bytes, Abi> operator~() const noexcept;-1- Let
-2- Returns: […]op
be the operator.