From 5921c03f12998bf72c212026f89397525427a14c Mon Sep 17 00:00:00 2001 From: David Sisco Date: Tue, 21 Jul 2026 02:02:28 -0700 Subject: [PATCH 01/16] docs: proposal document for new Register abstraction --- docs/RegisterProposal.md | 956 +++++++++++++++++++-------------------- 1 file changed, 478 insertions(+), 478 deletions(-) diff --git a/docs/RegisterProposal.md b/docs/RegisterProposal.md index 9e5217b..ec766e3 100644 --- a/docs/RegisterProposal.md +++ b/docs/RegisterProposal.md @@ -27,12 +27,12 @@ algorithms and partial-register handling remain outside `Register`. ## Decision status -| Status | Decisions | -| --- | --- | -| Controlling requirement | Template order is ``; every hardware lane is active; default construction uses the native zero-register operation; comparison behavior matches the selected hardware intrinsic; the abstraction has zero runtime overhead in supported configurations. | +| Status | Decisions | +| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Controlling requirement | Template order is ``; every hardware lane is active; default construction uses the native zero-register operation; comparison behavior matches the selected hardware intrinsic; the abstraction has zero runtime overhead in supported configurations. | | Implemented public design | Explicit register width with `NativeRegister` for target-selected width; C++23 explicit-object members for register-consuming operations; explicit scalar broadcast; `RegisterMask` predicates; fixed-extent element and byte transfers; operation names and results defined by the migration ledger. | -| Intentionally excluded | Partial and unsafe loads, automatic lane filling, collection transforms, native-order construction, ambiguous `expand`/`compress`, implementation-specific runtime rearrangements, and multi-register widening results. | -| Qualification contract | The supported compiler, ISA, type, width, generated-code, and non-inlined calling-boundary cells are defined in `docs/RegisterQualification.md`; individual outcomes are emitted as build receipts, reports, provenance files, and logs. | +| Intentionally excluded | Partial and unsafe loads, automatic lane filling, collection transforms, native-order construction, ambiguous `expand`/`compress`, implementation-specific runtime rearrangements, and multi-register widening results. | +| Qualification contract | The supported compiler, ISA, type, width, generated-code, and non-inlined calling-boundary cells are defined in `docs/RegisterQualification.md`; individual outcomes are emitted as build receipts, reports, provenance files, and logs. | ## Motivation @@ -100,12 +100,12 @@ output.store(destination); ## Responsibility boundaries -| Surface | Responsibility | Partial data | -| --- | --- | --- | -| `Register` | One complete hardware register | Rejected | -| `SimdVector` | One fixed logical value | Inactive lanes are managed by the type | -| `SimdAlgo` and future `Tensor` operations | Collections and batches | Tail policy belongs to the algorithm | -| `Api` | Compatibility facade and implementation routing | Existing behavior remains supported | +| Surface | Responsibility | Partial data | +| ----------------------------------------- | ----------------------------------------------- | -------------------------------------- | +| `Register` | One complete hardware register | Rejected | +| `SimdVector` | One fixed logical value | Inactive lanes are managed by the type | +| `SimdAlgo` and future `Tensor` operations | Collections and batches | Tail policy belongs to the algorithm | +| `Api` | Compatibility facade and implementation routing | Existing behavior remains supported | `Register` deliberately has no equivalent to `Api::load_partial`, `Api::set_partial`, or `Api::setr_partial`. A caller with fewer than @@ -150,10 +150,10 @@ initial ABI probes have been validated: ```cpp #if defined(__cpp_explicit_this_parameter) && \ - __cpp_explicit_this_parameter >= 202110L + __cpp_explicit_this_parameter >= 202110L #define SIMDLIB_REGISTER_INTERFACE_AVAILABLE 1 #elif defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1944 && \ - defined(_MSVC_LANG) && _MSVC_LANG > 202002L + defined(_MSVC_LANG) && _MSVC_LANG > 202002L #define SIMDLIB_REGISTER_INTERFACE_AVAILABLE 1 #else #define SIMDLIB_REGISTER_INTERFACE_AVAILABLE 0 @@ -205,12 +205,12 @@ ODR hazard. The preprocessor macro is the only public availability query. The core target retains its existing C++20 compiler matrix. Register support is a narrower, separately validated matrix: -| Compiler family | Initial Register floor | Platform | Language mode | Availability path | -| --- | --- | --- | --- | --- | -| Microsoft C++ | MSVC 19.44 | Windows x64 | `/std:c++latest` | `_MSC_VER` and `_MSVC_LANG` fallback | -| clang-cl | 20 | Windows x64 | C++23 | Standard feature-test macro | -| Clang | 22 | Linux x64 | C++23 | Standard feature-test macro | -| GCC | 14 | Linux x64 | C++23 | Standard feature-test macro | +| Compiler family | Initial Register floor | Platform | Language mode | Availability path | +| --------------- | ---------------------- | ----------- | ---------------- | ------------------------------------ | +| Microsoft C++ | MSVC 19.44 | Windows x64 | `/std:c++latest` | `_MSC_VER` and `_MSVC_LANG` fallback | +| clang-cl | 20 | Windows x64 | C++23 | Standard feature-test macro | +| Clang | 22 | Linux x64 | C++23 | Standard feature-test macro | +| GCC | 14 | Linux x64 | C++23 | Standard feature-test macro | Linux x64 GCC 13.2 remains in the core C++20 matrix and must compile the umbrella header with `SIMDLIB_REGISTER_INTERFACE_AVAILABLE == 0`. A compiler is added to the @@ -231,16 +231,16 @@ add_library(SimdLib::Register ALIAS SimdLibRegister) target_link_libraries(SimdLibRegister INTERFACE SimdLib::SimdLib) target_compile_features(SimdLibRegister INTERFACE cxx_std_23) target_compile_options( - SimdLibRegister - INTERFACE $<$:/std:c++latest>) + SimdLibRegister + INTERFACE $<$:/std:c++latest>) target_compile_definitions( - SimdLibRegister - INTERFACE SIMDLIB_REQUIRE_REGISTER_INTERFACE=1) + SimdLibRegister + INTERFACE SIMDLIB_REQUIRE_REGISTER_INTERFACE=1) ``` ```cpp #if defined(SIMDLIB_REQUIRE_REGISTER_INTERFACE) && \ - !SIMDLIB_REGISTER_INTERFACE_AVAILABLE + !SIMDLIB_REGISTER_INTERFACE_AVAILABLE #error "SimdLib::Register requires supported C++23 explicit object parameters." #endif ``` @@ -276,14 +276,14 @@ namespace SimdLib */ template inline constexpr bool is_register_available_v = - SimdLib::is_api_available_v; + SimdLib::is_api_available_v; /** * @brief Constrains a type and width to a supported complete SIMD register. */ template concept RegisterAvailable = - is_register_available_v; + is_register_available_v; /** * @brief Owns one complete SIMD register whose lanes are all active. @@ -291,7 +291,7 @@ concept RegisterAvailable = * @tparam register_width Width of the native register in bits. */ template - requires RegisterAvailable + requires RegisterAvailable class Register final; /** @@ -299,10 +299,10 @@ class Register final; * @tparam element_t Scalar interpretation of each register lane. */ template - requires RegisterAvailable + requires RegisterAvailable using NativeRegister = Register< - element_t, - is_register_available_v ? 256 : 128>; + element_t, + is_register_available_v ? 256 : 128>; } // namespace SimdLib ``` @@ -356,7 +356,7 @@ The operation ledger defines the remaining operation names. * @tparam bits Width of the associated register in bits. */ template - requires RegisterAvailable + requires RegisterAvailable class RegisterMask; /** @@ -365,192 +365,192 @@ class RegisterMask; * @tparam bits Width of the native register in bits. */ template - requires RegisterAvailable + requires RegisterAvailable class Register final { public: - using element_type = element_t; - using api_type = Api; - using native_type = typename api_type::vector_t; - using mask_type = RegisterMask; - - constexpr static inline std::size_t register_width = bits; - constexpr static inline std::size_t byte_count = api_type::byte_count; - constexpr static inline std::size_t lane_count = api_type::element_count; - - /** @brief Owns the complete native register value represented by this aggregate. */ - native_type native = api_type::setzero(); - - /** - * @brief Returns a register with every active lane set to zero. - * @return Fully initialized zero register. - */ - [[nodiscard]] static constexpr Register SIMD_FLAGS(Out, ForceInline) zero() noexcept; - - /** - * @brief Broadcasts one scalar value to every active lane. - * @param value Scalar value to broadcast. - * @return Register containing `value` in every lane. - */ - [[nodiscard]] static constexpr Register SIMD_FLAGS(Out, ForceInline) broadcast( - element_type value) noexcept; - - /** - * @brief Constructs a register from exactly one complete logical lane list. - * @param lanes Values in low-to-high logical lane order. - * @return Register containing all supplied lane values. - */ - template ... lane_types> - requires(sizeof...(lane_types) == lane_count) - [[nodiscard]] static constexpr Register SIMD_FLAGS(Out, ForceInline) from_lanes( - lane_types &&...lanes) noexcept; - - /** - * @brief Constructs a register from one complete fixed-size lane array. - * @param source Source containing every active lane in logical order. - * @return Register containing all source lane values. - */ - [[nodiscard]] static constexpr Register SIMD_FLAGS(Out, ForceInline) from_array( - const std::array &source) noexcept; - - /** - * @brief Loads a complete register from potentially unaligned storage. - * @param source Source containing exactly one register of elements. - * @return Register loaded from `source`. - */ - [[nodiscard]] static Register SIMD_FLAGS(Out, ForceInline) load( - std::span source) noexcept; - - /** - * @brief Loads a complete register from register-aligned storage. - * @param source Aligned source containing exactly one register of elements. - * @return Register loaded from `source`. - */ - [[nodiscard]] static Register SIMD_FLAGS(Out, ForceInline) load_aligned( - std::span source) noexcept; - - /** - * @brief Loads one complete register bit pattern from raw bytes. - * @param source Source containing exactly one register of bytes. - * @return Register containing the source bit pattern. - */ - [[nodiscard]] static Register SIMD_FLAGS(Out, ForceInline) load_bytes( - std::span source) noexcept; - - /** - * @brief Stores every active lane to potentially unaligned storage. - * @param value Register to store. - * @param destination Destination for exactly one register of elements. - */ - void SIMD_FLAGS(In, ForceInline) store( - this Register value, - std::span destination) noexcept; - - /** - * @brief Stores every active lane to register-aligned storage. - * @param value Register to store. - * @param destination Aligned destination for one complete register. - */ - void SIMD_FLAGS(In, ForceInline) store_aligned( - this Register value, - std::span destination) noexcept; - - /** - * @brief Stores the complete register bit pattern to raw bytes. - * @param value Register to store. - * @param destination Destination containing exactly one register of bytes. - */ - void SIMD_FLAGS(In, ForceInline) store_bytes( - this Register value, - std::span destination) noexcept; - - /** - * @brief Copies every active lane into a fixed-size array. - * @param value Register to copy. - * @return Array containing all lanes in low-to-high logical order. - */ - [[nodiscard]] constexpr - std::array SIMD_FLAGS(In, ForceInline) to_array( - this Register value) noexcept; - - /** - * @brief Returns one compile-time-selected lane. - * @tparam index Logical lane index. - * @param value Register containing the selected lane. - * @return Copy of the selected lane. - */ - template - requires(index < lane_count) - [[nodiscard]] constexpr element_type SIMD_FLAGS(In, ForceInline) lane( - this Register value) noexcept; - - /** - * @brief Returns the wrapped native register for intrinsic interoperation. - * @param value Register to unwrap. - * @return Complete native register value. - */ - [[nodiscard]] constexpr native_type SIMD_FLAGS(InOut, ForceInline) native( - this Register value) noexcept; - - /** - * @brief Adds corresponding lanes. - * @param lhs Left-hand register. - * @param rhs Right-hand register. - * @return Per-lane sum. - */ - [[nodiscard]] Register SIMD_FLAGS(InOut, ForceInline) operator+( - this Register lhs, - Register rhs) noexcept; - - /** - * @brief Subtracts corresponding lanes. - * @param lhs Left-hand register. - * @param rhs Right-hand register. - * @return Per-lane difference. - */ - [[nodiscard]] Register SIMD_FLAGS(InOut, ForceInline) operator-( - this Register lhs, - Register rhs) noexcept; - - /** - * @brief Multiplies corresponding lanes. - * @param lhs Left-hand register. - * @param rhs Right-hand register. - * @return Per-lane product. - */ - [[nodiscard]] Register SIMD_FLAGS(InOut, ForceInline) operator*( - this Register lhs, - Register rhs) noexcept; - - /** - * @brief Compares corresponding lanes for equality. - * @param lhs Left-hand register. - * @param rhs Right-hand register. - * @return Register-shaped lane predicate. - */ - [[nodiscard]] constexpr mask_type SIMD_FLAGS(InOut, ForceInline) compare_equal( - this Register lhs, - Register rhs) noexcept; - - /** - * @brief Tests whether every corresponding lane compares equal. - * @param lhs Left-hand register. - * @param rhs Right-hand register. - * @return `true` when all lanes compare equal. - */ - [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) operator==( - this Register lhs, - Register rhs) noexcept; - - /** - * @brief Tests whether any corresponding lane compares unequal. - * @param lhs Left-hand register. - * @param rhs Right-hand register. - * @return `true` when at least one lane compares unequal. - */ - [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) operator!=( - this Register lhs, - Register rhs) noexcept; + using element_type = element_t; + using api_type = Api; + using native_type = typename api_type::vector_t; + using mask_type = RegisterMask; + + constexpr static inline std::size_t register_width = bits; + constexpr static inline std::size_t byte_count = api_type::byte_count; + constexpr static inline std::size_t lane_count = api_type::element_count; + + /** @brief Owns the complete native register value represented by this aggregate. */ + native_type native = api_type::setzero(); + + /** + * @brief Returns a register with every active lane set to zero. + * @return Fully initialized zero register. + */ + [[nodiscard]] static constexpr Register SIMD_FLAGS(Out, ForceInline) zero() noexcept; + + /** + * @brief Broadcasts one scalar value to every active lane. + * @param value Scalar value to broadcast. + * @return Register containing `value` in every lane. + */ + [[nodiscard]] static constexpr Register SIMD_FLAGS(Out, ForceInline) broadcast( + element_type value) noexcept; + + /** + * @brief Constructs a register from exactly one complete logical lane list. + * @param lanes Values in low-to-high logical lane order. + * @return Register containing all supplied lane values. + */ + template ... lane_types> + requires(sizeof...(lane_types) == lane_count) + [[nodiscard]] static constexpr Register SIMD_FLAGS(Out, ForceInline) from_lanes( + lane_types &&...lanes) noexcept; + + /** + * @brief Constructs a register from one complete fixed-size lane array. + * @param source Source containing every active lane in logical order. + * @return Register containing all source lane values. + */ + [[nodiscard]] static constexpr Register SIMD_FLAGS(Out, ForceInline) from_array( + const std::array &source) noexcept; + + /** + * @brief Loads a complete register from potentially unaligned storage. + * @param source Source containing exactly one register of elements. + * @return Register loaded from `source`. + */ + [[nodiscard]] static Register SIMD_FLAGS(Out, ForceInline) load( + std::span source) noexcept; + + /** + * @brief Loads a complete register from register-aligned storage. + * @param source Aligned source containing exactly one register of elements. + * @return Register loaded from `source`. + */ + [[nodiscard]] static Register SIMD_FLAGS(Out, ForceInline) load_aligned( + std::span source) noexcept; + + /** + * @brief Loads one complete register bit pattern from raw bytes. + * @param source Source containing exactly one register of bytes. + * @return Register containing the source bit pattern. + */ + [[nodiscard]] static Register SIMD_FLAGS(Out, ForceInline) load_bytes( + std::span source) noexcept; + + /** + * @brief Stores every active lane to potentially unaligned storage. + * @param value Register to store. + * @param destination Destination for exactly one register of elements. + */ + void SIMD_FLAGS(In, ForceInline) store( + this Register value, + std::span destination) noexcept; + + /** + * @brief Stores every active lane to register-aligned storage. + * @param value Register to store. + * @param destination Aligned destination for one complete register. + */ + void SIMD_FLAGS(In, ForceInline) store_aligned( + this Register value, + std::span destination) noexcept; + + /** + * @brief Stores the complete register bit pattern to raw bytes. + * @param value Register to store. + * @param destination Destination containing exactly one register of bytes. + */ + void SIMD_FLAGS(In, ForceInline) store_bytes( + this Register value, + std::span destination) noexcept; + + /** + * @brief Copies every active lane into a fixed-size array. + * @param value Register to copy. + * @return Array containing all lanes in low-to-high logical order. + */ + [[nodiscard]] constexpr + std::array SIMD_FLAGS(In, ForceInline) to_array( + this Register value) noexcept; + + /** + * @brief Returns one compile-time-selected lane. + * @tparam index Logical lane index. + * @param value Register containing the selected lane. + * @return Copy of the selected lane. + */ + template + requires(index < lane_count) + [[nodiscard]] constexpr element_type SIMD_FLAGS(In, ForceInline) lane( + this Register value) noexcept; + + /** + * @brief Returns the wrapped native register for intrinsic interoperation. + * @param value Register to unwrap. + * @return Complete native register value. + */ + [[nodiscard]] constexpr native_type SIMD_FLAGS(InOut, ForceInline) native( + this Register value) noexcept; + + /** + * @brief Adds corresponding lanes. + * @param lhs Left-hand register. + * @param rhs Right-hand register. + * @return Per-lane sum. + */ + [[nodiscard]] Register SIMD_FLAGS(InOut, ForceInline) operator+( + this Register lhs, + Register rhs) noexcept; + + /** + * @brief Subtracts corresponding lanes. + * @param lhs Left-hand register. + * @param rhs Right-hand register. + * @return Per-lane difference. + */ + [[nodiscard]] Register SIMD_FLAGS(InOut, ForceInline) operator-( + this Register lhs, + Register rhs) noexcept; + + /** + * @brief Multiplies corresponding lanes. + * @param lhs Left-hand register. + * @param rhs Right-hand register. + * @return Per-lane product. + */ + [[nodiscard]] Register SIMD_FLAGS(InOut, ForceInline) operator*( + this Register lhs, + Register rhs) noexcept; + + /** + * @brief Compares corresponding lanes for equality. + * @param lhs Left-hand register. + * @param rhs Right-hand register. + * @return Register-shaped lane predicate. + */ + [[nodiscard]] constexpr mask_type SIMD_FLAGS(InOut, ForceInline) compare_equal( + this Register lhs, + Register rhs) noexcept; + + /** + * @brief Tests whether every corresponding lane compares equal. + * @param lhs Left-hand register. + * @param rhs Right-hand register. + * @return `true` when all lanes compare equal. + */ + [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) operator==( + this Register lhs, + Register rhs) noexcept; + + /** + * @brief Tests whether any corresponding lane compares unequal. + * @param lhs Left-hand register. + * @param rhs Right-hand register. + * @return `true` when at least one lane compares unequal. + */ + [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) operator!=( + this Register lhs, + Register rhs) noexcept; }; ``` @@ -583,7 +583,7 @@ Arithmetic and bitwise operators should initially accept only another ```cpp const auto adjusted = values * FloatRegister::broadcast(scale) + - FloatRegister::broadcast(offset); + FloatRegister::broadcast(offset); ``` This is intentionally more restrictive than `SimdVector`. It makes broadcast @@ -629,148 +629,148 @@ predicate lanes. * @tparam bits Width of the associated register in bits. */ template - requires RegisterAvailable + requires RegisterAvailable class RegisterMask final { public: - using register_type = Register; - using api_type = typename register_type::api_type; - using native_type = typename register_type::native_type; - using bits_type = std::conditional_t< - (register_type::lane_count <= 32), - std::uint32_t, - std::uint64_t>; - - constexpr static inline std::size_t register_width = bits; - constexpr static inline std::size_t lane_count = register_type::lane_count; - - /** - * @brief Owns the complete native predicate value represented by this aggregate. - * @pre Every logical lane is either all-zero or all-one when initialized directly. - */ - native_type native = api_type::setzero(); - - /** - * @brief Tests whether any predicate lane is set. - * @param value Predicate register to test. - * @return `true` when at least one lane is true. - */ - [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) any( - this RegisterMask value) noexcept; - - /** - * @brief Tests whether every predicate lane is set. - * @param value Predicate register to test. - * @return `true` when every lane is true. - */ - [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) all( - this RegisterMask value) noexcept; - - /** - * @brief Tests whether no predicate lane is set. - * @param value Predicate register to test. - * @return `true` when every lane is false. - */ - [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) none( - this RegisterMask value) noexcept; - - /** - * @brief Returns one compact bit per logical predicate lane. - * @param value Predicate register to reduce. - * @return Bit `i` set exactly when lane `i` is true. - */ - [[nodiscard]] constexpr bits_type SIMD_FLAGS(In, ForceInline) bits( - this RegisterMask value) noexcept; - - /** - * @brief Returns the wrapped native predicate register for intrinsic - * interoperation. - * @param value Predicate register to unwrap. - * @return Complete native predicate register value. - */ - [[nodiscard]] constexpr native_type SIMD_FLAGS(InOut, ForceInline) native( - this RegisterMask value) noexcept; - - /** - * @brief Selects lanes from two registers according to a predicate. - * @param condition Predicate controlling each selected lane. - * @param when_true Values selected for true predicate lanes. - * @param when_false Values selected for false predicate lanes. - * @return Register containing the selected values. - */ - [[nodiscard]] register_type SIMD_FLAGS(InOut, ForceInline) select( - this RegisterMask condition, - register_type when_true, - register_type when_false) noexcept; - - /** - * @brief Computes the intersection of two predicate registers. - * @param lhs Left-hand predicate register. - * @param rhs Right-hand predicate register. - * @return Predicate that is true where both inputs are true. - */ - [[nodiscard]] constexpr RegisterMask SIMD_FLAGS(InOut, ForceInline) operator&( - this RegisterMask lhs, - RegisterMask rhs) noexcept; - - /** - * @brief Computes the union of two predicate registers. - * @param lhs Left-hand predicate register. - * @param rhs Right-hand predicate register. - * @return Predicate that is true where either input is true. - */ - [[nodiscard]] constexpr RegisterMask SIMD_FLAGS(InOut, ForceInline) operator|( - this RegisterMask lhs, - RegisterMask rhs) noexcept; - - /** - * @brief Computes the exclusive union of two predicate registers. - * @param lhs Left-hand predicate register. - * @param rhs Right-hand predicate register. - * @return Predicate that is true where exactly one input is true. - */ - [[nodiscard]] constexpr RegisterMask SIMD_FLAGS(InOut, ForceInline) operator^( - this RegisterMask lhs, - RegisterMask rhs) noexcept; - - /** - * @brief Inverts every predicate lane. - * @param value Predicate register to invert. - * @return Predicate containing the inverse of every input lane. - */ - [[nodiscard]] constexpr RegisterMask SIMD_FLAGS(InOut, ForceInline) operator~( - this RegisterMask value) noexcept; - - /* - * Disabled compound assignment operators: their convenience does not justify - * the mutable-reference API surface, and MSVC 19.44 emits a redundant 32-byte - * stack-alignment frame for 256-bit wrapper mutation through references. - * Prefer lhs = lhs & rhs, lhs = lhs | rhs, or lhs = lhs ^ rhs. - * - /// @brief Intersects this predicate with another predicate. - /// @param lhs Predicate register to update. - /// @param rhs Right-hand predicate register. - /// @return Reference to the updated predicate. - constexpr auto SIMD_FLAGS(In, ForceInline) operator&=( - this RegisterMask &lhs, - RegisterMask rhs) noexcept -> RegisterMask &; - - /// @brief Unites this predicate with another predicate. - /// @param lhs Predicate register to update. - /// @param rhs Right-hand predicate register. - /// @return Reference to the updated predicate. - constexpr auto SIMD_FLAGS(In, ForceInline) operator|=( - this RegisterMask &lhs, - RegisterMask rhs) noexcept -> RegisterMask &; - - /// @brief Exclusively combines this predicate with another predicate. - /// @param lhs Predicate register to update. - /// @param rhs Right-hand predicate register. - /// @return Reference to the updated predicate. - constexpr auto SIMD_FLAGS(In, ForceInline) operator^=( - this RegisterMask &lhs, - RegisterMask rhs) noexcept -> RegisterMask &; - */ + using register_type = Register; + using api_type = typename register_type::api_type; + using native_type = typename register_type::native_type; + using bits_type = std::conditional_t< + (register_type::lane_count <= 32), + std::uint32_t, + std::uint64_t>; + + constexpr static inline std::size_t register_width = bits; + constexpr static inline std::size_t lane_count = register_type::lane_count; + + /** + * @brief Owns the complete native predicate value represented by this aggregate. + * @pre Every logical lane is either all-zero or all-one when initialized directly. + */ + native_type native = api_type::setzero(); + + /** + * @brief Tests whether any predicate lane is set. + * @param value Predicate register to test. + * @return `true` when at least one lane is true. + */ + [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) any( + this RegisterMask value) noexcept; + + /** + * @brief Tests whether every predicate lane is set. + * @param value Predicate register to test. + * @return `true` when every lane is true. + */ + [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) all( + this RegisterMask value) noexcept; + + /** + * @brief Tests whether no predicate lane is set. + * @param value Predicate register to test. + * @return `true` when every lane is false. + */ + [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) none( + this RegisterMask value) noexcept; + + /** + * @brief Returns one compact bit per logical predicate lane. + * @param value Predicate register to reduce. + * @return Bit `i` set exactly when lane `i` is true. + */ + [[nodiscard]] constexpr bits_type SIMD_FLAGS(In, ForceInline) bits( + this RegisterMask value) noexcept; + + /** + * @brief Returns the wrapped native predicate register for intrinsic + * interoperation. + * @param value Predicate register to unwrap. + * @return Complete native predicate register value. + */ + [[nodiscard]] constexpr native_type SIMD_FLAGS(InOut, ForceInline) native( + this RegisterMask value) noexcept; + + /** + * @brief Selects lanes from two registers according to a predicate. + * @param condition Predicate controlling each selected lane. + * @param when_true Values selected for true predicate lanes. + * @param when_false Values selected for false predicate lanes. + * @return Register containing the selected values. + */ + [[nodiscard]] register_type SIMD_FLAGS(InOut, ForceInline) select( + this RegisterMask condition, + register_type when_true, + register_type when_false) noexcept; + + /** + * @brief Computes the intersection of two predicate registers. + * @param lhs Left-hand predicate register. + * @param rhs Right-hand predicate register. + * @return Predicate that is true where both inputs are true. + */ + [[nodiscard]] constexpr RegisterMask SIMD_FLAGS(InOut, ForceInline) operator&( + this RegisterMask lhs, + RegisterMask rhs) noexcept; + + /** + * @brief Computes the union of two predicate registers. + * @param lhs Left-hand predicate register. + * @param rhs Right-hand predicate register. + * @return Predicate that is true where either input is true. + */ + [[nodiscard]] constexpr RegisterMask SIMD_FLAGS(InOut, ForceInline) operator|( + this RegisterMask lhs, + RegisterMask rhs) noexcept; + + /** + * @brief Computes the exclusive union of two predicate registers. + * @param lhs Left-hand predicate register. + * @param rhs Right-hand predicate register. + * @return Predicate that is true where exactly one input is true. + */ + [[nodiscard]] constexpr RegisterMask SIMD_FLAGS(InOut, ForceInline) operator^( + this RegisterMask lhs, + RegisterMask rhs) noexcept; + + /** + * @brief Inverts every predicate lane. + * @param value Predicate register to invert. + * @return Predicate containing the inverse of every input lane. + */ + [[nodiscard]] constexpr RegisterMask SIMD_FLAGS(InOut, ForceInline) operator~( + this RegisterMask value) noexcept; + + /* + * Disabled compound assignment operators: their convenience does not justify + * the mutable-reference API surface, and MSVC 19.44 emits a redundant 32-byte + * stack-alignment frame for 256-bit wrapper mutation through references. + * Prefer lhs = lhs & rhs, lhs = lhs | rhs, or lhs = lhs ^ rhs. + * + /// @brief Intersects this predicate with another predicate. + /// @param lhs Predicate register to update. + /// @param rhs Right-hand predicate register. + /// @return Reference to the updated predicate. + constexpr auto SIMD_FLAGS(In, ForceInline) operator&=( + this RegisterMask &lhs, + RegisterMask rhs) noexcept -> RegisterMask &; + + /// @brief Unites this predicate with another predicate. + /// @param lhs Predicate register to update. + /// @param rhs Right-hand predicate register. + /// @return Reference to the updated predicate. + constexpr auto SIMD_FLAGS(In, ForceInline) operator|=( + this RegisterMask &lhs, + RegisterMask rhs) noexcept -> RegisterMask &; + + /// @brief Exclusively combines this predicate with another predicate. + /// @param lhs Predicate register to update. + /// @param rhs Right-hand predicate register. + /// @return Reference to the updated predicate. + constexpr auto SIMD_FLAGS(In, ForceInline) operator^=( + this RegisterMask &lhs, + RegisterMask rhs) noexcept -> RegisterMask &; + */ }; ``` @@ -854,73 +854,73 @@ the explicit-object surface by generated-code and ABI tests. ### Construction and transfer ledger -| Current `Api` operation | Preferred `Register` form | Decision | -| --- | --- | --- | -| `load` | `Register::load(fixed_span)` | Canonical potentially unaligned full load | -| `load_aligned` | `Register::load_aligned(fixed_span)` | Retained with alignment precondition | -| `load_unaligned` | `Register::load(fixed_span)` | Redundant spelling omitted | -| `load_partial` | None | Partial data belongs to higher-level types | -| `load_unsafe` | None | Dynamic-extent unsafe load remains on `Api` | -| `store` to element span | `value.store(fixed_span)` | Canonical potentially unaligned full store | -| `store_aligned` | `value.store_aligned(fixed_span)` | Retained with alignment precondition | -| `store_unaligned` | `value.store(fixed_span)` | Redundant spelling omitted | -| `store` to fixed byte span | `value.store_bytes(fixed_byte_span)` | Renamed to make bit-pattern transfer explicit | -| `store` to dynamic byte span | None | Dynamic-extent transfer remains compatibility-only on `Api` | -| Fixed-byte `load` | `Register::load_bytes(fixed_byte_span)` | Symmetric bit-pattern transfer | -| `construct(array)` | `Register::from_array(array)` | Static factory; no ambiguous storage constructor | -| `to_array` | `value.to_array()` | Retained as a value conversion | -| `setzero` | Default construction and `Register::zero()` | Uses intrinsic-backed zero construction | -| `set1` | `Register::broadcast(value)` | Explicit scalar broadcast | -| `setr` | `Register::from_lanes(...)` | Requires exactly `lane_count` logical-order values | -| `set` | None | Native intrinsic argument order remains compatibility-only | -| `set_partial`, `setr_partial` | None | No partial or automatically filled lanes | +| Current `Api` operation | Preferred `Register` form | Decision | +| ----------------------------- | ------------------------------------------- | ----------------------------------------------------------- | +| `load` | `Register::load(fixed_span)` | Canonical potentially unaligned full load | +| `load_aligned` | `Register::load_aligned(fixed_span)` | Retained with alignment precondition | +| `load_unaligned` | `Register::load(fixed_span)` | Redundant spelling omitted | +| `load_partial` | None | Partial data belongs to higher-level types | +| `load_unsafe` | None | Dynamic-extent unsafe load remains on `Api` | +| `store` to element span | `value.store(fixed_span)` | Canonical potentially unaligned full store | +| `store_aligned` | `value.store_aligned(fixed_span)` | Retained with alignment precondition | +| `store_unaligned` | `value.store(fixed_span)` | Redundant spelling omitted | +| `store` to fixed byte span | `value.store_bytes(fixed_byte_span)` | Renamed to make bit-pattern transfer explicit | +| `store` to dynamic byte span | None | Dynamic-extent transfer remains compatibility-only on `Api` | +| Fixed-byte `load` | `Register::load_bytes(fixed_byte_span)` | Symmetric bit-pattern transfer | +| `construct(array)` | `Register::from_array(array)` | Static factory; no ambiguous storage constructor | +| `to_array` | `value.to_array()` | Retained as a value conversion | +| `setzero` | Default construction and `Register::zero()` | Uses intrinsic-backed zero construction | +| `set1` | `Register::broadcast(value)` | Explicit scalar broadcast | +| `setr` | `Register::from_lanes(...)` | Requires exactly `lane_count` logical-order values | +| `set` | None | Native intrinsic argument order remains compatibility-only | +| `set_partial`, `setr_partial` | None | No partial or automatically filled lanes | ### Arithmetic and reduction ledger -| Current `Api` operation | Preferred `Register` form | Result | -| --- | --- | --- | -| `add` | `lhs + rhs` | Same register type | -| `subtract` | `lhs - rhs` | Same register type | -| `multiply` | `lhs * rhs` | Same register type | -| `divide` | `lhs / rhs` | Same register type where supported | -| `modulus` | `lhs % rhs` | Same integral register type | -| `negate` | `-value` | Same register type | -| `min` | `lhs.min(rhs)` | Same register type | -| `max` | `lhs.max(rhs)` | Same register type | -| `multiply_add` | `lhs.multiply_add(rhs, addend)` | Same register type | -| `widen` | `value.widen_low()` | Explicit target `Register`; consumed lanes documented | -| `absolute` | `value.absolute()` | Same register type and intrinsic edge behavior | -| `sqrt` | `value.sqrt()` | Same register type where supported | -| `magnitude` | `value.magnitude()` | Floating groups broadcast; integer groups store an unchecked result only in their leading lane | -| `magnitude_checked` | `value.magnitude_checked()` | Integral groups store a saturated result followed by a canonical overflow mask | -| `normalize` | `value.normalize()` | Same floating register type | -| `avg` | `lhs.average(rhs)` | Same register type | -| `add_horizontal` | `lhs.horizontal_add(rhs)` | Same register type | -| `subtract_horizontal` | `lhs.horizontal_subtract(rhs)` | Same register type | -| `multiply_add_adjacent` | `lhs.multiply_add_adjacent(rhs)` | Explicit operation-result Register alias | -| `multiply_add_unsigned_signed_bytes` | `lhs.multiply_add_unsigned_signed_bytes(rhs)` | Explicit signed promoted-result Register alias | -| `sum_absolute_byte_differences` | `lhs.sum_absolute_byte_differences(rhs)` | Explicit unsigned-result Register alias | -| `multi_sum_absolute_byte_differences` | `lhs.multi_sum_absolute_byte_differences(rhs)` | Explicit unsigned-result Register alias | -| `min_position` | `value.min_position()` | `std::size_t` | -| `max_position` | `value.max_position()` | `std::size_t` | -| `add_saturated` | `lhs.add_saturated(rhs)` | Same register type | -| `subtract_saturated` | `lhs.subtract_saturated(rhs)` | Same register type | -| `hadd_saturated` | `lhs.horizontal_add_saturated(rhs)` | Same register type | -| `hsubtract_saturated` | `lhs.horizontal_subtract_saturated(rhs)` | Same register type | -| `add_subtract` | `lhs.add_subtract(rhs)` | Same floating register type | -| `dot_product` | `lhs.dot_product(rhs)` | Same register type with intrinsic-selected output lanes | +| Current `Api` operation | Preferred `Register` form | Result | +| ------------------------------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| `add` | `lhs + rhs` | Same register type | +| `subtract` | `lhs - rhs` | Same register type | +| `multiply` | `lhs * rhs` | Same register type | +| `divide` | `lhs / rhs` | Same register type where supported | +| `modulus` | `lhs % rhs` | Same integral register type | +| `negate` | `-value` | Same register type | +| `min` | `lhs.min(rhs)` | Same register type | +| `max` | `lhs.max(rhs)` | Same register type | +| `multiply_add` | `lhs.multiply_add(rhs, addend)` | Same register type | +| `widen` | `value.widen_low()` | Explicit target `Register`; consumed lanes documented | +| `absolute` | `value.absolute()` | Same register type and intrinsic edge behavior | +| `sqrt` | `value.sqrt()` | Same register type where supported | +| `magnitude` | `value.magnitude()` | Floating groups broadcast; integer groups store an unchecked result only in their leading lane | +| `magnitude_checked` | `value.magnitude_checked()` | Integral groups store a saturated result followed by a canonical overflow mask | +| `normalize` | `value.normalize()` | Same floating register type | +| `avg` | `lhs.average(rhs)` | Same register type | +| `add_horizontal` | `lhs.horizontal_add(rhs)` | Same register type | +| `subtract_horizontal` | `lhs.horizontal_subtract(rhs)` | Same register type | +| `multiply_add_adjacent` | `lhs.multiply_add_adjacent(rhs)` | Explicit operation-result Register alias | +| `multiply_add_unsigned_signed_bytes` | `lhs.multiply_add_unsigned_signed_bytes(rhs)` | Explicit signed promoted-result Register alias | +| `sum_absolute_byte_differences` | `lhs.sum_absolute_byte_differences(rhs)` | Explicit unsigned-result Register alias | +| `multi_sum_absolute_byte_differences` | `lhs.multi_sum_absolute_byte_differences(rhs)` | Explicit unsigned-result Register alias | +| `min_position` | `value.min_position()` | `std::size_t` | +| `max_position` | `value.max_position()` | `std::size_t` | +| `add_saturated` | `lhs.add_saturated(rhs)` | Same register type | +| `subtract_saturated` | `lhs.subtract_saturated(rhs)` | Same register type | +| `hadd_saturated` | `lhs.horizontal_add_saturated(rhs)` | Same register type | +| `hsubtract_saturated` | `lhs.horizontal_subtract_saturated(rhs)` | Same register type | +| `add_subtract` | `lhs.add_subtract(rhs)` | Same floating register type | +| `dot_product` | `lhs.dot_product(rhs)` | Same register type with intrinsic-selected output lanes | Operations whose intrinsic changes the lane type use constrained namespace-level alias templates. Keeping these aliases outside `Register` avoids conditional member declarations or helper-base storage that could complicate the exact one-native-member representation: -| Alias | Exact result mapping | -| --- | --- | +| Alias | Exact result mapping | +| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `multiply_add_adjacent_result_t` | `Register` for `int8_t`, `Register` for `uint8_t`, then the corresponding signedness at twice the lane width through 64 bits; 64-bit lanes remain 64-bit | -| `byte_multiply_add_result_t` | `Register` for supported signed/unsigned byte inputs | -| `sad_result_t` | `Register` | -| `multi_sad_result_t` | `Register` | +| `byte_multiply_add_result_t` | `Register` for supported signed/unsigned byte inputs | +| `sad_result_t` | `Register` | +| `multi_sad_result_t` | `Register` | The aliases are declared only when the corresponding backend operation is available. Each public operation names its exact alias as the return type rather @@ -931,20 +931,20 @@ formed mechanically. ### Bitwise and comparison ledger -| Current `Api` operation | Preferred `Register` form | Result | -| --- | --- | --- | -| `bitwise_and` | `lhs & rhs` | Same register type | -| `bitwise_or` | `lhs \| rhs` | Same register type | -| `bitwise_xor` | `lhs ^ rhs` | Same register type | -| `bitwise_not` | `~value` | Same register type | -| `bitwise_andnot` | `lhs.andnot(rhs)` | Same register type with existing operand polarity | -| `select` | `mask.select(when_true, when_false)` | Same Register type; canonical predicate remains Register-shaped | -| `movemask` | `value.movemask()` | Scalar mask with the selected intrinsic's native granularity | -| `movemask_slim` | `value.lane_sign_bits()` | Scalar mask with one bit per lane | -| `compare_equal`, `compare_greater`, `compare_greater_equal`, `compare_less`, `compare_less_equal` | Corresponding named comparison | `RegisterMask` preserving native predicates | -| `cmp_eq_mask`, `cmp_gt_mask`, `cmp_ge_mask`, `cmp_lt_mask`, `cmp_le_mask` | No compact-mask Register counterpart | Byte-granular legacy-compatible scalar mask | -| `cmp_eq_slim`, `cmp_gt_slim`, `cmp_ge_slim`, `cmp_lt_slim`, `cmp_le_slim` | Corresponding named comparison followed by `.bits()` | One compact bit per lane | -| Deprecated `cmp_eq`, `cmp_gt`, `cmp_ge`, `cmp_lt`, `cmp_le` | Corresponding explicitly named `cmp_*_mask` method | Byte-granular compatibility spelling | +| Current `Api` operation | Preferred `Register` form | Result | +| ------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | --------------------------------------------------------------- | +| `bitwise_and` | `lhs & rhs` | Same register type | +| `bitwise_or` | `lhs \| rhs` | Same register type | +| `bitwise_xor` | `lhs ^ rhs` | Same register type | +| `bitwise_not` | `~value` | Same register type | +| `bitwise_andnot` | `lhs.andnot(rhs)` | Same register type with existing operand polarity | +| `select` | `mask.select(when_true, when_false)` | Same Register type; canonical predicate remains Register-shaped | +| `movemask` | `value.movemask()` | Scalar mask with the selected intrinsic's native granularity | +| `movemask_slim` | `value.lane_sign_bits()` | Scalar mask with one bit per lane | +| `compare_equal`, `compare_greater`, `compare_greater_equal`, `compare_less`, `compare_less_equal` | Corresponding named comparison | `RegisterMask` preserving native predicates | +| `cmp_eq_mask`, `cmp_gt_mask`, `cmp_ge_mask`, `cmp_lt_mask`, `cmp_le_mask` | No compact-mask Register counterpart | Byte-granular legacy-compatible scalar mask | +| `cmp_eq_slim`, `cmp_gt_slim`, `cmp_ge_slim`, `cmp_lt_slim`, `cmp_le_slim` | Corresponding named comparison followed by `.bits()` | One compact bit per lane | +| Deprecated `cmp_eq`, `cmp_gt`, `cmp_ge`, `cmp_lt`, `cmp_le` | Corresponding explicitly named `cmp_*_mask` method | Byte-granular compatibility spelling | The legacy scalar comparison-mask layout is not uniform across integral and floating backends. `mask.bits()` deliberately normalizes it to one bit @@ -961,22 +961,22 @@ requires an explicit integer reinterpretation followed by integer comparison. ### Rearrangement ledger -| Current `Api` operation | Preferred `Register` form | Decision | -| --- | --- | --- | -| `expand` | None | Ambiguous legacy widening alias remains compatibility-only | -| `compress` | None | Ambiguous legacy narrowing alias remains compatibility-only | -| `extract` | `value.lane()` | Compile-time logical lane extraction | -| Runtime `extract_slow` | None | Explicit Api slow path; Register retains compile-time lane access | -| `lower_half` | `value.lower_half()` | Returns `Register` from a 256-bit source | -| `insert` | `value.with_lane(lane)` | Compile-time logical lane replacement | -| `unpack_lo` | `lhs.unpack_low(rhs)` | Wrapped backend result | -| `unpack_hi` | `lhs.unpack_high(rhs)` | Wrapped backend result | -| `shuffle` | `value.shuffle()` | One compile-time logical source-lane selector per output lane | -| `Api::shuffle` | `value.shuffle_bytes()` | One compile-time logical source-byte selector per output byte; result retains `T` | -| Register-selector `shuffle(value, selector)` | None | Native Api runtime control; Register exposes portable logical and byte shuffle forms | -| `shuffle_lo`; `shuffle_lo_slow` | `value.shuffle_low()` | Compile-time immediate form; scalar runtime control remains Api-only | -| `shuffle_hi`; `shuffle_hi_slow` | `value.shuffle_high()` | Compile-time immediate form; scalar runtime control remains Api-only | -| `blend`; register-mask `blend`; `blend_slow` | `lhs.blend(rhs)` | Immediate blend maps directly; predicate selection uses `mask.select(lhs, rhs)`; scalar runtime control remains Api-only | +| Current `Api` operation | Preferred `Register` form | Decision | +| -------------------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| `expand` | None | Ambiguous legacy widening alias remains compatibility-only | +| `compress` | None | Ambiguous legacy narrowing alias remains compatibility-only | +| `extract` | `value.lane()` | Compile-time logical lane extraction | +| Runtime `extract_slow` | None | Explicit Api slow path; Register retains compile-time lane access | +| `lower_half` | `value.lower_half()` | Returns `Register` from a 256-bit source | +| `insert` | `value.with_lane(lane)` | Compile-time logical lane replacement | +| `unpack_lo` | `lhs.unpack_low(rhs)` | Wrapped backend result | +| `unpack_hi` | `lhs.unpack_high(rhs)` | Wrapped backend result | +| `shuffle` | `value.shuffle()` | One compile-time logical source-lane selector per output lane | +| `Api::shuffle` | `value.shuffle_bytes()` | One compile-time logical source-byte selector per output byte; result retains `T` | +| Register-selector `shuffle(value, selector)` | None | Native Api runtime control; Register exposes portable logical and byte shuffle forms | +| `shuffle_lo`; `shuffle_lo_slow` | `value.shuffle_low()` | Compile-time immediate form; scalar runtime control remains Api-only | +| `shuffle_hi`; `shuffle_hi_slow` | `value.shuffle_high()` | Compile-time immediate form; scalar runtime control remains Api-only | +| `blend`; register-mask `blend`; `blend_slow` | `lhs.blend(rhs)` | Immediate blend maps directly; predicate selection uses `mask.select(lhs, rhs)`; scalar runtime control remains Api-only | Logical shuffle selectors use low-to-high lane numbering for the element type. The selector count must equal the register lane count, repeated selectors are @@ -996,24 +996,24 @@ nevertheless remains `Register`. ### Shift and conversion ledger -| Current `Api` operation | Preferred `Register` form | Result | -| --- | --- | --- | -| `shift_left` | `value << count` | Per-lane integral shift | -| `shift_right` | `value.logical_shift_right(count)` | Per-lane logical shift for signed or unsigned lanes | -| `shift_right_arithmetic` | `value >> count` | Per-lane arithmetic shift for signed lanes | -| Runtime `shift_bytes_left_slow` | `value.shift_bytes_left_slow(count)` | Complete integral 128-bit register byte shift | -| Compile-time `shift_bytes_left` | `value.shift_bytes_left()` | Complete integral 128- or 256-bit register byte shift | -| Runtime `shift_bytes_right_slow` | `value.shift_bytes_right_slow(count)` | Complete integral 128-bit register byte shift | -| Compile-time `shift_bytes_right` | `value.shift_bytes_right()` | Complete integral 128- or 256-bit register byte shift | -| Runtime `shift_bits_left_slow` | `value.shift_bits_left_slow(count)` | Complete integral 128-bit bit-string shift | -| Compile-time `shift_bits_left` | `value.shift_bits_left()` | Complete integral 128-bit bit-string shift | -| Runtime `shift_bits_right_slow` | `value.shift_bits_right_slow(count)` | Complete integral 128-bit bit-string shift | -| Compile-time shift_bits_right | alue.shift_bits_right() | Complete integral 128-bit bit-string shift | -| it_cast | alue.bit_cast() | Full-width bit-preserving reinterpretation | -| `convert_to_float` | `value.convert()` | `Register` from supported 32-bit integer lanes | -| `convert_to_int` | `value.convert()` | `Register` from float lanes | -| Explicit-target `convert` | `value.convert()` | Explicit target type | -| Inferred-target `convert` | None | Complementary-type inference remains compatibility-only on `Api` | +| Current `Api` operation | Preferred `Register` form | Result | +| ----------------------------------- | ------------------------------------- | ---------------------------------------------------------------- | +| `shift_left` | `value << count` | Per-lane integral shift | +| `shift_right` | `value.logical_shift_right(count)` | Per-lane logical shift for signed or unsigned lanes | +| `shift_right_arithmetic` | `value >> count` | Per-lane arithmetic shift for signed lanes | +| Runtime `shift_bytes_left_slow` | `value.shift_bytes_left_slow(count)` | Complete integral 128-bit register byte shift | +| Compile-time `shift_bytes_left` | `value.shift_bytes_left()` | Complete integral 128- or 256-bit register byte shift | +| Runtime `shift_bytes_right_slow` | `value.shift_bytes_right_slow(count)` | Complete integral 128-bit register byte shift | +| Compile-time `shift_bytes_right` | `value.shift_bytes_right()` | Complete integral 128- or 256-bit register byte shift | +| Runtime `shift_bits_left_slow` | `value.shift_bits_left_slow(count)` | Complete integral 128-bit bit-string shift | +| Compile-time `shift_bits_left` | `value.shift_bits_left()` | Complete integral 128-bit bit-string shift | +| Runtime `shift_bits_right_slow` | `value.shift_bits_right_slow(count)` | Complete integral 128-bit bit-string shift | +| Compile-time shift_bits_right | alue.shift_bits_right() | Complete integral 128-bit bit-string shift | +| it_cast | alue.bit_cast() | Full-width bit-preserving reinterpretation | +| `convert_to_float` | `value.convert()` | `Register` from supported 32-bit integer lanes | +| `convert_to_int` | `value.convert()` | `Register` from float lanes | +| Explicit-target `convert` | `value.convert()` | Explicit target type | +| Inferred-target `convert` | None | Complementary-type inference remains compatibility-only on `Api` | `operator>>` is available only when it has one unambiguous hardware meaning. Unsigned lanes use the logical shift. Signed lanes use the arithmetic shift. @@ -1023,13 +1023,13 @@ request zero fill. Shift-count behavior is part of the public contract and matches the existing backend operation rather than C++ scalar-shift rules: -| Shift family | Count contract | -| --- | --- | -| Per-lane left or logical right | Runtime count must be nonnegative; counts at least the lane width produce zero lanes | -| Per-lane arithmetic right | Runtime count must be nonnegative; counts at least the lane width clamp to `lane_width - 1` and therefore sign-fill | -| 128-bit byte shifts | Counts at most zero return the input; counts at least 16 return zero | -| Runtime 128-bit whole-register bit shifts | Counts at most zero return the input; counts at least 128 return zero | -| Compile-time 128-bit whole-register bit shifts | Negative counts are rejected; counts at least 128 produce zero | +| Shift family | Count contract | +| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | +| Per-lane left or logical right | Runtime count must be nonnegative; counts at least the lane width produce zero lanes | +| Per-lane arithmetic right | Runtime count must be nonnegative; counts at least the lane width clamp to `lane_width - 1` and therefore sign-fill | +| 128-bit byte shifts | Counts at most zero return the input; counts at least 16 return zero | +| Runtime 128-bit whole-register bit shifts | Counts at most zero return the input; counts at least 128 return zero | +| Compile-time 128-bit whole-register bit shifts | Negative counts are rejected; counts at least 128 produce zero | The implementation must not introduce release-only undefined behavior for a documented count. Negative per-lane shift counts are invalid runtime inputs and @@ -1038,14 +1038,14 @@ follow the SimdLib precondition policy; tests cover the boundary values `0`, ### Collection and internal ledger -| Current `Api` operation | `Register` decision | -| --- | --- | -| `transform_pack` | Remains a collection algorithm on `Api` or its future algorithm owner | -| Unary in-place `transform` | Remains a collection algorithm | -| Unary separate-output `transform` | Remains a collection algorithm | -| Binary `transform` | Remains a collection algorithm | -| `TransformForMaxPosition` | Internal helper; no public `Register` counterpart | -| `compare_each_element` | Internal fallback helper used by the comparison adapter | +| Current `Api` operation | `Register` decision | +| --------------------------------- | --------------------------------------------------------------------- | +| `transform_pack` | Remains a collection algorithm on `Api` or its future algorithm owner | +| Unary in-place `transform` | Remains a collection algorithm | +| Unary separate-output `transform` | Remains a collection algorithm | +| Binary `transform` | Remains a collection algorithm | +| `TransformForMaxPosition` | Internal helper; no public `Register` counterpart | +| `compare_each_element` | Internal fallback helper used by the comparison adapter | All preferred register-local operations return `Register`, `RegisterMask`, or an explicitly documented scalar. No preferred operation exposes a raw intrinsic @@ -1304,14 +1304,14 @@ Representative migration: // Existing interface. using U32Api = SimdLib::Api<128, std::uint32_t>; const auto old_result = U32Api::bitwise_or( - U32Api::add(lhs, rhs), - U32Api::set1(1)); + U32Api::add(lhs, rhs), + U32Api::set1(1)); // Register interface. using U32Register = SimdLib::Register; const auto new_result = - (U32Register{lhs} + U32Register{rhs}) | - U32Register::broadcast(1); + (U32Register{lhs} + U32Register{rhs}) | + U32Register::broadcast(1); ``` ## Validation strategy From 927988a37c87d2455ae6cb213f0027dc7a077b5e Mon Sep 17 00:00:00 2001 From: David Sisco Date: Sat, 12 Sep 2026 19:49:22 -0700 Subject: [PATCH 02/16] docs: improve register type proposal docs: improve register type proposal docs: implementation plan for Register type [Phase 0]: Freeze the Contract and Record the Baseline [Phase 1]: Add Language Availability and Build Integration docs: add new phase for implementing containerized multi-compiler build matrix [Phase 2]: Establish Reproducible Containerized Compiler Environments fix: only force full project rebuilds within CI environments [Phae 3]: initial phase 3 chore: always compare compiler codegen with stack protection flags turned on fix: move constexpr code out of method body to prevent MSVC being stupid and forcing bad codegen fix: MSVC has bad heuristics fix: compilers (MSVC) can de-optimize methods due to characteristics of constexpr code if its within the main body perf: implement method flattening for transform methods chore: remove redundant inline statements dev: add todo tasks for supporting Intel ICX and NVC++ compilers chore: update phase 3 tasks [Phase 3]: Establish the Representation and Performance Harness [Phase 4]: Implement Register Construction, Observation, and Transfer feat: implement missing api methods & drop 32bit support [Phase 5]: Implement RegisterMask, Comparisons, and Selection perf: mark applicable methods for flattening and register-only-access [Phase 6]: Implement Basic Arithmetic, Bitwise Operations, and Shifts perf: improve code gen for MSVC docs: additional project tasks [Phase 7]: Implement Specialized Arithmetic and Reductions refactor: encapsulate all layer concepts into interface-like namespaces [Phase 8]: Implement Rearrangement and Conversion Operations dev: setup clang format chore: fix formatting error dev: add task to format all files chore: run format all [Phase 9]: Complete the Operation and Constraint Matrix [Phase 10]: Qualify Correctness, Constexpr, Preconditions, ABI, and Performance [Phase 11]: Expose, Migrate, Document, and Close Out dev: update project todo docs: refresh Register closeout evidence dev: update project todo dev: update project todo docs: implementation tasklist for build pipeline improvements docs: revise implementation plan chore: commit temp changes before refactoring work [Phase 0]: Freeze the Matrix and Measure the Baseline [Phase 1]: Create Exhaustive CMake Build Profiles [Phase 2]: Separate Build and Test Responsibilities [Phase 3]: Refactor Container Matrix Orchestration [Phase 4]: Add Native Compiler and Top-Level Commands [Phase 5]: Migrate CI Without Losing Coverage [Phase 6]: Prove Completeness and Cache Reuse [Phase 7]: Document and Migrate Interfaces docs: add additional phase for build-time improvement [Phase 7]: Document and Migrate Interfaces [Phase 8]: Remove Retired Windows GNU Support References chore: remove completed task list docs: task list for improving build performance [Phase 0]: Establish the Compilation Baseline [Phase 1]: Separate Benchmark Compilation from the Default Build [Phase 2]: Evaluate the Constexpr Compilation Burden [Phase 3]: Analyze the `uint128_t` Compilation Burden [Phase 4]: Analyze the `Bmi.h` Compilation Burden docs: plan to expand Api::shuffle support to all integer widths [Phase 0]: Freeze the Existing Surface and Baseline [Phase 1]: Establish Independent Behavioral and Constraint Oracles [Phase 2]: Implement the 128-Bit Backends [Phase 3]: Implement the 256-Bit Backends [Phase 4]: Generalize the Public Layers [Phase 5]: Prove Runtime Code Quality and Compilation Cost [Phase 6]: Document and Complete Validation docs: remove completed task list chore: code comments refactor: encapsulate vector aliases within their own file dev: update project task list chore: remove completed task list docs: proposal plan for new unified SIMD-FLAGS macro system refactor: redefine alias types to point to register type instead of old SimdVector feat: implement `shuffle_bytes<>` method for Simd Register class tests: test coverage for Register `shuffle_bytes` method docs: implementation plan for new unified SIMD-FLAGS macro system [Phase 0]: Freeze the Grammar and Contract [Phase 1]: Prove the Macro Grammar Is Implementable revise SIMD_FLAGS macro prototype [Phase 2]: Qualify Compiler Placement and Attribute Composition [Phase 3]: Implement the Public Macro and Compiler Adapters [Phase 4]: Establish Contract and Code-Generation Tests [Phase 5]: Inventory and Classify Existing Declarations fix: minor corrections to api & implementation layers docs: refactor tasklist for SIMD layer methodss which use stack memory during runtime [Task 1]: Restore a Focused Compilable Baseline [Task 2]: Constant-Evaluation Helper Boundary [Task 3]: Unconditional API Delegation [Task 4]: Specialized 128-Bit Runtime Extraction docs: expand planning tasklist [Task 5]: Specialized 256-Bit Runtime Extraction docs: amend planning doc with new performance phase [Task 6]: Implementation Extraction Naming Consolidation [Task 7]: Specialized 128-Bit Runtime Insertion [Task 8/9]: Specialized 256-Bit Runtime Insertion [Task 10]: Specialized 128-Bit Integer Remainder Extensions docs: extend planning tasks with codegen test suite cleanup work [Task 11]: Specialized 256-Bit Integer Remainder Extensions [Task 12]: Complete-Register Runtime Byte Shifts docs: extend planning tasks with codegen test suite cleanup work [Task 13]: Complete-Register Bit Shifts [Task 14]: 128-Bit 64-Bit-Lane `setr` [Task 15]: Immediate-Control Runtime Naming [Task 16]: Method-Flag Inventory Reconciliation [Task 17]: Focused Cross-Compiler Validation [Task 18]: Branchless 256-Bit Runtime Extraction Evaluation [Task 19]: Permanent Generated-Code Fixture Rationalization [Task 20]: Complete Permanent Generated-Code Suite Audit and Final Integration docs: remove completed task list chore: code comments docs: initial build pipeline optimization proposal [Phase 0]: Freeze Validation Ownership and Baseline the Pipeline [Phase 1]: Replace the Monolithic Artifact Sweep with Scoped Aggregates [Phase 2]: Deduplicate Repository and Compiler-Front-End Contracts [Phase 3]: Separate Optimized Codegen Gates from Diagnostic Codegen [Phase 4]: Reduce the Ordinary Debug Compiler Matrix [Phase 5]: Slim Runtime, Sanitizer, and Coverage Target Sets [Phase 6]: Deduplicate Examples, ODR, Smoke, and External Consumers [Phase 7]: Refactor Presets and Unified Pipeline Orchestration [Phase 8]: Add Matrix-Ownership and No-Rebuild Regression Coverage [Phase 9]: Measure, Qualify, and Document [Phase 10]: Remove Temporary Planning and Evidence Documentation [Phase 6]: Migrate Implementation and Api Layers [Phase 7]: Migrate Register-Facing and Remaining Public Code [Phase 8]: Remove the Legacy Declaration Surface and Add Audits [Phase 9]: Document, Qualify, and Close Out chore: removing leftover legacy macro usages chore: remove completed task list dev: update project task list chore: remove obsolete files chore: Remove `MethodFlagsInventory.csv` from the repo and audit tooling chore: Remove `MethodFlagsRegisterOnly.csv` from the repo and audit tooling. docs: build pipeline cleanup plan [Phase 1]: Retire Method-Flags Source Auditing [Phase 2]: Remove Public-Header `static_assert` Auditing [Phase 3]: Restructure Validation-Matrix and Pipeline Validation chore: remove completed task list docs: register byte shift extension plan dev: update project todo [Phase 1]: Fix the Naming and Semantic Contract [Phase 2]: Rename the Existing Complete-Register Shift Surface [Phase 3]: Implement Immediate Complete-Register Byte Shifts [Phase 4]: Expose the Immediate API Through `Api` and `Register` dev: update project todo [Phase 5]: Prove Semantics, Availability, and Generated Code [Phase 6]: Documentation and Final Cleanup chore: remove completed task list docs: implementation plan for compile-time register constants [Phase 1]: Fix the Public and Layer Contracts chore: remove deprecated task list chore: remove python from github ci and use choco instead fix: save & restore Clang/CMake paths before & after importing the MSVC environment so they arent overwritten dev: lower minimum version for CMake to 3.31 & CLang to 20 dev: fix linux container build for github ci docs: initial implementation plan for PartialRegister type docs: amend implementation plan for PartialRegister type [Phase 1]: Freeze the Public Contract and Operation Ledger [Phase 2]: Establish Headers, Availability, and the Object Model [Phase 3]: Implement PartialRegisterMask and Predicate Semantics [Phase 4]: Implement Construction, Transfer, and Observation [Phase 5]: Implement Arithmetic and Specialized Operations [Phase 6]: Implement Bitwise, Shift, and Comparison Operations [Phase 7]: Implement Rearrangement and Conversion Operations [Phase 8]: Complete the Public Surface and Integration [Phase 9]: Qualify ABI, Generated Code, and Performance [Phase 10]: Documentation, Full Validation, and Cleanup chore: remove unneeded documents chore: remove unneeded documents dev: add MSVC LLVM 20 to build matrix dev: extract partial register codegen snapshot tests into json config file docs: initial proposal for aligning Register types codegen tests with new standards --- .github/workflows/ci.yml | 68 +- CMakeLists.txt | 27 +- README.md | 13 +- benchmarks/Register.benchmarks.cpp | 50 + cmake/CheckPublicConsumerBoundary.cmake | 2 + cmake/CompareRegisterCodegen.cmake | 76 +- .../RunInstalledPartialRegisterConsumer.cmake | 47 + cmake/SimdLibConfig.cmake.in | 3 + cmake/ValidateCodegenRecords.cmake | 12 +- cmake/VerifyPublicConsumptionProfile.cmake | 3 + cmake/development/ConfigurationProbes.cmake | 108 +- cmake/development/ConstexprProbes.cmake | 25 + cmake/development/Development.cmake | 1 + cmake/development/HeaderProbes.cmake | 71 +- .../development/PartialRegisterCodegen.cmake | 637 ++++++++ .../PartialRegisterCodegenProfiles.json | 165 ++ cmake/development/RuntimeTests.cmake | 73 +- cmake/development/SmokeTests.cmake | 59 + containers/Dockerfile.windows-clang20 | 89 ++ docs/BuildPipeline.md | 28 +- docs/ContainerValidation.md | 12 + docs/PartialRegisterOperationLedger.md | 266 ++++ docs/PartialRegisterQualification.md | 320 ++++ docs/PublicNamespace.md | 20 +- docs/RegisterCodegenAudit.md | 174 --- docs/RegisterCodegenPolicyProposal.md | 253 ++++ docs/RegisterCodegenSymbolAudit.csv | 811 ---------- ...egisterProposal.md => RegisterContract.md} | 983 ++++++------ docs/RegisterImplementationMatrix.md | 22 +- docs/RegisterQualification.md | 54 +- include/SimdLib/Aliases.h | 101 +- include/SimdLib/Api.h | 246 +++ include/SimdLib/Detail/Implementations.h | 30 + include/SimdLib/IApi.h | 116 ++ include/SimdLib/IPartialRegisterMask.h | 52 + include/SimdLib/IRegister.h | 167 ++- include/SimdLib/PartialRegister.h | 1336 +++++++++++++++++ include/SimdLib/PartialRegisterFwd.h | 182 +++ include/SimdLib/PartialRegisterMask.h | 220 +++ include/SimdLib/SimdLib.h | 2 + tests/ApiPartialTransfer.tests.cpp | 162 ++ tests/ImplementationHalfTransfer.tests.cpp | 79 + tests/PartialRegisterArithmetic.tests.cpp | 498 ++++++ ...alRegisterBitwiseShiftComparison.tests.cpp | 299 ++++ ...tialRegisterConstructionTransfer.tests.cpp | 199 +++ tests/PartialRegisterObjectModel.tests.cpp | 200 +++ .../PartialRegisterOperationMatrix.tests.cpp | 218 +++ ...lRegisterRearrangementConversion.tests.cpp | 432 ++++++ ...ialRegisterSpecializedOperations.tests.cpp | 347 +++++ tests/RegisterPreconditionFailure.tests.cpp | 101 ++ .../PartialRegisterEnabledProbe.cpp | 17 + tests/codegen/ApiPartialTransferCodegen.cpp | 2 + .../ApiPartialTransferCodegenFixture.h | 145 ++ .../codegen/ApiPartialTransferCodegenRaw.cpp | 2 + tests/codegen/PartialRegisterAbi.cpp | 204 +++ tests/codegen/PartialRegisterAbiRaw.cpp | 176 +++ .../PartialRegisterArithmeticCodegen.cpp | 229 +++ .../PartialRegisterArithmeticCodegenRaw.cpp | 309 ++++ .../codegen/PartialRegisterGeneralCodegen.cpp | 85 ++ .../PartialRegisterGeneralCodegenRaw.cpp | 106 ++ tests/codegen/PartialRegisterMaskCodegen.cpp | 45 + .../codegen/PartialRegisterMaskCodegenRaw.cpp | 82 + .../PartialRegisterExcessiveActiveCount.cpp | 8 + .../PartialRegisterFractionalBitCast.cpp | 9 + .../PartialRegisterFullTransferExtent.cpp | 39 + .../register/PartialRegisterHeaderCxx20.cpp | 7 + .../PartialRegisterImplicitNative.cpp | 9 + .../PartialRegisterImplicitScalar.cpp | 9 + .../PartialRegisterInactiveLaneIndex.cpp | 17 + .../PartialRegisterInactiveUpperHalf.cpp | 14 + .../PartialRegisterInactiveWidenResult.cpp | 10 + ...tialRegisterInvalidByteShuffleSelector.cpp | 9 + .../PartialRegisterInvalidShuffleSelector.cpp | 9 + ...ialRegisterInvalidShuffleSelectorCount.cpp | 9 + .../PartialRegisterMaskInvalidActiveCount.cpp | 5 + .../PartialRegisterNegativeBitShiftLeft.cpp | 12 + .../PartialRegisterNegativeBitShiftRight.cpp | 12 + .../PartialRegisterNegativeByteShiftLeft.cpp | 12 + .../PartialRegisterNegativeByteShiftRight.cpp | 12 + .../PartialRegisterOversizedLaneList.cpp | 12 + .../PartialRegisterRequirementCxx20.cpp | 8 + .../PartialRegisterUnavailableWidth.cpp | 9 + .../PartialRegisterZeroActiveCount.cpp | 8 + tests/constexpr/ApiConstexprContracts.h | 26 +- .../PartialRegisterConstexpr.tests.cpp | 244 +++ .../PartialRegisterMaskConstexpr.tests.cpp | 55 + tests/consumer/register.cpp | 20 +- tests/consumer/register_api.cpp | 6 + tests/consumer/register_api.h | 8 + tests/headers/AliasesHeaderProbe.cpp | 5 + .../IPartialRegisterMaskHeaderProbe.cpp | 3 + .../InstalledPartialRegisterHeaderProbe.cpp | 17 + ...nstalledPartialRegisterMaskHeaderProbe.cpp | 7 + .../headers/PartialRegisterFwdHeaderProbe.cpp | 10 + tests/headers/PartialRegisterHeaderProbe.cpp | 17 + .../PartialRegisterMaskHeaderProbe.cpp | 14 + tests/headers/SimdLibRegisterHeaderProbe.cpp | 4 + tests/installed_consumer/CMakeLists.txt | 61 + tests/installed_consumer/main.cpp | 50 + .../PartialRegisterRepresentation.tests.cpp | 114 ++ tests/partial_register_odr/fixture.h | 35 + tests/partial_register_odr/main.cpp | 25 + .../second_translation_unit.cpp | 18 + tools/Run-WindowsClang20Container.ps1 | 59 + tools/validation-matrix.json | 5 +- wiki/Technical-Reference.md | 75 +- 106 files changed, 10365 insertions(+), 1613 deletions(-) create mode 100644 cmake/RunInstalledPartialRegisterConsumer.cmake create mode 100644 cmake/SimdLibConfig.cmake.in create mode 100644 cmake/development/PartialRegisterCodegen.cmake create mode 100644 cmake/development/PartialRegisterCodegenProfiles.json create mode 100644 containers/Dockerfile.windows-clang20 create mode 100644 docs/PartialRegisterOperationLedger.md create mode 100644 docs/PartialRegisterQualification.md delete mode 100644 docs/RegisterCodegenAudit.md create mode 100644 docs/RegisterCodegenPolicyProposal.md delete mode 100644 docs/RegisterCodegenSymbolAudit.csv rename docs/{RegisterProposal.md => RegisterContract.md} (60%) create mode 100644 include/SimdLib/IPartialRegisterMask.h create mode 100644 include/SimdLib/PartialRegister.h create mode 100644 include/SimdLib/PartialRegisterFwd.h create mode 100644 include/SimdLib/PartialRegisterMask.h create mode 100644 tests/ApiPartialTransfer.tests.cpp create mode 100644 tests/ImplementationHalfTransfer.tests.cpp create mode 100644 tests/PartialRegisterArithmetic.tests.cpp create mode 100644 tests/PartialRegisterBitwiseShiftComparison.tests.cpp create mode 100644 tests/PartialRegisterConstructionTransfer.tests.cpp create mode 100644 tests/PartialRegisterObjectModel.tests.cpp create mode 100644 tests/PartialRegisterOperationMatrix.tests.cpp create mode 100644 tests/PartialRegisterRearrangementConversion.tests.cpp create mode 100644 tests/PartialRegisterSpecializedOperations.tests.cpp create mode 100644 tests/availability/PartialRegisterEnabledProbe.cpp create mode 100644 tests/codegen/ApiPartialTransferCodegen.cpp create mode 100644 tests/codegen/ApiPartialTransferCodegenFixture.h create mode 100644 tests/codegen/ApiPartialTransferCodegenRaw.cpp create mode 100644 tests/codegen/PartialRegisterAbi.cpp create mode 100644 tests/codegen/PartialRegisterAbiRaw.cpp create mode 100644 tests/codegen/PartialRegisterArithmeticCodegen.cpp create mode 100644 tests/codegen/PartialRegisterArithmeticCodegenRaw.cpp create mode 100644 tests/codegen/PartialRegisterGeneralCodegen.cpp create mode 100644 tests/codegen/PartialRegisterGeneralCodegenRaw.cpp create mode 100644 tests/codegen/PartialRegisterMaskCodegen.cpp create mode 100644 tests/codegen/PartialRegisterMaskCodegenRaw.cpp create mode 100644 tests/compile_fail/register/PartialRegisterExcessiveActiveCount.cpp create mode 100644 tests/compile_fail/register/PartialRegisterFractionalBitCast.cpp create mode 100644 tests/compile_fail/register/PartialRegisterFullTransferExtent.cpp create mode 100644 tests/compile_fail/register/PartialRegisterHeaderCxx20.cpp create mode 100644 tests/compile_fail/register/PartialRegisterImplicitNative.cpp create mode 100644 tests/compile_fail/register/PartialRegisterImplicitScalar.cpp create mode 100644 tests/compile_fail/register/PartialRegisterInactiveLaneIndex.cpp create mode 100644 tests/compile_fail/register/PartialRegisterInactiveUpperHalf.cpp create mode 100644 tests/compile_fail/register/PartialRegisterInactiveWidenResult.cpp create mode 100644 tests/compile_fail/register/PartialRegisterInvalidByteShuffleSelector.cpp create mode 100644 tests/compile_fail/register/PartialRegisterInvalidShuffleSelector.cpp create mode 100644 tests/compile_fail/register/PartialRegisterInvalidShuffleSelectorCount.cpp create mode 100644 tests/compile_fail/register/PartialRegisterMaskInvalidActiveCount.cpp create mode 100644 tests/compile_fail/register/PartialRegisterNegativeBitShiftLeft.cpp create mode 100644 tests/compile_fail/register/PartialRegisterNegativeBitShiftRight.cpp create mode 100644 tests/compile_fail/register/PartialRegisterNegativeByteShiftLeft.cpp create mode 100644 tests/compile_fail/register/PartialRegisterNegativeByteShiftRight.cpp create mode 100644 tests/compile_fail/register/PartialRegisterOversizedLaneList.cpp create mode 100644 tests/compile_fail/register/PartialRegisterRequirementCxx20.cpp create mode 100644 tests/compile_fail/register/PartialRegisterUnavailableWidth.cpp create mode 100644 tests/compile_fail/register/PartialRegisterZeroActiveCount.cpp create mode 100644 tests/constexpr/PartialRegisterConstexpr.tests.cpp create mode 100644 tests/constexpr/PartialRegisterMaskConstexpr.tests.cpp create mode 100644 tests/headers/IPartialRegisterMaskHeaderProbe.cpp create mode 100644 tests/headers/InstalledPartialRegisterHeaderProbe.cpp create mode 100644 tests/headers/InstalledPartialRegisterMaskHeaderProbe.cpp create mode 100644 tests/headers/PartialRegisterFwdHeaderProbe.cpp create mode 100644 tests/headers/PartialRegisterHeaderProbe.cpp create mode 100644 tests/headers/PartialRegisterMaskHeaderProbe.cpp create mode 100644 tests/installed_consumer/CMakeLists.txt create mode 100644 tests/installed_consumer/main.cpp create mode 100644 tests/partial_register/PartialRegisterRepresentation.tests.cpp create mode 100644 tests/partial_register_odr/fixture.h create mode 100644 tests/partial_register_odr/main.cpp create mode 100644 tests/partial_register_odr/second_translation_unit.cpp create mode 100644 tools/Run-WindowsClang20Container.ps1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16658a5..2fb683f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,22 +38,74 @@ jobs: out/pipeline/provenance if-no-files-found: error - native-clangcl: - name: clang-cl and Clang coverage x64 validation + windows-clang20-container: + name: Windows clang-cl 20 container validation runs-on: windows-2022 steps: - uses: actions/checkout@v4 - - name: Build every Clang validation cell + - name: Build the Windows clang-cl 20 container and validation cell + run: tools/Run-WindowsClang20Container.ps1 -Action Build + - name: Test the exact clang-cl 20 build receipt + run: tools/Run-WindowsClang20Container.ps1 -Action Test -SkipImageBuild + - name: Build clang-cl 20 benchmark artifacts explicitly + run: tools/Run-WindowsClang20Container.ps1 -Action BuildBenchmarks -SkipImageBuild + - name: Upload clang-cl 20 evidence + if: always() + uses: actions/upload-artifact@v4 + with: + name: clang20-evidence + path: | + out/pipeline/windows-clangcl/**/provenance + out/pipeline/windows-clangcl/**/reports + out/pipeline/windows-clangcl/**/validation-build.manifest + out/pipeline/windows-clangcl/**/benchmark-build.manifest + out/pipeline/windows-clangcl/**/build/register-codegen/**/*.json + out/pipeline/windows-clangcl/**/build/register-codegen/**/*.txt + out/pipeline/windows-clangcl/**/build/partial-register-codegen/**/*.json + out/pipeline/windows-clangcl/**/build/partial-register-codegen/**/*.txt + out/pipeline/windows-clangcl/**/build/method-flags-codegen/**/*.json + out/pipeline/windows-clangcl/**/build/method-flags-codegen/**/*.txt + out/pipeline/logs + out/pipeline/provenance + if-no-files-found: error + + native-clang22: + name: clang-cl 22 and Clang 22 coverage x64 validation + runs-on: windows-2022 + steps: + - uses: actions/checkout@v4 + - name: Install the pinned Windows toolchain with Chocolatey + shell: pwsh + run: | + choco upgrade cmake --version=4.4.0 --yes --allow-downgrade --no-progress --limit-output + if ($LASTEXITCODE -notin @(0, 1605, 1614, 1641, 3010)) { + throw "Chocolatey failed to install CMake 4.4.0 with exit code $LASTEXITCODE" + } + choco upgrade llvm --version=22.1.7 --yes --allow-downgrade --no-progress --limit-output + if ($LASTEXITCODE -notin @(0, 1605, 1614, 1641, 3010)) { + throw "Chocolatey failed to install LLVM 22.1.7 with exit code $LASTEXITCODE" + } + 'C:\Program Files\CMake\bin' | Out-File -Encoding utf8 -Append $env:GITHUB_PATH + 'C:\Program Files\LLVM\bin' | Out-File -Encoding utf8 -Append $env:GITHUB_PATH + - name: Verify the pinned Windows toolchain + run: | + $cmakeVersion = & 'C:\Program Files\CMake\bin\cmake.exe' --version | Select-Object -First 1 + if ($cmakeVersion -ne 'cmake version 4.4.0') { throw "Unexpected CMake version: $cmakeVersion" } + $version = & 'C:\Program Files\LLVM\bin\clang-cl.exe' --version | Select-Object -First 1 + if ($version -notmatch 'clang version 22\.1\.7\b') { throw "Unexpected clang-cl version: $version" } + $cmakeVersion + $version + - name: Build every Clang 22 validation cell run: tools/Build.ps1 -Scope Native -Compiler ClangCl,ClangCoverage - - name: Test the exact Clang build receipt + - name: Test the exact Clang 22 build receipt run: tools/Run-Tests.ps1 -Scope Native -Compiler ClangCl,ClangCoverage - - name: Build clang-cl benchmark artifacts explicitly + - name: Build clang-cl 22 benchmark artifacts explicitly run: tools/Build-Benchmarks.ps1 -Scope Native -Compiler ClangCl - - name: Upload Clang evidence + - name: Upload Clang 22 evidence if: always() uses: actions/upload-artifact@v4 with: - name: clang-evidence + name: clang22-evidence path: | out/pipeline/windows-clangcl/**/provenance out/pipeline/windows-clangcl/**/reports @@ -61,6 +113,8 @@ jobs: out/pipeline/windows-clangcl/**/benchmark-build.manifest out/pipeline/windows-clangcl/**/build/register-codegen/**/*.json out/pipeline/windows-clangcl/**/build/register-codegen/**/*.txt + out/pipeline/windows-clangcl/**/build/partial-register-codegen/**/*.json + out/pipeline/windows-clangcl/**/build/partial-register-codegen/**/*.txt out/pipeline/windows-clangcl/**/build/method-flags-codegen/**/*.json out/pipeline/windows-clangcl/**/build/method-flags-codegen/**/*.txt out/pipeline/windows-clang-coverage/**/provenance diff --git a/CMakeLists.txt b/CMakeLists.txt index bb8074e..f6f7d63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,11 +2,15 @@ cmake_minimum_required(VERSION 3.31) project(SimdLib VERSION 0.2.0 LANGUAGES CXX) +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + add_library(SimdLib INTERFACE) add_library(SimdLib::SimdLib ALIAS SimdLib) target_compile_features(SimdLib INTERFACE cxx_std_20) target_include_directories(SimdLib INTERFACE - $) + $ + $) target_sources(SimdLib INTERFACE $) @@ -40,6 +44,27 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" endif() set_property(TARGET SimdLibRegister PROPERTY SIMDLIB_REGISTER_COMPILER_SUPPORTED ${SIMDLIB_REGISTER_COMPILER_SUPPORTED}) +set_property(TARGET SimdLibRegister PROPERTY EXPORT_NAME Register) + +install(TARGETS SimdLib SimdLibRegister EXPORT SimdLibTargets) +install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(EXPORT SimdLibTargets + FILE SimdLibTargets.cmake + NAMESPACE SimdLib:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SimdLib) + +configure_package_config_file( + cmake/SimdLibConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/SimdLibConfig.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SimdLib) +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/SimdLibConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion) +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/SimdLibConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/SimdLibConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SimdLib) if(PROJECT_IS_TOP_LEVEL) include(cmake/development/Development.cmake) diff --git a/README.md b/README.md index e329524..c97eaa8 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ SimdLib is a small, header-only library for working with SIMD data and bit-heavy code without scattering compiler intrinsics throughout your project. Its core surface remains C++20; supporting C++23 translation units can additionally use -the complete-register value interface. +complete- and partial-register value interfaces. There is no library binary to build or ship. Add the headers to your project, link the CMake interface target, and use only the pieces you need. @@ -16,6 +16,11 @@ link the CMake interface target, and use only the pieces you need. stable storage and ABI contracts. - `RegisterMask` preserves native comparison predicates and provides composition, reduction, observation, and selection operations. +- `PartialRegister` represents a compile-time contiguous + low-lane prefix in one native register and keeps its inactive suffix + all-bits-zero. +- `PartialRegisterMask` is the matching active-prefix + predicate type. - `NativeApi` and `Api` remain supported for C++20, compatibility, specialized low-level access, collection helpers, and operations intentionally excluded from `Register`. @@ -306,6 +311,12 @@ fixtures retain normal `/GS` protection and paired disassembly for review. configuration details, formatting, and development commands. - [Public namespace and compatibility](docs/PublicNamespace.md) describes the supported API boundary. +- [Register contract and qualification](docs/RegisterContract.md) records the + complete-register semantics; [RegisterQualification.md](docs/RegisterQualification.md) + records its compiler, ABI, generated-code, and validation boundaries. +- [PartialRegister design and qualification](docs/PartialRegisterQualification.md) + records the active-prefix contract, result rules, examples, compiler matrix, + and validation evidence. - [Build and validation](docs/BuildPipeline.md) documents the supported build, test, compiler-matrix, and reporting commands. diff --git a/benchmarks/Register.benchmarks.cpp b/benchmarks/Register.benchmarks.cpp index fad5655..e7fdd05 100644 --- a/benchmarks/Register.benchmarks.cpp +++ b/benchmarks/Register.benchmarks.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -58,6 +59,55 @@ template [[nodiscard]] std::array make } // namespace +TEST_CASE("PartialRegister invariant-maintenance benchmarks", "[simdlib][benchmark][partial-register]") +{ + using partial128 = SimdLib::PartialRegister; + using partial256 = SimdLib::PartialRegister; + using integer_partial256 = SimdLib::PartialRegister; + auto seed = runtime_seed(); + const auto lhs128_lanes = make_float_lanes(seed); + const auto rhs128_lanes = make_float_lanes(seed); + const auto lhs256_lanes = make_float_lanes(seed); + const auto rhs256_lanes = make_float_lanes(seed); + const auto integer_lhs_lanes = make_unsigned_lanes(seed); + const auto integer_rhs_lanes = make_unsigned_lanes(seed); + const auto lhs128 = partial128::load(std::span{lhs128_lanes}); + const auto rhs128 = partial128::load(std::span{rhs128_lanes}); + const auto lhs256 = partial256::load(std::span{lhs256_lanes}); + const auto rhs256 = partial256::load(std::span{rhs256_lanes}); + const auto integer_lhs = integer_partial256::load(std::span{integer_lhs_lanes}); + const auto integer_rhs = integer_partial256::load(std::span{integer_rhs_lanes}); + + BENCHMARK("PartialRegister 128-bit three-lane add") + { + return (lhs128 + rhs128).native; + }; + BENCHMARK("Raw Api 128-bit canonical three-lane add") + { + return partial128::api_type::add(lhs128.native, rhs128.native); + }; + BENCHMARK("PartialRegister 256-bit five-lane add") + { + return (lhs256 + rhs256).native; + }; + BENCHMARK("Raw Api 256-bit canonical five-lane add") + { + return partial256::api_type::add(lhs256.native, rhs256.native); + }; + BENCHMARK("PartialRegister 256-bit five-lane division") + { + return (integer_lhs / integer_rhs).native; + }; + BENCHMARK("PartialRegister 256-bit five-lane compare and select") + { + return lhs256.compare_greater(rhs256).select(lhs256, rhs256).native; + }; + BENCHMARK("PartialRegister 256-bit five-lane shuffle") + { + return lhs256.template shuffle<4, 3, 2, 1, 0>().native; + }; +} + TEST_CASE("Register runtime-derived wrapper and raw benchmarks", "[simdlib][benchmark][register]") { using register128 = SimdLib::Register; diff --git a/cmake/CheckPublicConsumerBoundary.cmake b/cmake/CheckPublicConsumerBoundary.cmake index 0d49f5b..6f48d7b 100644 --- a/cmake/CheckPublicConsumerBoundary.cmake +++ b/cmake/CheckPublicConsumerBoundary.cmake @@ -9,6 +9,8 @@ file(GLOB_RECURSE public_consumer_sources "${SOURCE_DIRECTORY}/tests/consumer/*.cpp" "${SOURCE_DIRECTORY}/tests/format_odr/*.cpp" "${SOURCE_DIRECTORY}/tests/headers/*.cpp" + "${SOURCE_DIRECTORY}/tests/installed_consumer/*.cpp" + "${SOURCE_DIRECTORY}/tests/partial_register_odr/*.cpp" "${SOURCE_DIRECTORY}/tests/register_odr/*.cpp" "${SOURCE_DIRECTORY}/tests/smoke/*.cpp") list(SORT public_consumer_sources) diff --git a/cmake/CompareRegisterCodegen.cmake b/cmake/CompareRegisterCodegen.cmake index 20fff37..76b8ce3 100644 --- a/cmake/CompareRegisterCodegen.cmake +++ b/cmake/CompareRegisterCodegen.cmake @@ -28,6 +28,12 @@ endif() if(NOT DEFINED RECORDED_DIFFERENCE_REASON OR "${RECORDED_DIFFERENCE_REASON}" STREQUAL "") set(RECORDED_DIFFERENCE_REASON "non-release-differential") endif() +if(NOT DEFINED EXPECTED_WRAPPER_PROFILE_SHA256) + set(EXPECTED_WRAPPER_PROFILE_SHA256 "") +endif() +if(NOT DEFINED EXPECTED_RAW_PROFILE_SHA256) + set(EXPECTED_RAW_PROFILE_SHA256 "") +endif() if(NOT DEFINED RECORD_FILE OR "${RECORD_FILE}" STREQUAL "") set(RECORD_FILE "${ARTIFACT_DIRECTORY}/comparison.record.json") endif() @@ -132,16 +138,20 @@ endfunction() # @brief Removes object identity, instruction addresses, and encoded bytes while retaining instructions. # @param input_text Raw object disassembly. # @param output_variable Variable that receives normalized disassembly. +# @param symbol_variable Optional variable that receives fixture symbols in disassembly order. function(simdlib_normalize_disassembly input_text output_variable) set(normalized "${input_text}") string(REPLACE "\r\n" "\n" normalized "${normalized}") string(REPLACE "\n" ";" disassembly_lines "${normalized}") set(fixture_only "") + set(fixture_symbols "") set(in_fixture OFF) foreach(disassembly_line IN LISTS disassembly_lines) if(disassembly_line MATCHES "<[^>]*${SYMBOL_PATTERN}[^>]*>:") if(EXCLUDE_SYMBOL_PATTERN STREQUAL "" OR NOT disassembly_line MATCHES "<[^>]*${EXCLUDE_SYMBOL_PATTERN}[^>]*>:") set(in_fixture ON) + string(REGEX REPLACE ".*<([^>]*)>:.*" "\\1" fixture_symbol "${disassembly_line}") + list(APPEND fixture_symbols "${fixture_symbol}") string(APPEND fixture_only ":\n") else() set(in_fixture OFF) @@ -165,6 +175,9 @@ function(simdlib_normalize_disassembly input_text output_variable) string(REGEX REPLACE "\n+" "\n" normalized "${normalized}") string(STRIP "${normalized}" normalized) set(${output_variable} "${normalized}" PARENT_SCOPE) + if(ARGC GREATER 2) + set(${ARGV2} "${fixture_symbols}" PARENT_SCOPE) + endif() endfunction() # @brief Removes allocator-selected vector-register identities, including names repeated in disassembler comments. @@ -309,8 +322,8 @@ endfunction() simdlib_disassemble("${WRAPPER_OBJECT}" wrapper_disassembly) simdlib_disassemble("${RAW_OBJECT}" raw_disassembly) -simdlib_normalize_disassembly("${wrapper_disassembly}" wrapper_normalized) -simdlib_normalize_disassembly("${raw_disassembly}" raw_normalized) +simdlib_normalize_disassembly("${wrapper_disassembly}" wrapper_normalized wrapper_symbols) +simdlib_normalize_disassembly("${raw_disassembly}" raw_normalized raw_symbols) simdlib_profile_disassembly("${wrapper_normalized}" wrapper_profile) simdlib_profile_disassembly("${raw_normalized}" raw_profile) @@ -367,9 +380,61 @@ file(WRITE "${ARTIFACT_DIRECTORY}/raw.normalized.txt" "${raw_normalized}\n") file(WRITE "${ARTIFACT_DIRECTORY}/wrapper.profile.txt" "${wrapper_profile}\n") file(WRITE "${ARTIFACT_DIRECTORY}/raw.profile.txt" "${raw_profile}\n") file(WRITE "${ARTIFACT_DIRECTORY}/wrapper.comparable.profile.txt" "${comparable_wrapper_profile}\n") +string(REPLACE ":\n" ";" wrapper_profile_blocks "${wrapper_profile}") +string(REPLACE ":\n" ";" raw_profile_blocks "${raw_profile}") +list(POP_FRONT wrapper_profile_blocks) +list(POP_FRONT raw_profile_blocks) +list(LENGTH wrapper_symbols wrapper_symbol_count) +list(LENGTH raw_symbols raw_symbol_count) +list(LENGTH wrapper_profile_blocks wrapper_block_count) +list(LENGTH raw_profile_blocks raw_block_count) +if(NOT wrapper_symbol_count EQUAL raw_symbol_count OR + NOT wrapper_symbol_count EQUAL wrapper_block_count OR + NOT raw_symbol_count EQUAL raw_block_count) + message(FATAL_ERROR "Unable to map generated-code profile blocks to exact fixture symbols") +endif() +set(symbol_inventory "") +set(instruction_differences "") +if(wrapper_symbol_count GREATER 0) + math(EXPR last_symbol_index "${wrapper_symbol_count} - 1") + foreach(symbol_index RANGE 0 ${last_symbol_index}) + list(GET wrapper_symbols ${symbol_index} wrapper_symbol) + list(GET raw_symbols ${symbol_index} raw_symbol) + list(GET wrapper_profile_blocks ${symbol_index} wrapper_block) + list(GET raw_profile_blocks ${symbol_index} raw_block) + string(APPEND symbol_inventory + "wrapper=${wrapper_symbol}\nraw=${raw_symbol}\n") + if(NOT wrapper_block STREQUAL raw_block) + string(APPEND instruction_differences + "wrapper_symbol=${wrapper_symbol}\nraw_symbol=${raw_symbol}\n" + "wrapper:\n${wrapper_block}\nraw:\n${raw_block}\n") + endif() + endforeach() +endif() +file(WRITE "${ARTIFACT_DIRECTORY}/symbols.txt" "${symbol_inventory}") +file(WRITE "${ARTIFACT_DIRECTORY}/instruction-differences.txt" "${instruction_differences}") +file(SHA256 "${ARTIFACT_DIRECTORY}/wrapper.profile.txt" wrapper_profile_hash) +file(SHA256 "${ARTIFACT_DIRECTORY}/raw.profile.txt" raw_profile_hash) + +if(comparison_result STREQUAL "failed" AND + NOT EXPECTED_WRAPPER_PROFILE_SHA256 STREQUAL "" AND + NOT EXPECTED_RAW_PROFILE_SHA256 STREQUAL "") + if(NOT wrapper_profile_hash STREQUAL EXPECTED_WRAPPER_PROFILE_SHA256 OR + NOT raw_profile_hash STREQUAL EXPECTED_RAW_PROFILE_SHA256) + message(FATAL_ERROR + "The retained generated-code difference changed; " + "wrapper expected=${EXPECTED_WRAPPER_PROFILE_SHA256} actual=${wrapper_profile_hash}; " + "raw expected=${EXPECTED_RAW_PROFILE_SHA256} actual=${raw_profile_hash}; " + "inspect ${ARTIFACT_DIRECTORY}") + endif() + set(comparison_result "accepted-compiler-exception") + set(accepted_exception "${RECORDED_DIFFERENCE_REASON}") +endif() file(WRITE "${ARTIFACT_DIRECTORY}/comparison.txt" "result=${comparison_result}\n" - "accepted_exception=${accepted_exception}\n") + "accepted_exception=${accepted_exception}\n" + "wrapper_profile_sha256=${wrapper_profile_hash}\n" + "raw_profile_sha256=${raw_profile_hash}\n") file(WRITE "${ARTIFACT_DIRECTORY}/provenance.txt" "compiler_id=${COMPILER_ID}\n" "compiler_version=${COMPILER_VERSION}\n" @@ -387,6 +452,10 @@ file(WRITE "${ARTIFACT_DIRECTORY}/provenance.txt" "record_only=${RECORD_ONLY}\n" "comparison_result=${comparison_result}\n" "accepted_exception=${accepted_exception}\n" + "wrapper_profile_sha256=${wrapper_profile_hash}\n" + "raw_profile_sha256=${raw_profile_hash}\n" + "expected_wrapper_profile_sha256=${EXPECTED_WRAPPER_PROFILE_SHA256}\n" + "expected_raw_profile_sha256=${EXPECTED_RAW_PROFILE_SHA256}\n" "wrapper_object=${WRAPPER_OBJECT}\n" "raw_object=${RAW_OBJECT}\n") @@ -428,6 +497,7 @@ file(WRITE "${record_temporary_file}" " \"kind\": \"comparison\",\n" " \"result\": \"${comparison_result_json}\",\n" " \"accepted_exception\": \"${accepted_exception_json}\",\n" + " \"profiles\": {\"wrapper_sha256\": \"${wrapper_profile_hash}\", \"raw_sha256\": \"${raw_profile_hash}\"},\n" " \"inputs\": {\n" " \"wrapper\": {\"path\": \"${WRAPPER_OBJECT_json}\", \"sha256\": \"${wrapper_hash}\"},\n" " \"raw\": {\"path\": \"${RAW_OBJECT_json}\", \"sha256\": \"${raw_hash}\"}\n" diff --git a/cmake/RunInstalledPartialRegisterConsumer.cmake b/cmake/RunInstalledPartialRegisterConsumer.cmake new file mode 100644 index 0000000..085d575 --- /dev/null +++ b/cmake/RunInstalledPartialRegisterConsumer.cmake @@ -0,0 +1,47 @@ +if(NOT DEFINED SIMDLIB_BUILD_DIR OR NOT DEFINED SIMDLIB_SOURCE_DIR OR + NOT DEFINED SIMDLIB_CONSUMER_SOURCE_DIR OR NOT DEFINED SIMDLIB_CONSUMER_BINARY_DIR OR + NOT DEFINED SIMDLIB_INSTALL_PREFIX OR NOT DEFINED SIMDLIB_CMAKE_COMMAND OR + NOT DEFINED SIMDLIB_GENERATOR OR NOT DEFINED SIMDLIB_CXX_COMPILER) + message(FATAL_ERROR "Installed PartialRegister consumer runner is missing a required input") +endif() + +execute_process( + COMMAND "${SIMDLIB_CMAKE_COMMAND}" --install "${SIMDLIB_BUILD_DIR}" + --prefix "${SIMDLIB_INSTALL_PREFIX}" --config "${SIMDLIB_CONFIG}" + RESULT_VARIABLE install_result) +if(NOT install_result EQUAL 0) + message(FATAL_ERROR "Installing SimdLib for the downstream consumer failed: ${install_result}") +endif() + +set(configure_command + "${SIMDLIB_CMAKE_COMMAND}" + -S "${SIMDLIB_CONSUMER_SOURCE_DIR}" + -B "${SIMDLIB_CONSUMER_BINARY_DIR}" + -G "${SIMDLIB_GENERATOR}" + "-DCMAKE_CXX_COMPILER=${SIMDLIB_CXX_COMPILER}" + "-DCMAKE_CXX_FLAGS=${SIMDLIB_CXX_FLAGS}" + "-DCMAKE_EXE_LINKER_FLAGS=${SIMDLIB_EXE_LINKER_FLAGS}" + "-DCMAKE_CXX_SCAN_FOR_MODULES=${SIMDLIB_CXX_SCAN_FOR_MODULES}" + "-DCMAKE_PREFIX_PATH=${SIMDLIB_INSTALL_PREFIX}" + "-DSIMDLIB_FORBIDDEN_INCLUDE_DIR=${SIMDLIB_SOURCE_DIR}/include") +if(DEFINED SIMDLIB_GENERATOR_PLATFORM AND NOT SIMDLIB_GENERATOR_PLATFORM STREQUAL "") + list(APPEND configure_command -A "${SIMDLIB_GENERATOR_PLATFORM}") +endif() +if(DEFINED SIMDLIB_GENERATOR_TOOLSET AND NOT SIMDLIB_GENERATOR_TOOLSET STREQUAL "") + list(APPEND configure_command -T "${SIMDLIB_GENERATOR_TOOLSET}") +endif() +if(DEFINED SIMDLIB_MAKE_PROGRAM AND NOT SIMDLIB_MAKE_PROGRAM STREQUAL "") + list(APPEND configure_command "-DCMAKE_MAKE_PROGRAM=${SIMDLIB_MAKE_PROGRAM}") +endif() +execute_process(COMMAND ${configure_command} RESULT_VARIABLE configure_result) +if(NOT configure_result EQUAL 0) + message(FATAL_ERROR "Configuring the installed SimdLib consumer failed: ${configure_result}") +endif() + +execute_process( + COMMAND "${SIMDLIB_CMAKE_COMMAND}" --build "${SIMDLIB_CONSUMER_BINARY_DIR}" + --config "${SIMDLIB_CONFIG}" + RESULT_VARIABLE build_result) +if(NOT build_result EQUAL 0) + message(FATAL_ERROR "Building the installed SimdLib consumer failed: ${build_result}") +endif() diff --git a/cmake/SimdLibConfig.cmake.in b/cmake/SimdLibConfig.cmake.in new file mode 100644 index 0000000..6c6ad1d --- /dev/null +++ b/cmake/SimdLibConfig.cmake.in @@ -0,0 +1,3 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/SimdLibTargets.cmake") diff --git a/cmake/ValidateCodegenRecords.cmake b/cmake/ValidateCodegenRecords.cmake index 4daa240..66664aa 100644 --- a/cmake/ValidateCodegenRecords.cmake +++ b/cmake/ValidateCodegenRecords.cmake @@ -1,9 +1,10 @@ cmake_minimum_required(VERSION 3.31) -if(NOT DEFINED RECORD_INDEX OR "${RECORD_INDEX}" STREQUAL "") - message(FATAL_ERROR "ValidateCodegenRecords requires RECORD_INDEX") +if((NOT DEFINED RECORD_INDEX OR "${RECORD_INDEX}" STREQUAL "") AND + (NOT DEFINED RECORD_FILE OR "${RECORD_FILE}" STREQUAL "")) + message(FATAL_ERROR "ValidateCodegenRecords requires RECORD_INDEX or RECORD_FILE") endif() -if(NOT EXISTS "${RECORD_INDEX}") +if(DEFINED RECORD_INDEX AND NOT "${RECORD_INDEX}" STREQUAL "" AND NOT EXISTS "${RECORD_INDEX}") message(FATAL_ERROR "Required generated-code record index is missing: ${RECORD_INDEX}") endif() @@ -64,6 +65,11 @@ function(simdlib_validate_codegen_record record_file) endif() endfunction() +if(DEFINED RECORD_FILE AND NOT "${RECORD_FILE}" STREQUAL "") + simdlib_validate_codegen_record("${RECORD_FILE}") + return() +endif() + file(STRINGS "${RECORD_INDEX}" record_files) set(validated_record_count 0) foreach(record_file IN LISTS record_files) diff --git a/cmake/VerifyPublicConsumptionProfile.cmake b/cmake/VerifyPublicConsumptionProfile.cmake index 21ae460..9429da1 100644 --- a/cmake/VerifyPublicConsumptionProfile.cmake +++ b/cmake/VerifyPublicConsumptionProfile.cmake @@ -21,6 +21,9 @@ set(expected_smoke_targets set(expected_consumer_targets CoreConsumerSmoke) if(REGISTER_SUPPORTED) list(APPEND expected_smoke_targets + InstalledPackagePartialRegisterConsumerArtifacts + PartialRegisterOdrAvx2 + PartialRegisterOdrSse42 RegisterExamples RegisterOdr) list(APPEND expected_consumer_targets RegisterConsumerSmoke) diff --git a/cmake/development/ConfigurationProbes.cmake b/cmake/development/ConfigurationProbes.cmake index e614610..cfa9d22 100644 --- a/cmake/development/ConfigurationProbes.cmake +++ b/cmake/development/ConfigurationProbes.cmake @@ -144,6 +144,27 @@ if(SIMDLIB_BUILD_CONFIGURATION_PROBES) ${CMAKE_CURRENT_SOURCE_DIR}/tests/config/MethodFlagsConfigUnsupportedTargetProbe.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/RegisterHeaderCxx20.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/RegisterRequirementCxx20.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterHeaderCxx20.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterRequirementCxx20.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterMaskInvalidActiveCount.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterZeroActiveCount.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterExcessiveActiveCount.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterInactiveUpperHalf.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterOversizedLaneList.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterFullTransferExtent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterInactiveLaneIndex.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterImplicitScalar.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterImplicitNative.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterUnavailableWidth.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterNegativeByteShiftLeft.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterNegativeByteShiftRight.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterNegativeBitShiftLeft.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterNegativeBitShiftRight.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterInvalidShuffleSelector.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterInvalidShuffleSelectorCount.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterInvalidByteShuffleSelector.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterFractionalBitCast.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/PartialRegisterInactiveWidenResult.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/RegisterAvailabilityOverride.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/RegisterUnsupportedCompiler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests/compile_fail/register/RegisterPartialLaneList.cpp @@ -197,11 +218,76 @@ if(SIMDLIB_BUILD_CONFIGURATION_PROBES) simdlib_add_language_probe(RegisterEnabledProbe tests/availability/RegisterEnabledProbe.cpp 23 SimdLib::Register) - simdlib_add_language_probe(RegisterComparisonChainingProbe tests/availability/RegisterComparisonChainingProbe.cpp 23 SimdLib::Register) simdlib_enable_register_sse42(RegisterComparisonChainingProbe) + simdlib_add_language_probe(PartialRegisterEnabledProbe + tests/availability/PartialRegisterEnabledProbe.cpp 23 SimdLib::Register) + simdlib_enable_register_sse42(PartialRegisterEnabledProbe) + simdlib_add_language_probe(PartialRegisterAvx2EnabledProbe + tests/availability/PartialRegisterEnabledProbe.cpp 23 SimdLib::Register) + target_compile_definitions(PartialRegisterAvx2EnabledProbe PRIVATE + SIMDLIB_PARTIAL_REGISTER_AVAILABILITY_BITS=256) + simdlib_enable_register_avx2(PartialRegisterAvx2EnabledProbe) + simdlib_expect_language_probe_failure(PartialRegisterMaskInvalidActiveCountFailure + tests/compile_fail/register/PartialRegisterMaskInvalidActiveCount.cpp 23 + constraints) + simdlib_expect_language_probe_failure(PartialRegisterZeroActiveCountFailure + tests/compile_fail/register/PartialRegisterZeroActiveCount.cpp 23 + constraints) + simdlib_expect_language_probe_failure(PartialRegisterExcessiveActiveCountFailure + tests/compile_fail/register/PartialRegisterExcessiveActiveCount.cpp 23 + constraints) + simdlib_expect_language_probe_failure(PartialRegisterInactiveUpperHalfFailure + tests/compile_fail/register/PartialRegisterInactiveUpperHalf.cpp 23 + SIMDLIB_PARTIAL_REGISTER_REJECTS_INACTIVE_UPPER_HALF) + simdlib_expect_language_probe_failure(PartialRegisterOversizedLaneListFailure + tests/compile_fail/register/PartialRegisterOversizedLaneList.cpp 23 + SIMDLIB_PARTIAL_REGISTER_REJECTS_OVERSIZED_LANE_LIST) + simdlib_expect_language_probe_failure(PartialRegisterFullTransferExtentFailure + tests/compile_fail/register/PartialRegisterFullTransferExtent.cpp 23 + SIMDLIB_PARTIAL_REGISTER_REJECTS_FULL_TRANSFER_EXTENT) + simdlib_expect_language_probe_failure(PartialRegisterInactiveLaneIndexFailure + tests/compile_fail/register/PartialRegisterInactiveLaneIndex.cpp 23 + SIMDLIB_PARTIAL_REGISTER_REJECTS_INACTIVE_LANE_INDEX) + simdlib_expect_language_probe_failure(PartialRegisterImplicitScalarFailure + tests/compile_fail/register/PartialRegisterImplicitScalar.cpp 23 + SIMDLIB_PARTIAL_REGISTER_REJECTS_IMPLICIT_SCALAR) + simdlib_expect_language_probe_failure(PartialRegisterImplicitNativeFailure + tests/compile_fail/register/PartialRegisterImplicitNative.cpp 23 + SIMDLIB_PARTIAL_REGISTER_REJECTS_IMPLICIT_NATIVE) + simdlib_expect_language_probe_failure(PartialRegisterUnavailableWidthFailure + tests/compile_fail/register/PartialRegisterUnavailableWidth.cpp 23 + constraints) + simdlib_expect_language_probe_failure(PartialRegisterNegativeByteShiftLeftFailure + tests/compile_fail/register/PartialRegisterNegativeByteShiftLeft.cpp 23 + shift_bytes_left) + simdlib_expect_language_probe_failure(PartialRegisterNegativeByteShiftRightFailure + tests/compile_fail/register/PartialRegisterNegativeByteShiftRight.cpp 23 + shift_bytes_right) + simdlib_expect_language_probe_failure(PartialRegisterNegativeBitShiftLeftFailure + tests/compile_fail/register/PartialRegisterNegativeBitShiftLeft.cpp 23 + shift_bits_left) + simdlib_expect_language_probe_failure(PartialRegisterNegativeBitShiftRightFailure + tests/compile_fail/register/PartialRegisterNegativeBitShiftRight.cpp 23 + shift_bits_right) + simdlib_expect_language_probe_failure(PartialRegisterInvalidShuffleSelectorFailure + tests/compile_fail/register/PartialRegisterInvalidShuffleSelector.cpp 23 + SIMDLIB_PARTIAL_REGISTER_REJECTS_INVALID_SHUFFLE_SELECTOR) + simdlib_expect_language_probe_failure(PartialRegisterInvalidShuffleSelectorCountFailure + tests/compile_fail/register/PartialRegisterInvalidShuffleSelectorCount.cpp 23 + SIMDLIB_PARTIAL_REGISTER_REJECTS_INVALID_SHUFFLE_SELECTOR_COUNT) + simdlib_expect_language_probe_failure(PartialRegisterInvalidByteShuffleSelectorFailure + tests/compile_fail/register/PartialRegisterInvalidByteShuffleSelector.cpp 23 + SIMDLIB_PARTIAL_REGISTER_REJECTS_INVALID_BYTE_SHUFFLE_SELECTOR) + simdlib_expect_language_probe_failure(PartialRegisterFractionalBitCastFailure + tests/compile_fail/register/PartialRegisterFractionalBitCast.cpp 23 + SIMDLIB_PARTIAL_REGISTER_REJECTS_FRACTIONAL_BIT_CAST_RESULT) + simdlib_expect_language_probe_failure(PartialRegisterInactiveWidenResultFailure + tests/compile_fail/register/PartialRegisterInactiveWidenResult.cpp 23 + SIMDLIB_PARTIAL_REGISTER_REJECTS_INACTIVE_UPPER_HALF_WIDEN_RESULT) + foreach(register_width IN ITEMS 128 256) add_library(RegisterRepresentation${register_width} OBJECT tests/register/RegisterRepresentation.tests.cpp) @@ -216,6 +302,20 @@ if(SIMDLIB_BUILD_CONFIGURATION_PROBES) else() simdlib_enable_register_avx2(RegisterRepresentation${register_width}) endif() + + add_library(PartialRegisterRepresentation${register_width} OBJECT + tests/partial_register/PartialRegisterRepresentation.tests.cpp) + simdlib_register_development_target( + PartialRegisterRepresentation${register_width} COMPILER_CONTRACT) + target_link_libraries(PartialRegisterRepresentation${register_width} PRIVATE SimdLib::Register) + target_compile_definitions(PartialRegisterRepresentation${register_width} PRIVATE + SIMDLIB_REGISTER_TEST_WIDTH=${register_width}) + simdlib_enable_development_warnings(PartialRegisterRepresentation${register_width}) + if(register_width EQUAL 128) + simdlib_enable_register_sse42(PartialRegisterRepresentation${register_width}) + else() + simdlib_enable_register_avx2(PartialRegisterRepresentation${register_width}) + endif() endforeach() simdlib_expect_language_probe_failure(RegisterPartialLaneListFailure @@ -284,6 +384,12 @@ if(SIMDLIB_BUILD_CONFIGURATION_PROBES) simdlib_expect_language_probe_failure(RegisterRequirementCxx20Failure tests/compile_fail/register/RegisterRequirementCxx20.cpp 20 SIMDLIB_REGISTER_INTERFACE_UNAVAILABLE) + simdlib_expect_language_probe_failure(PartialRegisterHeaderCxx20Failure + tests/compile_fail/register/PartialRegisterHeaderCxx20.cpp 20 + SIMDLIB_PARTIAL_REGISTER_HEADER_REQUIRES_CXX23) + simdlib_expect_language_probe_failure(PartialRegisterRequirementCxx20Failure + tests/compile_fail/register/PartialRegisterRequirementCxx20.cpp 20 + SIMDLIB_REGISTER_INTERFACE_UNAVAILABLE) simdlib_expect_language_probe_failure(RegisterAvailabilityOverrideFailure tests/compile_fail/register/RegisterAvailabilityOverride.cpp 20 SIMDLIB_REGISTER_INTERFACE_AVAILABILITY_IS_COMPUTED) diff --git a/cmake/development/ConstexprProbes.cmake b/cmake/development/ConstexprProbes.cmake index 6eeb9a4..68d2f0e 100644 --- a/cmake/development/ConstexprProbes.cmake +++ b/cmake/development/ConstexprProbes.cmake @@ -105,6 +105,31 @@ if(SIMDLIB_BUILD_CONSTEXPR_PROBES) simdlib_add_constexpr_probe(ApiDisabledConstexprProbe tests/constexpr/ApiDisabledConstexpr.tests.cpp) list(APPEND simdlib_constexpr_targets ApiDisabledConstexprProbe) + if(SIMDLIB_REGISTER_COMPILER_SUPPORTED) + add_library(PartialRegisterMaskConstexprProbe OBJECT tests/constexpr/PartialRegisterMaskConstexpr.tests.cpp) + simdlib_register_development_target(PartialRegisterMaskConstexprProbe CONSTEXPR_CONTRACT) + target_link_libraries(PartialRegisterMaskConstexprProbe PRIVATE SimdLib::Register) + simdlib_enable_development_warnings(PartialRegisterMaskConstexprProbe) + simdlib_enable_register_sse42(PartialRegisterMaskConstexprProbe) + list(APPEND simdlib_constexpr_targets PartialRegisterMaskConstexprProbe) + + add_library(PartialRegisterConstexprProbe OBJECT tests/constexpr/PartialRegisterConstexpr.tests.cpp) + simdlib_register_development_target(PartialRegisterConstexprProbe CONSTEXPR_CONTRACT) + target_link_libraries(PartialRegisterConstexprProbe PRIVATE SimdLib::Register) + simdlib_enable_development_warnings(PartialRegisterConstexprProbe) + simdlib_enable_register_sse42(PartialRegisterConstexprProbe) + list(APPEND simdlib_constexpr_targets PartialRegisterConstexprProbe) + + add_library(PartialRegisterAvx2ConstexprProbe OBJECT tests/constexpr/PartialRegisterConstexpr.tests.cpp) + simdlib_register_development_target(PartialRegisterAvx2ConstexprProbe CONSTEXPR_CONTRACT) + target_link_libraries(PartialRegisterAvx2ConstexprProbe PRIVATE SimdLib::Register) + target_compile_definitions(PartialRegisterAvx2ConstexprProbe PRIVATE + SIMDLIB_PARTIAL_REGISTER_CONSTEXPR_ENABLE_256=1) + simdlib_enable_development_warnings(PartialRegisterAvx2ConstexprProbe) + simdlib_enable_register_avx2(PartialRegisterAvx2ConstexprProbe) + list(APPEND simdlib_constexpr_targets PartialRegisterAvx2ConstexprProbe) + endif() + set(constexpr_object_expressions "") foreach(constexpr_target IN LISTS simdlib_constexpr_targets) list(APPEND constexpr_object_expressions "$") diff --git a/cmake/development/Development.cmake b/cmake/development/Development.cmake index 43995f0..423c0a9 100644 --- a/cmake/development/Development.cmake +++ b/cmake/development/Development.cmake @@ -22,6 +22,7 @@ set(simdlib_development_modules ConstexprProbes HeaderProbes RegisterCodegen + PartialRegisterCodegen SmokeTests RuntimeTests Examples diff --git a/cmake/development/HeaderProbes.cmake b/cmake/development/HeaderProbes.cmake index 302c597..37f0bb2 100644 --- a/cmake/development/HeaderProbes.cmake +++ b/cmake/development/HeaderProbes.cmake @@ -74,6 +74,7 @@ if(SIMDLIB_BUILD_HEADER_PROBES) IImpl IRegister IRegisterMask + IPartialRegisterMask Api SimdApi SimdVector @@ -92,13 +93,29 @@ if(SIMDLIB_BUILD_HEADER_PROBES) endforeach() if(SIMDLIB_REGISTER_COMPILER_SUPPORTED) - add_library(InstalledRegisterHeaderProbe OBJECT - tests/headers/InstalledRegisterHeaderProbe.cpp) + add_library(InstalledRegisterHeaderProbe OBJECT + tests/headers/InstalledRegisterHeaderProbe.cpp) simdlib_configure_installed_header_probe( InstalledRegisterHeaderProbe 23) target_compile_definitions(InstalledRegisterHeaderProbe PRIVATE SIMDLIB_REQUIRE_REGISTER_INTERFACE=1) - simdlib_enable_register_sse42(InstalledRegisterHeaderProbe) + simdlib_enable_register_sse42(InstalledRegisterHeaderProbe) + + add_library(InstalledPartialRegisterHeaderProbe OBJECT + tests/headers/InstalledPartialRegisterHeaderProbe.cpp) + simdlib_configure_installed_header_probe( + InstalledPartialRegisterHeaderProbe 23) + target_compile_definitions(InstalledPartialRegisterHeaderProbe PRIVATE + SIMDLIB_REQUIRE_REGISTER_INTERFACE=1) + simdlib_enable_register_sse42(InstalledPartialRegisterHeaderProbe) + + add_library(InstalledPartialRegisterMaskHeaderProbe OBJECT + tests/headers/InstalledPartialRegisterMaskHeaderProbe.cpp) + simdlib_configure_installed_header_probe( + InstalledPartialRegisterMaskHeaderProbe 23) + target_compile_definitions(InstalledPartialRegisterMaskHeaderProbe PRIVATE + SIMDLIB_REQUIRE_REGISTER_INTERFACE=1) + simdlib_enable_register_sse42(InstalledPartialRegisterMaskHeaderProbe) add_library(HeaderAliasesProbe OBJECT tests/headers/AliasesHeaderProbe.cpp) @@ -113,6 +130,54 @@ if(SIMDLIB_BUILD_HEADER_PROBES) target_link_libraries(HeaderRegisterProbe PRIVATE SimdLib::Register) simdlib_enable_development_warnings(HeaderRegisterProbe) + add_library(HeaderPartialRegisterFwdProbe OBJECT + tests/headers/PartialRegisterFwdHeaderProbe.cpp) + simdlib_register_development_target(HeaderPartialRegisterFwdProbe COMPILER_CONTRACT) + target_link_libraries(HeaderPartialRegisterFwdProbe PRIVATE SimdLib::Register) + simdlib_enable_development_warnings(HeaderPartialRegisterFwdProbe) + simdlib_enable_register_sse42(HeaderPartialRegisterFwdProbe) + + add_library(HeaderPartialRegisterFwdAvx2Probe OBJECT + tests/headers/PartialRegisterFwdHeaderProbe.cpp) + simdlib_register_development_target(HeaderPartialRegisterFwdAvx2Probe COMPILER_CONTRACT) + target_link_libraries(HeaderPartialRegisterFwdAvx2Probe PRIVATE SimdLib::Register) + target_compile_definitions(HeaderPartialRegisterFwdAvx2Probe PRIVATE + SIMDLIB_PARTIAL_REGISTER_HEADER_PROBE_BITS=256) + simdlib_enable_development_warnings(HeaderPartialRegisterFwdAvx2Probe) + simdlib_enable_register_avx2(HeaderPartialRegisterFwdAvx2Probe) + + add_library(HeaderPartialRegisterProbe OBJECT + tests/headers/PartialRegisterHeaderProbe.cpp) + simdlib_register_development_target(HeaderPartialRegisterProbe COMPILER_CONTRACT) + target_link_libraries(HeaderPartialRegisterProbe PRIVATE SimdLib::Register) + simdlib_enable_development_warnings(HeaderPartialRegisterProbe) + simdlib_enable_register_sse42(HeaderPartialRegisterProbe) + + add_library(HeaderPartialRegisterAvx2Probe OBJECT + tests/headers/PartialRegisterHeaderProbe.cpp) + simdlib_register_development_target(HeaderPartialRegisterAvx2Probe COMPILER_CONTRACT) + target_link_libraries(HeaderPartialRegisterAvx2Probe PRIVATE SimdLib::Register) + target_compile_definitions(HeaderPartialRegisterAvx2Probe PRIVATE + SIMDLIB_PARTIAL_REGISTER_HEADER_PROBE_BITS=256) + simdlib_enable_development_warnings(HeaderPartialRegisterAvx2Probe) + simdlib_enable_register_avx2(HeaderPartialRegisterAvx2Probe) + + add_library(HeaderPartialRegisterMaskProbe OBJECT + tests/headers/PartialRegisterMaskHeaderProbe.cpp) + simdlib_register_development_target(HeaderPartialRegisterMaskProbe COMPILER_CONTRACT) + target_link_libraries(HeaderPartialRegisterMaskProbe PRIVATE SimdLib::Register) + simdlib_enable_development_warnings(HeaderPartialRegisterMaskProbe) + simdlib_enable_register_sse42(HeaderPartialRegisterMaskProbe) + + add_library(HeaderPartialRegisterMaskAvx2Probe OBJECT + tests/headers/PartialRegisterMaskHeaderProbe.cpp) + simdlib_register_development_target(HeaderPartialRegisterMaskAvx2Probe COMPILER_CONTRACT) + target_link_libraries(HeaderPartialRegisterMaskAvx2Probe PRIVATE SimdLib::Register) + target_compile_definitions(HeaderPartialRegisterMaskAvx2Probe PRIVATE + SIMDLIB_PARTIAL_REGISTER_HEADER_PROBE_BITS=256) + simdlib_enable_development_warnings(HeaderPartialRegisterMaskAvx2Probe) + simdlib_enable_register_avx2(HeaderPartialRegisterMaskAvx2Probe) + add_library(HeaderRegisterMaskProbe OBJECT tests/headers/RegisterMaskHeaderProbe.cpp) simdlib_register_development_target(HeaderRegisterMaskProbe COMPILER_CONTRACT) diff --git a/cmake/development/PartialRegisterCodegen.cmake b/cmake/development/PartialRegisterCodegen.cmake new file mode 100644 index 0000000..29b281f --- /dev/null +++ b/cmake/development/PartialRegisterCodegen.cmake @@ -0,0 +1,637 @@ +include_guard(GLOBAL) + +if(NOT PROJECT_IS_TOP_LEVEL) + message(FATAL_ERROR "PartialRegisterCodegen.cmake is available only to top-level SimdLib builds") +endif() + +block(SCOPE_FOR VARIABLES) + +set(simdlib_partial_codegen_profiles_file + "${CMAKE_CURRENT_LIST_DIR}/PartialRegisterCodegenProfiles.json") +if(NOT EXISTS "${simdlib_partial_codegen_profiles_file}") + message(FATAL_ERROR + "PartialRegister retained code-generation profiles are missing: " + "${simdlib_partial_codegen_profiles_file}") +endif() +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS + "${simdlib_partial_codegen_profiles_file}") +file(READ "${simdlib_partial_codegen_profiles_file}" + simdlib_partial_codegen_profiles_json) + +# @brief Reads one required property from a retained code-generation profile. +# @param output_variable Variable receiving the decoded property value. +# @param entry_index Zero-based profile entry index. +# @param member_name Required JSON member name. +# @param expected_type Required JSON value type. +function(simdlib_partial_codegen_profile_property output_variable entry_index member_name expected_type) + string(JSON actual_type ERROR_VARIABLE property_error TYPE + "${simdlib_partial_codegen_profiles_json}" + entries ${entry_index} "${member_name}") + if(property_error) + message(FATAL_ERROR + "PartialRegister code-generation profile ${entry_index} does not define " + "${member_name}: ${property_error}") + endif() + if(NOT actual_type STREQUAL expected_type) + message(FATAL_ERROR + "PartialRegister code-generation profile ${entry_index} member " + "${member_name} must be ${expected_type}, not ${actual_type}") + endif() + string(JSON property_value GET "${simdlib_partial_codegen_profiles_json}" + entries ${entry_index} "${member_name}") + set(${output_variable} "${property_value}" PARENT_SCOPE) +endfunction() + +string(JSON simdlib_partial_codegen_root_member_count + ERROR_VARIABLE simdlib_partial_codegen_root_error LENGTH + "${simdlib_partial_codegen_profiles_json}") +if(simdlib_partial_codegen_root_error OR + NOT simdlib_partial_codegen_root_member_count EQUAL 2) + message(FATAL_ERROR + "PartialRegister code-generation profile root must contain exactly " + "schemaVersion and entries: ${simdlib_partial_codegen_root_error}") +endif() +string(JSON simdlib_partial_codegen_schema_type + ERROR_VARIABLE simdlib_partial_codegen_schema_error TYPE + "${simdlib_partial_codegen_profiles_json}" schemaVersion) +string(JSON simdlib_partial_codegen_entries_type + ERROR_VARIABLE simdlib_partial_codegen_entries_type_error TYPE + "${simdlib_partial_codegen_profiles_json}" entries) +if(simdlib_partial_codegen_schema_error OR + NOT simdlib_partial_codegen_schema_type STREQUAL "NUMBER" OR + simdlib_partial_codegen_entries_type_error OR + NOT simdlib_partial_codegen_entries_type STREQUAL "ARRAY") + message(FATAL_ERROR + "PartialRegister code-generation profile root requires numeric schemaVersion " + "and array entries") +endif() +string(JSON simdlib_partial_codegen_schema_version GET + "${simdlib_partial_codegen_profiles_json}" schemaVersion) +if(NOT simdlib_partial_codegen_schema_version EQUAL 1) + message(FATAL_ERROR + "PartialRegister code-generation profile schemaVersion must be 1") +endif() +string(JSON simdlib_partial_codegen_profile_count + ERROR_VARIABLE simdlib_partial_codegen_entries_error LENGTH + "${simdlib_partial_codegen_profiles_json}" entries) +if(simdlib_partial_codegen_entries_error OR + simdlib_partial_codegen_profile_count EQUAL 0) + message(FATAL_ERROR + "PartialRegister code-generation profiles must define a non-empty entries array: " + "${simdlib_partial_codegen_entries_error}") +endif() +math(EXPR simdlib_partial_codegen_profile_last + "${simdlib_partial_codegen_profile_count} - 1") +set(simdlib_partial_codegen_wildcard_keys "") +set(simdlib_partial_codegen_exact_base_keys "") +set(simdlib_partial_codegen_exact_versions "") +foreach(entry_index RANGE ${simdlib_partial_codegen_profile_last}) + string(JSON entry_member_count LENGTH + "${simdlib_partial_codegen_profiles_json}" entries ${entry_index}) + if(NOT entry_member_count EQUAL 8) + message(FATAL_ERROR + "PartialRegister code-generation profile ${entry_index} must contain " + "exactly the eight schema members") + endif() + simdlib_partial_codegen_profile_property(entry_compiler ${entry_index} compiler STRING) + simdlib_partial_codegen_profile_property(entry_profile ${entry_index} profile STRING) + simdlib_partial_codegen_profile_property(entry_register_width ${entry_index} registerWidth NUMBER) + simdlib_partial_codegen_profile_property(entry_isa_profile ${entry_index} isaProfile STRING) + simdlib_partial_codegen_profile_property(entry_reason ${entry_index} reason STRING) + simdlib_partial_codegen_profile_property(entry_wrapper_hash ${entry_index} wrapperSha256 STRING) + simdlib_partial_codegen_profile_property(entry_raw_hash ${entry_index} rawSha256 STRING) + if(NOT entry_compiler STREQUAL "clang-cl" AND + NOT entry_compiler STREQUAL "msvc") + message(FATAL_ERROR + "PartialRegister code-generation profile ${entry_index} has unsupported " + "compiler ${entry_compiler}") + endif() + if(NOT entry_profile MATCHES "^(predicate|value|arithmetic|general|abi)$") + message(FATAL_ERROR + "PartialRegister code-generation profile ${entry_index} has unsupported " + "profile ${entry_profile}") + endif() + if(NOT entry_register_width EQUAL 128 AND + NOT entry_register_width EQUAL 256) + message(FATAL_ERROR + "PartialRegister code-generation profile ${entry_index} has unsupported " + "registerWidth ${entry_register_width}") + endif() + if(NOT entry_isa_profile STREQUAL "SSE42" AND + NOT entry_isa_profile STREQUAL "AVX2") + message(FATAL_ERROR + "PartialRegister code-generation profile ${entry_index} has unsupported " + "isaProfile ${entry_isa_profile}") + endif() + if(entry_reason STREQUAL "") + message(FATAL_ERROR + "PartialRegister code-generation profile ${entry_index} has an empty reason") + endif() + foreach(hash_name IN ITEMS wrapper raw) + set(hash_value "${entry_${hash_name}_hash}") + string(LENGTH "${hash_value}" hash_length) + if(NOT hash_length EQUAL 64 OR NOT hash_value MATCHES "^[0-9a-f]+$") + message(FATAL_ERROR + "PartialRegister code-generation profile ${entry_index} has an invalid " + "${hash_name} SHA-256 value") + endif() + endforeach() + string(JSON entry_version_count ERROR_VARIABLE entry_versions_error LENGTH + "${simdlib_partial_codegen_profiles_json}" + entries ${entry_index} compilerVersions) + if(entry_versions_error OR entry_version_count EQUAL 0) + message(FATAL_ERROR + "PartialRegister code-generation profile ${entry_index} must define " + "at least one compiler version: ${entry_versions_error}") + endif() + math(EXPR entry_version_last "${entry_version_count} - 1") + set(entry_base_key + "${entry_compiler}|${entry_profile}|${entry_register_width}|${entry_isa_profile}") + foreach(version_index RANGE ${entry_version_last}) + string(JSON entry_version_type TYPE + "${simdlib_partial_codegen_profiles_json}" + entries ${entry_index} compilerVersions ${version_index}) + string(JSON entry_version GET "${simdlib_partial_codegen_profiles_json}" + entries ${entry_index} compilerVersions ${version_index}) + if(NOT entry_version_type STREQUAL "STRING" OR entry_version STREQUAL "") + message(FATAL_ERROR + "PartialRegister code-generation profile ${entry_index} has an invalid " + "compiler version at index ${version_index}") + endif() + if(entry_version STREQUAL "*") + list(FIND simdlib_partial_codegen_wildcard_keys + "${entry_base_key}" duplicate_wildcard_index) + list(FIND simdlib_partial_codegen_exact_base_keys + "${entry_base_key}" wildcard_exact_overlap_index) + if(NOT duplicate_wildcard_index EQUAL -1 OR + NOT wildcard_exact_overlap_index EQUAL -1) + message(FATAL_ERROR + "PartialRegister code-generation profile ${entry_index} overlaps " + "another retained profile for ${entry_base_key}") + endif() + list(APPEND simdlib_partial_codegen_wildcard_keys "${entry_base_key}") + else() + if(NOT entry_version MATCHES "^[0-9]+(\\.[0-9]+)*$") + message(FATAL_ERROR + "PartialRegister code-generation profile ${entry_index} has invalid " + "compiler version ${entry_version}") + endif() + list(FIND simdlib_partial_codegen_wildcard_keys + "${entry_base_key}" exact_wildcard_overlap_index) + if(NOT exact_wildcard_overlap_index EQUAL -1) + message(FATAL_ERROR + "PartialRegister code-generation profile ${entry_index} overlaps " + "a wildcard retained profile for ${entry_base_key}") + endif() + list(LENGTH simdlib_partial_codegen_exact_base_keys exact_key_count) + if(exact_key_count GREATER 0) + math(EXPR exact_key_last "${exact_key_count} - 1") + foreach(exact_key_index RANGE ${exact_key_last}) + list(GET simdlib_partial_codegen_exact_base_keys + ${exact_key_index} existing_base_key) + list(GET simdlib_partial_codegen_exact_versions + ${exact_key_index} existing_version) + if("${existing_base_key}" STREQUAL "${entry_base_key}" AND + "${entry_version}" VERSION_EQUAL "${existing_version}") + message(FATAL_ERROR + "PartialRegister code-generation profile ${entry_index} overlaps " + "another retained profile for ${entry_base_key}, ${entry_version}") + endif() + endforeach() + endif() + list(APPEND simdlib_partial_codegen_exact_base_keys "${entry_base_key}") + list(APPEND simdlib_partial_codegen_exact_versions "${entry_version}") + endif() + endforeach() +endforeach() + +# @brief Selects the exact retained generated-code profiles for one qualified compiler cell. +# @param profile Qualified PartialRegister code-generation profile. +# @param register_width Native width selected for the fixture. +# @param isa_profile ISA profile selected for the fixture. +# @param reason_variable Output variable receiving the documented exception identifier. +# @param wrapper_hash_variable Output variable receiving the retained wrapper profile hash. +# @param raw_hash_variable Output variable receiving the retained raw profile hash. +function(simdlib_partial_retained_profiles profile register_width isa_profile reason_variable wrapper_hash_variable raw_hash_variable) + set(reason "") + set(wrapper_hash "") + set(raw_hash "") + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND SIMDLIB_MSVC_STYLE_DRIVER) + set(compiler_key "clang-cl") + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(compiler_key "msvc") + else() + set(${reason_variable} "" PARENT_SCOPE) + set(${wrapper_hash_variable} "" PARENT_SCOPE) + set(${raw_hash_variable} "" PARENT_SCOPE) + return() + endif() + set(match_count 0) + foreach(entry_index RANGE ${simdlib_partial_codegen_profile_last}) + string(JSON entry_compiler GET "${simdlib_partial_codegen_profiles_json}" + entries ${entry_index} compiler) + string(JSON entry_profile GET "${simdlib_partial_codegen_profiles_json}" + entries ${entry_index} profile) + string(JSON entry_register_width GET "${simdlib_partial_codegen_profiles_json}" + entries ${entry_index} registerWidth) + string(JSON entry_isa_profile GET "${simdlib_partial_codegen_profiles_json}" + entries ${entry_index} isaProfile) + if(NOT entry_compiler STREQUAL "${compiler_key}" OR + NOT entry_profile STREQUAL "${profile}" OR + NOT entry_register_width EQUAL register_width OR + NOT entry_isa_profile STREQUAL "${isa_profile}") + continue() + endif() + string(JSON entry_version_count LENGTH + "${simdlib_partial_codegen_profiles_json}" + entries ${entry_index} compilerVersions) + math(EXPR entry_version_last "${entry_version_count} - 1") + set(version_matches FALSE) + foreach(version_index RANGE ${entry_version_last}) + string(JSON entry_version GET "${simdlib_partial_codegen_profiles_json}" + entries ${entry_index} compilerVersions ${version_index}) + if(entry_version STREQUAL "*" OR + CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL "${entry_version}") + set(version_matches TRUE) + endif() + endforeach() + if(NOT version_matches) + continue() + endif() + math(EXPR match_count "${match_count} + 1") + if(match_count GREATER 1) + message(FATAL_ERROR + "Multiple PartialRegister code-generation profiles match ${compiler_key} " + "${CMAKE_CXX_COMPILER_VERSION}, ${profile}, ${register_width}, ${isa_profile}") + endif() + string(JSON reason GET "${simdlib_partial_codegen_profiles_json}" + entries ${entry_index} reason) + string(JSON wrapper_hash GET "${simdlib_partial_codegen_profiles_json}" + entries ${entry_index} wrapperSha256) + string(JSON raw_hash GET "${simdlib_partial_codegen_profiles_json}" + entries ${entry_index} rawSha256) + endforeach() + set(${reason_variable} "${reason}" PARENT_SCOPE) + set(${wrapper_hash_variable} "${wrapper_hash}" PARENT_SCOPE) + set(${raw_hash_variable} "${raw_hash}" PARENT_SCOPE) +endfunction() + +# @brief Adds one wrapper/raw partial-register invariant-boundary generated-code comparison. +# @param register_width Native register width selected for the fixture. +# @param isa_profile ISA profile used to compile both fixture sides. +function(simdlib_add_partial_mask_codegen_gate register_width isa_profile) + set(stack_protector_mode "default") + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(stack_protector_mode "msvc-gs") + endif() + simdlib_partial_retained_profiles(predicate ${register_width} ${isa_profile} + predicate_difference_reason predicate_wrapper_hash predicate_raw_hash) + simdlib_partial_retained_profiles(value ${register_width} ${isa_profile} + value_difference_reason value_wrapper_hash value_raw_hash) + if(isa_profile STREQUAL "SSE42") + set(target_suffix "${register_width}Sse42") + set(artifact_profile sse42) + else() + set(target_suffix "${register_width}Avx2") + set(artifact_profile avx2) + endif() + set(wrapper_target PartialRegisterMaskCodegenWrapper${target_suffix}) + set(raw_target PartialRegisterMaskCodegenRaw${target_suffix}) + add_library(${wrapper_target} OBJECT tests/codegen/PartialRegisterMaskCodegen.cpp) + add_library(${raw_target} OBJECT tests/codegen/PartialRegisterMaskCodegenRaw.cpp) + foreach(target IN ITEMS ${wrapper_target} ${raw_target}) + simdlib_register_development_target(${target} OPTIMIZED_CODEGEN) + target_link_libraries(${target} PRIVATE SimdLib::Register) + target_compile_definitions(${target} PRIVATE SIMDLIB_PARTIAL_MASK_CODEGEN_WIDTH=${register_width}) + simdlib_enable_development_warnings(${target}) + if(isa_profile STREQUAL "SSE42") + simdlib_enable_register_sse42(${target}) + else() + simdlib_enable_register_avx2(${target}) + endif() + endforeach() + set(artifact_directory "${CMAKE_CURRENT_BINARY_DIR}/partial-register-codegen/${artifact_profile}/${register_width}") + set(record_file "${artifact_directory}/predicate-composition/comparison.record.json") + set(value_record_file "${artifact_directory}/value-operations/comparison.record.json") + add_custom_command( + OUTPUT "${record_file}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${artifact_directory}/predicate-composition" + COMMAND ${CMAKE_COMMAND} + -DWRAPPER_OBJECT=$ + -DRAW_OBJECT=$ + -DOBJDUMP=${CMAKE_OBJDUMP} + -DARTIFACT_DIRECTORY=${artifact_directory}/predicate-composition + -DCOMPILER_ID=${CMAKE_CXX_COMPILER_ID} + -DCOMPILER_VERSION=${CMAKE_CXX_COMPILER_VERSION} + -DCOMPILER_PATH=${CMAKE_CXX_COMPILER} + -DSYSTEM_NAME=${CMAKE_SYSTEM_NAME} + -DSYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR} + -DCONFIGURATION=$ + -DREGISTER_WIDTH=${register_width} + -DISA_PROFILE=${isa_profile} + -DVECTORCALL_ENABLED=0 + -DSTACK_PROTECTOR_MODE=${stack_protector_mode} + -DRECORD_ONLY=OFF + -DRECORDED_DIFFERENCE_REASON=${predicate_difference_reason} + -DEXPECTED_WRAPPER_PROFILE_SHA256=${predicate_wrapper_hash} + -DEXPECTED_RAW_PROFILE_SHA256=${predicate_raw_hash} + -DCODEGEN_PROFILE=partial-register-invariant-boundaries + "-DSYMBOL_PATTERN=simdlib_partial_mask_codegen_compose" + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CompareRegisterCodegen.cmake + DEPENDS ${wrapper_target} ${raw_target} $ $ cmake/CompareRegisterCodegen.cmake + COMMENT "Comparing ${register_width}-bit partial-register invariant-boundary code" + VERBATIM) + add_custom_command( + OUTPUT "${value_record_file}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${artifact_directory}/value-operations" + COMMAND ${CMAKE_COMMAND} + -DWRAPPER_OBJECT=$ + -DRAW_OBJECT=$ + -DOBJDUMP=${CMAKE_OBJDUMP} + -DARTIFACT_DIRECTORY=${artifact_directory}/value-operations + -DCOMPILER_ID=${CMAKE_CXX_COMPILER_ID} + -DCOMPILER_VERSION=${CMAKE_CXX_COMPILER_VERSION} + -DCOMPILER_PATH=${CMAKE_CXX_COMPILER} + -DSYSTEM_NAME=${CMAKE_SYSTEM_NAME} + -DSYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR} + -DCONFIGURATION=$ + -DREGISTER_WIDTH=${register_width} + -DISA_PROFILE=${isa_profile} + -DVECTORCALL_ENABLED=0 + -DSTACK_PROTECTOR_MODE=${stack_protector_mode} + -DRECORD_ONLY=OFF + -DRECORDED_DIFFERENCE_REASON=${value_difference_reason} + -DEXPECTED_WRAPPER_PROFILE_SHA256=${value_wrapper_hash} + -DEXPECTED_RAW_PROFILE_SHA256=${value_raw_hash} + -DCODEGEN_PROFILE=partial-register-value-operations + "-DSYMBOL_PATTERN=simdlib_partial_register_codegen_(add|divide|import)" + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CompareRegisterCodegen.cmake + DEPENDS ${wrapper_target} ${raw_target} $ $ cmake/CompareRegisterCodegen.cmake + COMMENT "Comparing ${register_width}-bit partial-register value-operation code" + VERBATIM) + add_custom_target(PartialRegisterMaskCodegen${target_suffix} DEPENDS "${record_file}" "${value_record_file}") + add_dependencies(PartialRegisterMaskCodegen${target_suffix} ${wrapper_target} ${raw_target}) + simdlib_register_development_target(PartialRegisterMaskCodegen${target_suffix} OPTIMIZED_CODEGEN) + add_test(NAME PartialRegisterMaskCodegen.${target_suffix} + COMMAND ${CMAKE_COMMAND} -DRECORD_FILE=${record_file} -DEXPECTED_POLICY_MODE=ENFORCE -DEXPECTED_CONFIGURATION=$ -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/ValidateCodegenRecords.cmake) + set_tests_properties(PartialRegisterMaskCodegen.${target_suffix} PROPERTIES LABELS "PARTIAL_REGISTER;CODEGEN;${isa_profile}" RUN_SERIAL TRUE) + simdlib_register_development_test(PartialRegisterMaskCodegen.${target_suffix} OPTIMIZED_CODEGEN) + add_test(NAME PartialRegisterValueCodegen.${target_suffix} + COMMAND ${CMAKE_COMMAND} -DRECORD_FILE=${value_record_file} -DEXPECTED_POLICY_MODE=ENFORCE -DEXPECTED_CONFIGURATION=$ -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/ValidateCodegenRecords.cmake) + set_tests_properties(PartialRegisterValueCodegen.${target_suffix} PROPERTIES LABELS "PARTIAL_REGISTER;CODEGEN;${isa_profile}" RUN_SERIAL TRUE) + simdlib_register_development_test(PartialRegisterValueCodegen.${target_suffix} OPTIMIZED_CODEGEN) +endfunction() + +# @brief Adds one strict wrapper/raw arithmetic and specialized-operation generated-code comparison. +# @param register_width Native register width selected for the fixture. +# @param isa_profile ISA profile used to compile both fixture sides. +function(simdlib_add_partial_arithmetic_codegen_gate register_width isa_profile) + set(stack_protector_mode "default") + if(isa_profile STREQUAL "SSE42") + set(target_suffix "${register_width}Sse42") + set(artifact_profile sse42) + else() + set(target_suffix "${register_width}Avx2") + set(artifact_profile avx2) + endif() + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(stack_protector_mode "msvc-gs") + endif() + simdlib_partial_retained_profiles(arithmetic ${register_width} ${isa_profile} + codegen_difference_reason codegen_wrapper_hash codegen_raw_hash) + set(wrapper_target PartialRegisterArithmeticCodegenWrapper${target_suffix}) + set(raw_target PartialRegisterArithmeticCodegenRaw${target_suffix}) + add_library(${wrapper_target} OBJECT tests/codegen/PartialRegisterArithmeticCodegen.cpp) + add_library(${raw_target} OBJECT tests/codegen/PartialRegisterArithmeticCodegenRaw.cpp) + foreach(target IN ITEMS ${wrapper_target} ${raw_target}) + simdlib_register_development_target(${target} OPTIMIZED_CODEGEN) + target_link_libraries(${target} PRIVATE SimdLib::Register) + target_compile_definitions(${target} PRIVATE SIMDLIB_PARTIAL_ARITHMETIC_CODEGEN_WIDTH=${register_width}) + simdlib_enable_development_warnings(${target}) + if(isa_profile STREQUAL "SSE42") + simdlib_enable_register_sse42(${target}) + else() + simdlib_enable_register_avx2(${target}) + endif() + endforeach() + set(artifact_directory "${CMAKE_CURRENT_BINARY_DIR}/partial-register-codegen/${artifact_profile}/${register_width}/arithmetic-specialized") + set(record_file "${artifact_directory}/comparison.record.json") + add_custom_command( + OUTPUT "${record_file}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${artifact_directory}" + COMMAND ${CMAKE_COMMAND} + -DWRAPPER_OBJECT=$ + -DRAW_OBJECT=$ + -DOBJDUMP=${CMAKE_OBJDUMP} + -DARTIFACT_DIRECTORY=${artifact_directory} + -DCOMPILER_ID=${CMAKE_CXX_COMPILER_ID} + -DCOMPILER_VERSION=${CMAKE_CXX_COMPILER_VERSION} + -DCOMPILER_PATH=${CMAKE_CXX_COMPILER} + -DSYSTEM_NAME=${CMAKE_SYSTEM_NAME} + -DSYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR} + -DCONFIGURATION=$ + -DREGISTER_WIDTH=${register_width} + -DISA_PROFILE=${isa_profile} + -DVECTORCALL_ENABLED=0 + -DSTACK_PROTECTOR_MODE=${stack_protector_mode} + -DRECORD_ONLY=OFF + -DRECORDED_DIFFERENCE_REASON=${codegen_difference_reason} + -DEXPECTED_WRAPPER_PROFILE_SHA256=${codegen_wrapper_hash} + -DEXPECTED_RAW_PROFILE_SHA256=${codegen_raw_hash} + -DCODEGEN_PROFILE=partial-register-arithmetic-specialized + "-DSYMBOL_PATTERN=simdlib_partial_arithmetic_codegen_" + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CompareRegisterCodegen.cmake + DEPENDS ${wrapper_target} ${raw_target} $ $ cmake/CompareRegisterCodegen.cmake + COMMENT "Comparing ${register_width}-bit partial-register arithmetic and specialized-operation code" + VERBATIM) + add_custom_target(PartialRegisterArithmeticCodegen${target_suffix} DEPENDS "${record_file}") + add_dependencies(PartialRegisterArithmeticCodegen${target_suffix} ${wrapper_target} ${raw_target}) + simdlib_register_development_target(PartialRegisterArithmeticCodegen${target_suffix} OPTIMIZED_CODEGEN) + add_test(NAME PartialRegisterArithmeticCodegen.${target_suffix} + COMMAND ${CMAKE_COMMAND} -DRECORD_FILE=${record_file} -DEXPECTED_POLICY_MODE=ENFORCE -DEXPECTED_CONFIGURATION=$ -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/ValidateCodegenRecords.cmake) + set_tests_properties(PartialRegisterArithmeticCodegen.${target_suffix} PROPERTIES LABELS "PARTIAL_REGISTER;CODEGEN;${isa_profile}" RUN_SERIAL TRUE) + simdlib_register_development_test(PartialRegisterArithmeticCodegen.${target_suffix} OPTIMIZED_CODEGEN) +endfunction() + +# @brief Adds paired general PartialRegister operation-family fixtures. +# @param register_width Native register width selected for the fixture. +# @param isa_profile ISA profile used to compile both fixture sides. +function(simdlib_add_partial_general_codegen_gate register_width isa_profile) + simdlib_partial_retained_profiles(general ${register_width} ${isa_profile} + general_difference_reason general_wrapper_hash general_raw_hash) + if(isa_profile STREQUAL "SSE42") + set(target_suffix "${register_width}Sse42") + set(artifact_profile sse42) + else() + set(target_suffix "${register_width}Avx2") + set(artifact_profile avx2) + endif() + set(wrapper_target PartialRegisterGeneralWrapper${target_suffix}) + set(raw_target PartialRegisterGeneralRaw${target_suffix}) + add_library(${wrapper_target} OBJECT tests/codegen/PartialRegisterGeneralCodegen.cpp) + add_library(${raw_target} OBJECT tests/codegen/PartialRegisterGeneralCodegenRaw.cpp) + foreach(target IN ITEMS ${wrapper_target} ${raw_target}) + simdlib_register_development_target(${target} OPTIMIZED_CODEGEN) + target_link_libraries(${target} PRIVATE SimdLib::Register) + target_compile_definitions(${target} PRIVATE SIMDLIB_PARTIAL_GENERAL_CODEGEN_WIDTH=${register_width}) + simdlib_enable_development_warnings(${target}) + if(isa_profile STREQUAL "SSE42") + simdlib_enable_register_sse42(${target}) + else() + simdlib_enable_register_avx2(${target}) + endif() + endforeach() + set(artifact_directory "${CMAKE_CURRENT_BINARY_DIR}/partial-register-codegen/${artifact_profile}/${register_width}/general") + set(record_file "${artifact_directory}/comparison.record.json") + add_custom_command(OUTPUT "${record_file}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${artifact_directory}" + COMMAND ${CMAKE_COMMAND} -DWRAPPER_OBJECT=$ -DRAW_OBJECT=$ -DOBJDUMP=${CMAKE_OBJDUMP} -DARTIFACT_DIRECTORY=${artifact_directory} -DCOMPILER_ID=${CMAKE_CXX_COMPILER_ID} -DCOMPILER_VERSION=${CMAKE_CXX_COMPILER_VERSION} -DCOMPILER_PATH=${CMAKE_CXX_COMPILER} -DSYSTEM_NAME=${CMAKE_SYSTEM_NAME} -DSYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR} -DCONFIGURATION=$ -DREGISTER_WIDTH=${register_width} -DISA_PROFILE=${isa_profile} -DVECTORCALL_ENABLED=0 -DSTACK_PROTECTOR_MODE=default -DRECORD_ONLY=OFF -DRECORDED_DIFFERENCE_REASON=${general_difference_reason} -DEXPECTED_WRAPPER_PROFILE_SHA256=${general_wrapper_hash} -DEXPECTED_RAW_PROFILE_SHA256=${general_raw_hash} -DCODEGEN_PROFILE=partial-register-general "-DSYMBOL_PATTERN=simdlib_partial_general_codegen_" -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CompareRegisterCodegen.cmake + DEPENDS ${wrapper_target} ${raw_target} $ $ cmake/CompareRegisterCodegen.cmake VERBATIM) + add_custom_target(PartialRegisterGeneral${target_suffix} DEPENDS "${record_file}") + add_dependencies(PartialRegisterGeneral${target_suffix} ${wrapper_target} ${raw_target}) + simdlib_register_development_target(PartialRegisterGeneral${target_suffix} OPTIMIZED_CODEGEN) + add_test(NAME PartialRegisterGeneral.${target_suffix} COMMAND ${CMAKE_COMMAND} -DRECORD_FILE=${record_file} -DEXPECTED_POLICY_MODE=ENFORCE -DEXPECTED_CONFIGURATION=$ -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/ValidateCodegenRecords.cmake) + set_tests_properties(PartialRegisterGeneral.${target_suffix} PROPERTIES LABELS "PARTIAL_REGISTER;CODEGEN;${isa_profile}" RUN_SERIAL TRUE) + simdlib_register_development_test(PartialRegisterGeneral.${target_suffix} OPTIMIZED_CODEGEN) +endfunction() + +# @brief Adds paired non-inlined PartialRegister/native ABI mirrors. +# @param register_width Native register width selected for the fixture. +# @param isa_profile ISA profile used to compile both fixture sides. +function(simdlib_add_partial_abi_codegen_gate register_width isa_profile) + simdlib_partial_retained_profiles(abi ${register_width} ${isa_profile} + abi_difference_reason abi_wrapper_hash abi_raw_hash) + if(isa_profile STREQUAL "SSE42") + set(target_suffix "${register_width}Sse42") + set(artifact_profile sse42) + else() + set(target_suffix "${register_width}Avx2") + set(artifact_profile avx2) + endif() + set(wrapper_target PartialRegisterAbiWrapper${target_suffix}) + set(raw_target PartialRegisterAbiRaw${target_suffix}) + add_library(${wrapper_target} OBJECT tests/codegen/PartialRegisterAbi.cpp) + add_library(${raw_target} OBJECT tests/codegen/PartialRegisterAbiRaw.cpp) + foreach(target IN ITEMS ${wrapper_target} ${raw_target}) + simdlib_register_development_target(${target} OPTIMIZED_CODEGEN) + target_link_libraries(${target} PRIVATE SimdLib::Register) + target_compile_definitions(${target} PRIVATE SIMDLIB_PARTIAL_ABI_WIDTH=${register_width}) + simdlib_enable_development_warnings(${target}) + if(isa_profile STREQUAL "SSE42") + simdlib_enable_register_sse42(${target}) + else() + simdlib_enable_register_avx2(${target}) + endif() + endforeach() + set(artifact_directory "${CMAKE_CURRENT_BINARY_DIR}/partial-register-codegen/${artifact_profile}/${register_width}/abi") + set(record_file "${artifact_directory}/comparison.record.json") + add_custom_command(OUTPUT "${record_file}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${artifact_directory}" + COMMAND ${CMAKE_COMMAND} -DWRAPPER_OBJECT=$ -DRAW_OBJECT=$ -DOBJDUMP=${CMAKE_OBJDUMP} -DARTIFACT_DIRECTORY=${artifact_directory} -DCOMPILER_ID=${CMAKE_CXX_COMPILER_ID} -DCOMPILER_VERSION=${CMAKE_CXX_COMPILER_VERSION} -DCOMPILER_PATH=${CMAKE_CXX_COMPILER} -DSYSTEM_NAME=${CMAKE_SYSTEM_NAME} -DSYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR} -DCONFIGURATION=$ -DREGISTER_WIDTH=${register_width} -DISA_PROFILE=${isa_profile} -DVECTORCALL_ENABLED=1 -DSTACK_PROTECTOR_MODE=default -DRECORD_ONLY=OFF -DRECORDED_DIFFERENCE_REASON=${abi_difference_reason} -DEXPECTED_WRAPPER_PROFILE_SHA256=${abi_wrapper_hash} -DEXPECTED_RAW_PROFILE_SHA256=${abi_raw_hash} -DCODEGEN_PROFILE=partial-register-abi "-DSYMBOL_PATTERN=simdlib_partial_abi_" -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CompareRegisterCodegen.cmake + DEPENDS ${wrapper_target} ${raw_target} $ $ cmake/CompareRegisterCodegen.cmake VERBATIM) + add_custom_target(PartialRegisterAbi${target_suffix} DEPENDS "${record_file}") + add_dependencies(PartialRegisterAbi${target_suffix} ${wrapper_target} ${raw_target}) + simdlib_register_development_target(PartialRegisterAbi${target_suffix} OPTIMIZED_CODEGEN) + add_test(NAME PartialRegisterAbi.${target_suffix} COMMAND ${CMAKE_COMMAND} -DRECORD_FILE=${record_file} -DEXPECTED_POLICY_MODE=ENFORCE -DEXPECTED_CONFIGURATION=$ -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/ValidateCodegenRecords.cmake) + set_tests_properties(PartialRegisterAbi.${target_suffix} PROPERTIES LABELS "PARTIAL_REGISTER;ABI;CODEGEN;${isa_profile}" RUN_SERIAL TRUE) + simdlib_register_development_test(PartialRegisterAbi.${target_suffix} OPTIMIZED_CODEGEN) +endfunction() + +# @brief Adds one strict Api partial-transfer versus intrinsic-baseline codegen comparison. +# @param register_width Native register width selected for the fixture. +# @param isa_profile ISA profile used to compile both fixture sides. +function(simdlib_add_api_partial_transfer_codegen_gate register_width isa_profile) + if(isa_profile STREQUAL "SSE42") + set(target_suffix "${register_width}Sse42") + set(artifact_profile sse42) + else() + set(target_suffix "${register_width}Avx2") + set(artifact_profile avx2) + endif() + set(wrapper_target ApiPartialTransferCodegenWrapper${target_suffix}) + set(raw_target ApiPartialTransferCodegenRaw${target_suffix}) + add_library(${wrapper_target} OBJECT tests/codegen/ApiPartialTransferCodegen.cpp) + add_library(${raw_target} OBJECT tests/codegen/ApiPartialTransferCodegenRaw.cpp) + foreach(target IN ITEMS ${wrapper_target} ${raw_target}) + simdlib_register_development_target(${target} OPTIMIZED_CODEGEN) + target_link_libraries(${target} PRIVATE SimdLib::SimdLib) + target_compile_definitions(${target} PRIVATE SIMDLIB_API_PARTIAL_CODEGEN_WIDTH=${register_width}) + simdlib_enable_development_warnings(${target}) + if(isa_profile STREQUAL "SSE42") + simdlib_enable_register_sse42(${target}) + else() + simdlib_enable_register_avx2(${target}) + endif() + endforeach() + set(artifact_directory "${CMAKE_CURRENT_BINARY_DIR}/api-partial-transfer-codegen/${artifact_profile}/${register_width}") + set(record_file "${artifact_directory}/comparison.record.json") + add_custom_command( + OUTPUT "${record_file}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${artifact_directory}" + COMMAND ${CMAKE_COMMAND} + -DWRAPPER_OBJECT=$ + -DRAW_OBJECT=$ + -DOBJDUMP=${CMAKE_OBJDUMP} + -DARTIFACT_DIRECTORY=${artifact_directory} + -DCOMPILER_ID=${CMAKE_CXX_COMPILER_ID} + -DCOMPILER_VERSION=${CMAKE_CXX_COMPILER_VERSION} + -DCOMPILER_PATH=${CMAKE_CXX_COMPILER} + -DSYSTEM_NAME=${CMAKE_SYSTEM_NAME} + -DSYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR} + -DCONFIGURATION=$ + -DREGISTER_WIDTH=${register_width} + -DISA_PROFILE=${isa_profile} + -DVECTORCALL_ENABLED=0 + -DSTACK_PROTECTOR_MODE=default + -DRECORD_ONLY=OFF + -DCODEGEN_PROFILE=api-partial-transfer + "-DSYMBOL_PATTERN=simdlib_api_partial_codegen_" + "-DEXCLUDE_SYMBOL_PATTERN=simdlib_api_partial_codegen_to_array" + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CompareRegisterCodegen.cmake + DEPENDS ${wrapper_target} ${raw_target} $ $ cmake/CompareRegisterCodegen.cmake + COMMENT "Comparing ${register_width}-bit Api partial-transfer code against intrinsic baselines" + VERBATIM) + add_custom_target(ApiPartialTransferCodegen${target_suffix} DEPENDS "${record_file}") + add_dependencies(ApiPartialTransferCodegen${target_suffix} ${wrapper_target} ${raw_target}) + simdlib_register_development_target(ApiPartialTransferCodegen${target_suffix} OPTIMIZED_CODEGEN) + add_test(NAME ApiPartialTransferCodegen.${target_suffix} + COMMAND ${CMAKE_COMMAND} -E compare_files "${record_file}" "${record_file}") + set_tests_properties(ApiPartialTransferCodegen.${target_suffix} PROPERTIES LABELS "API;PARTIAL_TRANSFER;CODEGEN;${isa_profile}" RUN_SERIAL TRUE) + simdlib_register_development_test(ApiPartialTransferCodegen.${target_suffix} OPTIMIZED_CODEGEN) +endfunction() + +if(SIMDLIB_BUILD_REGISTER_CODEGEN_GATES AND SIMDLIB_REGISTER_COMPILER_SUPPORTED) + if(NOT CMAKE_OBJDUMP) + find_program(CMAKE_OBJDUMP NAMES llvm-objdump llvm-objdump.exe) + endif() + if(NOT CMAKE_OBJDUMP) + message(FATAL_ERROR "PartialRegisterMask codegen gates require llvm-objdump") + endif() + simdlib_add_partial_mask_codegen_gate(128 SSE42) + simdlib_add_partial_mask_codegen_gate(256 AVX2) + simdlib_add_partial_arithmetic_codegen_gate(128 SSE42) + simdlib_add_partial_arithmetic_codegen_gate(256 AVX2) + simdlib_add_partial_general_codegen_gate(128 SSE42) + simdlib_add_partial_general_codegen_gate(256 AVX2) + simdlib_add_partial_abi_codegen_gate(128 SSE42) + simdlib_add_partial_abi_codegen_gate(256 AVX2) + simdlib_add_api_partial_transfer_codegen_gate(128 SSE42) + simdlib_add_api_partial_transfer_codegen_gate(256 AVX2) + add_custom_target(PartialRegisterMaskCodegen DEPENDS PartialRegisterMaskCodegen128Sse42 PartialRegisterMaskCodegen256Avx2) + simdlib_register_development_target(PartialRegisterMaskCodegen OPTIMIZED_CODEGEN) + add_dependencies(RegisterCodegen PartialRegisterMaskCodegen) + add_custom_target(PartialRegisterArithmeticCodegen DEPENDS PartialRegisterArithmeticCodegen128Sse42 PartialRegisterArithmeticCodegen256Avx2) + simdlib_register_development_target(PartialRegisterArithmeticCodegen OPTIMIZED_CODEGEN) + add_dependencies(RegisterCodegen PartialRegisterArithmeticCodegen) + add_custom_target(PartialRegisterGeneral DEPENDS PartialRegisterGeneral128Sse42 PartialRegisterGeneral256Avx2) + simdlib_register_development_target(PartialRegisterGeneral OPTIMIZED_CODEGEN) + add_dependencies(RegisterCodegen PartialRegisterGeneral) + add_custom_target(PartialRegisterAbi DEPENDS PartialRegisterAbi128Sse42 PartialRegisterAbi256Avx2) + simdlib_register_development_target(PartialRegisterAbi OPTIMIZED_CODEGEN) + add_dependencies(RegisterCodegen PartialRegisterAbi) + add_custom_target(ApiPartialTransferCodegen DEPENDS ApiPartialTransferCodegen128Sse42 ApiPartialTransferCodegen256Avx2) + simdlib_register_development_target(ApiPartialTransferCodegen OPTIMIZED_CODEGEN) + add_dependencies(RegisterCodegen ApiPartialTransferCodegen) +endif() + +endblock() diff --git a/cmake/development/PartialRegisterCodegenProfiles.json b/cmake/development/PartialRegisterCodegenProfiles.json new file mode 100644 index 0000000..f423cad --- /dev/null +++ b/cmake/development/PartialRegisterCodegenProfiles.json @@ -0,0 +1,165 @@ +{ + "schemaVersion": 1, + "entries": [ + { + "compiler": "clang-cl", + "compilerVersions": ["20.1.8"], + "profile": "arithmetic", + "registerWidth": 128, + "isaProfile": "SSE42", + "reason": "clang-equivalent-operand-selection-and-scalar-division-scheduling", + "wrapperSha256": "7a7bdc8388855fc36ff194b173a1390af3cdfb19b60744d6d8b290576dbda609", + "rawSha256": "d0aa4f63e875a0f8ee53c64add1f7cc97a6ef2c7172f81e1cc4707d01aefccc1" + }, + { + "compiler": "clang-cl", + "compilerVersions": ["22.1.7", "22.1.8"], + "profile": "arithmetic", + "registerWidth": 128, + "isaProfile": "SSE42", + "reason": "clang-equivalent-operand-selection-and-scalar-division-scheduling", + "wrapperSha256": "895b587adb28f832780881dc106e6419dc5d55bcc8c68aec36a3e0caad2dd70c", + "rawSha256": "ae9f72212abfe3ed49b456a04b547b43a1f388c52ede0391ffaa63ebe3e57d7a" + }, + { + "compiler": "clang-cl", + "compilerVersions": ["20.1.8"], + "profile": "arithmetic", + "registerWidth": 256, + "isaProfile": "AVX2", + "reason": "clang-equivalent-commutative-operand-and-register-selection", + "wrapperSha256": "b21046545181e11fda3364e0a2617ff0534add9d8edb02899f93cb42d6bdc449", + "rawSha256": "dddc628b21cab6ac671229a69e15d64b2745ef2bf93a8ccd95c4c092557d7e29" + }, + { + "compiler": "clang-cl", + "compilerVersions": ["22.1.7", "22.1.8"], + "profile": "arithmetic", + "registerWidth": 256, + "isaProfile": "AVX2", + "reason": "clang-equivalent-commutative-operand-and-register-selection", + "wrapperSha256": "f6c808129c4d128a7c0b49bff6732f99bccc47d1a430462040d4cd840dbd853f", + "rawSha256": "7534f844dae0255c78786edce1196290fc97d1f8f85b651cc77f8578eb5ad38e" + }, + { + "compiler": "clang-cl", + "compilerVersions": ["20.1.8", "22.1.7", "22.1.8"], + "profile": "value", + "registerWidth": 128, + "isaProfile": "SSE42", + "reason": "clang-equivalent-add-operand-selection-and-scalar-division-scheduling", + "wrapperSha256": "caa6b7c5c57cc3754f495f85bb2f1593de64c0d425df56f2798aeebd2e0ae297", + "rawSha256": "e0972c1f50b1a8e1448c63879488fb04416e1510e0396ea922822b42be0f3031" + }, + { + "compiler": "clang-cl", + "compilerVersions": ["20.1.8", "22.1.7", "22.1.8"], + "profile": "value", + "registerWidth": 256, + "isaProfile": "AVX2", + "reason": "clang-equivalent-commutative-add-operand-selection", + "wrapperSha256": "cfd9f0deb4b440152aac87cb0d1cd2223b8c0023a81aa9666fc37c8491aa786f", + "rawSha256": "947545f582ed36c65c4252daab6b7e406d49a764d2761b8f4e3e437a240ae14b" + }, + { + "compiler": "msvc", + "compilerVersions": ["*"], + "profile": "predicate", + "registerWidth": 128, + "isaProfile": "SSE42", + "reason": "msvc-gs-predicate-composition-cookie", + "wrapperSha256": "40d3d2c14684c7cf4f7ec54e2745a71cc9c96ca77045f8e20e1d2cf2125f5d0f", + "rawSha256": "274480001869ad12ae5da9e9bfc801eb9823aa555ef42c2dd70ca4d37fb61c2a" + }, + { + "compiler": "msvc", + "compilerVersions": ["*"], + "profile": "predicate", + "registerWidth": 256, + "isaProfile": "AVX2", + "reason": "msvc-gs-predicate-composition-cookie", + "wrapperSha256": "d080a20d867fa7df30bb9ea1701ec5199900025e9629b4fbc25ee317d9e7a890", + "rawSha256": "c93ce1d1b5f1089deb799e23ed0e17deb3049498e50c10c37d65905296dfa555" + }, + { + "compiler": "msvc", + "compilerVersions": ["*"], + "profile": "value", + "registerWidth": 128, + "isaProfile": "SSE42", + "reason": "msvc-equivalent-import-normalization-gs-and-value-operation-allocation", + "wrapperSha256": "e500054cea61f6aabcebf97fadf9264b6de112e5d00519119b9ed093d70239dd", + "rawSha256": "9c085b63a9217f371422e89c09f4984b04cd0b37c3e780d3cb9bc4356f9857d1" + }, + { + "compiler": "msvc", + "compilerVersions": ["*"], + "profile": "value", + "registerWidth": 256, + "isaProfile": "AVX2", + "reason": "msvc-equivalent-import-normalization-gs-and-value-operation-allocation", + "wrapperSha256": "c6addf1f03722e3402215409c4f7c076ee16deecabcead203205e8e89d581ad8", + "rawSha256": "94367252b5828ebb560c32fdbcc95014c0991337a8adac50a808404378fa0d5c" + }, + { + "compiler": "msvc", + "compilerVersions": ["*"], + "profile": "arithmetic", + "registerWidth": 128, + "isaProfile": "SSE42", + "reason": "msvc-equivalent-unaligned-moves-and-register-allocation", + "wrapperSha256": "558ce994846ad0e4a9a44fff73b7e156eee94861041cb82ad2016d65af1b537c", + "rawSha256": "1d135fce910efd2919e05b564bc41f3d9fdaa5a474c0900947a9594716c8132c" + }, + { + "compiler": "msvc", + "compilerVersions": ["*"], + "profile": "arithmetic", + "registerWidth": 256, + "isaProfile": "AVX2", + "reason": "msvc-equivalent-unaligned-moves-and-register-allocation", + "wrapperSha256": "2eb906ffce8bf9931bcd1867dd50332fb06f4a17f288f98f7f7906e9c0f0067b", + "rawSha256": "167f03d2b0e974c1db1250d84257fe0a0612cb2173a2cde65d65dc636c72e8f0" + }, + { + "compiler": "msvc", + "compilerVersions": ["*"], + "profile": "general", + "registerWidth": 128, + "isaProfile": "SSE42", + "reason": "msvc-equivalent-vector-moves-mask-materialization-register-allocation-and-gs-cookie", + "wrapperSha256": "3e1b4ffebe93da71eec7360fb305c441d4255bea45d6af73e2fdc3a174d00075", + "rawSha256": "6b764b03be59acd44ab6301df1db1b2997902d94c20e603865f3a9fadb240709" + }, + { + "compiler": "msvc", + "compilerVersions": ["*"], + "profile": "general", + "registerWidth": 256, + "isaProfile": "AVX2", + "reason": "msvc-equivalent-vector-moves-mask-materialization-register-allocation-and-gs-cookie", + "wrapperSha256": "58abbe6e22f172fff40d7053103eb667cf4eb70a9c9b4a28eb40a998a8c12c26", + "rawSha256": "98e2e877a335a6423a156a813f6bf17a87bd04fd11cb8210eb26d2cc5ba0673a" + }, + { + "compiler": "msvc", + "compilerVersions": ["*"], + "profile": "abi", + "registerWidth": 128, + "isaProfile": "SSE42", + "reason": "msvc-partial-invariant-boundary-normalization-and-gs-cookie", + "wrapperSha256": "e2fc5990e063a65e7a77fd7bdcdd7469fed24bc3ccc3a4c927c2ab4fb5c528d1", + "rawSha256": "f8c290188f776e5887e14f2b2254d7d43a9fa6cdc8edfd0b835331d0270befca" + }, + { + "compiler": "msvc", + "compilerVersions": ["*"], + "profile": "abi", + "registerWidth": 256, + "isaProfile": "AVX2", + "reason": "msvc-partial-invariant-boundary-normalization-and-gs-cookie", + "wrapperSha256": "50201329cc0be4ebf40194ad0f789818634b0616c56bc690893023b111bf1ed4", + "rawSha256": "1db3aea52362ed96c2c45709be8725a73cbdf97db50ac7d5ae2481b0bf803b13" + } + ] +} diff --git a/cmake/development/RuntimeTests.cmake b/cmake/development/RuntimeTests.cmake index 2c43c4d..f03324e 100644 --- a/cmake/development/RuntimeTests.cmake +++ b/cmake/development/RuntimeTests.cmake @@ -84,6 +84,7 @@ if(SIMDLIB_BUILD_RUNTIME_TESTS) CHECKS_VALIDATION) target_link_libraries(RegisterPreconditionTests PRIVATE SimdLib::Register Catch2::Catch2WithMain) simdlib_enable_development_warnings(RegisterPreconditionTests) + target_compile_definitions(RegisterPreconditionTests PRIVATE SIMDLIB_ENABLE_CHECKS=1) simdlib_set_coverage_profile_prefix(RegisterPreconditionTests "Register.AVX2Preconditions") simdlib_enable_register_sse42(RegisterPreconditionTests) @@ -94,7 +95,41 @@ if(SIMDLIB_BUILD_RUNTIME_TESTS) PASS_REGULAR_EXPRESSION "SIMDLIB_REGISTER_PRECONDITION_FAILURE_EXPECTED_61B4C2" TIMEOUT 10) simdlib_label_discovered_tests(RegisterPreconditionTests_DISCOVERED_TESTS - "REGISTER;PRECONDITIONS;AVX2" CHECKS_VALIDATION) + "REGISTER;PARTIAL_REGISTER;PRECONDITIONS;AVX2" CHECKS_VALIDATION) + + simdlib_add_catch_test(PartialRegisterAvx2Tests tests/PartialRegisterObjectModel.tests.cpp + PartialRegister.AVX2 "PARTIAL_REGISTER;AVX2") + target_sources(PartialRegisterAvx2Tests PRIVATE + tests/PartialRegisterConstructionTransfer.tests.cpp + tests/PartialRegisterArithmetic.tests.cpp + tests/PartialRegisterBitwiseShiftComparison.tests.cpp + tests/PartialRegisterRearrangementConversion.tests.cpp + tests/PartialRegisterOperationMatrix.tests.cpp + tests/PartialRegisterSpecializedOperations.tests.cpp) + target_link_libraries(PartialRegisterAvx2Tests PRIVATE SimdLib::Register) + target_compile_definitions(PartialRegisterAvx2Tests PRIVATE + SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256=1) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + target_compile_options(PartialRegisterAvx2Tests PRIVATE /bigobj) + endif() + simdlib_enable_register_avx2(PartialRegisterAvx2Tests) + + simdlib_add_catch_test(PartialRegisterSse42Tests tests/PartialRegisterObjectModel.tests.cpp + PartialRegister.SSE42 "PARTIAL_REGISTER;SSE42") + target_sources(PartialRegisterSse42Tests PRIVATE + tests/PartialRegisterConstructionTransfer.tests.cpp + tests/PartialRegisterArithmetic.tests.cpp + tests/PartialRegisterBitwiseShiftComparison.tests.cpp + tests/PartialRegisterRearrangementConversion.tests.cpp + tests/PartialRegisterOperationMatrix.tests.cpp + tests/PartialRegisterSpecializedOperations.tests.cpp) + target_link_libraries(PartialRegisterSse42Tests PRIVATE SimdLib::Register) + target_compile_definitions(PartialRegisterSse42Tests PRIVATE + SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256=0) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + target_compile_options(PartialRegisterSse42Tests PRIVATE /bigobj) + endif() + simdlib_enable_register_sse42(PartialRegisterSse42Tests) endif() simdlib_add_catch_test(BmiPortableTests tests/Bmi.tests.cpp @@ -140,7 +175,21 @@ if(SIMDLIB_BUILD_RUNTIME_TESTS) endif() endif() - if(SIMDLIB_BUILD_API_SSE42_TESTS) + if(SIMDLIB_BUILD_API_SSE42_TESTS) + simdlib_add_catch_test(ImplHalfTransfer128Tests tests/ImplementationHalfTransfer.tests.cpp + Implementation.HalfTransfer128 "IMPLEMENTATION;PARTIAL_TRANSFER;SSE42") + target_compile_definitions(ImplHalfTransfer128Tests PRIVATE + SIMDLIB_IMPLEMENTATION_HALF_TRANSFER_TEST_WIDTH=128) + if(SIMDLIB_MSVC_STYLE_DRIVER) + target_compile_definitions(ImplHalfTransfer128Tests PRIVATE + SIMDLIB_HAS_SSE3=1 SIMDLIB_HAS_SSSE3=1 SIMDLIB_HAS_SSE41=1 SIMDLIB_HAS_SSE42=1) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(ImplHalfTransfer128Tests PRIVATE /arch:AVX2) + endif() + else() + target_compile_options(ImplHalfTransfer128Tests PRIVATE -msse4.2) + endif() + simdlib_add_catch_test(LogicalShuffleImpl128Tests tests/LogicalShuffleImpl128.tests.cpp LogicalShuffle.Impl128 "LOGICAL_SHUFFLE;SSE42") if(SIMDLIB_MSVC_STYLE_DRIVER) @@ -156,10 +205,12 @@ if(SIMDLIB_BUILD_RUNTIME_TESTS) simdlib_add_catch_test(ApiSse42Tests tests/Api128.tests.cpp Api.SSE42 "SSE42") target_sources(ApiSse42Tests PRIVATE + tests/ApiPartialTransfer.tests.cpp tests/LogicalShuffleApi.tests.cpp tests/ImmediateControlSlowPaths.tests.cpp tests/CompleteRegisterShift.tests.cpp) target_compile_definitions(ApiSse42Tests PRIVATE + SIMDLIB_API_PARTIAL_TRANSFER_TEST_WIDTH=128 SIMDLIB_LOGICAL_SHUFFLE_TEST_WIDTH=128 SIMDLIB_IMMEDIATE_CONTROL_TEST_WIDTH=128 SIMDLIB_COMPLETE_SHIFT_TEST_WIDTH=128) @@ -184,7 +235,7 @@ if(SIMDLIB_BUILD_RUNTIME_TESTS) target_compile_definitions(${uint128_target} PRIVATE SIMDLIB_TEST_CONSTEXPR_ASSERTIONS=$) endforeach() - target_compile_definitions(UInt128PortableTests PRIVATE + target_compile_definitions(UInt128PortableTests PRIVATE SIMDLIB_USE_COMPILER_CARRY_INTRINSICS=0 SIMDLIB_EXPECT_CARRY_PATH=0 SIMDLIB_HAS_BMI1=0) target_compile_definitions(UInt128ScalarTests PRIVATE SIMDLIB_EXPECT_CARRY_PATH=0) target_compile_definitions(UInt128OptimizedTests PRIVATE SIMDLIB_HAS_BMI1=1) @@ -207,7 +258,7 @@ if(SIMDLIB_BUILD_RUNTIME_TESTS) target_compile_options(UInt128OptimizedTests PRIVATE /arch:AVX2) target_compile_options(UInt128PortableTests PRIVATE /arch:AVX2) endif() - else() + else() target_compile_options(UInt128OptimizedTests PRIVATE -msse4.2 -mbmi) target_compile_options(UInt128PortableTests PRIVATE -msse4.2 -mno-bmi) endif() @@ -228,7 +279,17 @@ if(SIMDLIB_BUILD_RUNTIME_TESTS) simdlib_register_development_test(UInt128ScalarResultSetEquivalence RUNTIME_VALIDATION) endif() - if(SIMDLIB_BUILD_API_AVX2_TESTS) + if(SIMDLIB_BUILD_API_AVX2_TESTS) + simdlib_add_catch_test(ImplHalfTransfer256Tests tests/ImplementationHalfTransfer.tests.cpp + Implementation.HalfTransfer256 "IMPLEMENTATION;PARTIAL_TRANSFER;AVX2") + target_compile_definitions(ImplHalfTransfer256Tests PRIVATE + SIMDLIB_IMPLEMENTATION_HALF_TRANSFER_TEST_WIDTH=256) + if(SIMDLIB_MSVC_STYLE_DRIVER) + target_compile_options(ImplHalfTransfer256Tests PRIVATE /arch:AVX2) + else() + target_compile_options(ImplHalfTransfer256Tests PRIVATE -mavx2) + endif() + simdlib_add_catch_test(LogicalShuffleImpl256Tests tests/LogicalShuffleImpl256.tests.cpp LogicalShuffle.Impl256 "LOGICAL_SHUFFLE;AVX2") if(SIMDLIB_MSVC_STYLE_DRIVER) @@ -240,10 +301,12 @@ if(SIMDLIB_BUILD_RUNTIME_TESTS) simdlib_add_catch_test(ApiAvx2Tests tests/Api256.tests.cpp Api.AVX2 "AVX2") target_sources(ApiAvx2Tests PRIVATE + tests/ApiPartialTransfer.tests.cpp tests/LogicalShuffleApi.tests.cpp tests/ImmediateControlSlowPaths.tests.cpp tests/CompleteRegisterShift.tests.cpp) target_compile_definitions(ApiAvx2Tests PRIVATE + SIMDLIB_API_PARTIAL_TRANSFER_TEST_WIDTH=256 SIMDLIB_LOGICAL_SHUFFLE_TEST_WIDTH=256 SIMDLIB_IMMEDIATE_CONTROL_TEST_WIDTH=256 SIMDLIB_COMPLETE_SHIFT_TEST_WIDTH=256) diff --git a/cmake/development/SmokeTests.cmake b/cmake/development/SmokeTests.cmake index 13d3e6e..67051ea 100644 --- a/cmake/development/SmokeTests.cmake +++ b/cmake/development/SmokeTests.cmake @@ -33,6 +33,65 @@ if(SIMDLIB_BUILD_SMOKE_TESTS) set_tests_properties(RegisterOdr PROPERTIES LABELS "REGISTER;ODR;SSE42") simdlib_register_development_test(RegisterOdr SMOKE_VALIDATION) simdlib_set_coverage_profile_prefix(RegisterOdr "RegisterOdr") + + # @brief Adds one PartialRegister multi-translation-unit test for an ISA profile. + # @param target Development executable target name. + # @param bits Physical PartialRegister width used by the test. + # @param isa_profile Test label identifying the selected ISA profile. + function(simdlib_add_partial_register_odr_test target bits isa_profile) + add_executable(${target} + tests/partial_register_odr/main.cpp + tests/partial_register_odr/second_translation_unit.cpp + tests/partial_register_odr/fixture.h) + simdlib_register_development_target(${target} SMOKE_VALIDATION) + target_link_libraries(${target} PRIVATE SimdLib::Register) + target_compile_definitions(${target} PRIVATE + SIMDLIB_PARTIAL_REGISTER_ODR_BITS=${bits}) + simdlib_enable_development_warnings(${target}) + if(bits EQUAL 256) + simdlib_enable_register_avx2(${target}) + else() + simdlib_enable_register_sse42(${target}) + endif() + add_test(NAME ${target} COMMAND ${target}) + set_tests_properties(${target} PROPERTIES + LABELS "PARTIAL_REGISTER;ODR;${isa_profile}") + simdlib_register_development_test(${target} SMOKE_VALIDATION) + simdlib_set_coverage_profile_prefix(${target} "${target}") + endfunction() + + simdlib_add_partial_register_odr_test(PartialRegisterOdrSse42 128 SSE42) + simdlib_add_partial_register_odr_test(PartialRegisterOdrAvx2 256 AVX2) + + add_custom_target(InstalledPackagePartialRegisterConsumerArtifacts + COMMAND ${CMAKE_COMMAND} + -DSIMDLIB_BUILD_DIR=${CMAKE_CURRENT_BINARY_DIR} + -DSIMDLIB_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} + -DSIMDLIB_CONSUMER_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/tests/installed_consumer + -DSIMDLIB_CONSUMER_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}/installed-partial-register-consumer/$ + -DSIMDLIB_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/installed-partial-register-package/$ + -DSIMDLIB_CMAKE_COMMAND=${CMAKE_COMMAND} + -DSIMDLIB_CTEST_COMMAND=${CMAKE_CTEST_COMMAND} + -DSIMDLIB_GENERATOR=${CMAKE_GENERATOR} + -DSIMDLIB_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM} + -DSIMDLIB_GENERATOR_TOOLSET=${CMAKE_GENERATOR_TOOLSET} + -DSIMDLIB_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} + -DSIMDLIB_CXX_COMPILER=${CMAKE_CXX_COMPILER} + "-DSIMDLIB_CXX_FLAGS=${CMAKE_CXX_FLAGS}" + "-DSIMDLIB_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS}" + -DSIMDLIB_CXX_SCAN_FOR_MODULES=${CMAKE_CXX_SCAN_FOR_MODULES} + -DSIMDLIB_CONFIG=$ + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/RunInstalledPartialRegisterConsumer.cmake + VERBATIM) + simdlib_register_development_target(InstalledPackagePartialRegisterConsumerArtifacts SMOKE_VALIDATION) + add_test(NAME InstalledPackagePartialRegisterConsumer + COMMAND ${CMAKE_CTEST_COMMAND} + --test-dir ${CMAKE_CURRENT_BINARY_DIR}/installed-partial-register-consumer/$ + -C $ --output-on-failure) + set_tests_properties(InstalledPackagePartialRegisterConsumer PROPERTIES + LABELS "PARTIAL_REGISTER;CONSUMER;INSTALL;SSE42;AVX2" + RUN_SERIAL TRUE) + simdlib_register_development_test(InstalledPackagePartialRegisterConsumer SMOKE_VALIDATION) endif() endif() diff --git a/containers/Dockerfile.windows-clang20 b/containers/Dockerfile.windows-clang20 new file mode 100644 index 0000000..30e9c84 --- /dev/null +++ b/containers/Dockerfile.windows-clang20 @@ -0,0 +1,89 @@ +# syntax=docker/dockerfile:1.7@sha256:a57df69d0ea827fb7266491f2813635de6f17269be881f696fbfdf2d83dda33e +# escape=` + +ARG WINDOWS_BASE=mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2022@sha256:3983348680840ca6e53ad641e314c3c9184ca2fd19f88bc467600f7d9f6e9d73 + +FROM ${WINDOWS_BASE} + +LABEL org.opencontainers.image.title="SimdLib Windows clang-cl 20 validation" ` + org.opencontainers.image.description="Visual Studio 2022 Build Tools and pinned LLVM 20 environment for SimdLib" ` + org.opencontainers.image.source="https://github.com/dsisco11/SimdLib" ` + org.opencontainers.image.version="clang-cl-20.1.8-cmake-3.31.6" ` + org.simdlib.base.digest="sha256:3983348680840ca6e53ad641e314c3c9184ca2fd19f88bc467600f7d9f6e9d73" ` + org.simdlib.chocolatey.version="2.5.1" ` + org.simdlib.llvm.version="20.1.8" ` + org.simdlib.cmake.sha256="4cc39d59426cbbeb33d0daff1350e39b0585a90e3c9883dbba70816b8d282811" ` + org.simdlib.powershell.sha256="28e23a6e3a18662b73e6fb267855f31786fb95ec943111de9e9e1338844106b4" ` + org.simdlib.git.sha256="c2c955a21fa99889d83f485f24fa5d9a38fffc2d509d4022385510e11c26b250" + +SHELL ["powershell.exe", "-NoLogo", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command"] + +RUN $ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue'; ` + Invoke-WebRequest -Uri 'https://aka.ms/vs/17/release/vs_buildtools.exe' -OutFile C:\vs_buildtools.exe; ` + $arguments = @('--quiet', '--wait', '--norestart', '--nocache', ` + '--installPath', 'C:\BuildTools', ` + '--add', 'Microsoft.VisualStudio.Workload.VCTools', ` + '--add', 'Microsoft.VisualStudio.Component.VC.CMake.Project', ` + '--includeRecommended', ` + '--remove', 'Microsoft.VisualStudio.Component.Windows10SDK.10240', ` + '--remove', 'Microsoft.VisualStudio.Component.Windows10SDK.10586', ` + '--remove', 'Microsoft.VisualStudio.Component.Windows10SDK.14393', ` + '--remove', 'Microsoft.VisualStudio.Component.Windows81SDK'); ` + $process = Start-Process C:\vs_buildtools.exe -ArgumentList $arguments -Wait -PassThru; ` + if ($process.ExitCode -notin @(0, 3010)) { throw "Visual Studio Build Tools installer failed with exit code $($process.ExitCode)" }; ` + Remove-Item C:\vs_buildtools.exe + +ARG LLVM_VERSION=20.1.8 +ARG CHOCOLATEY_VERSION=2.5.1 +ARG CMAKE_VERSION=3.31.6 +ARG CMAKE_SHA256=4cc39d59426cbbeb33d0daff1350e39b0585a90e3c9883dbba70816b8d282811 +ARG POWERSHELL_VERSION=7.5.3 +ARG POWERSHELL_SHA256=28e23a6e3a18662b73e6fb267855f31786fb95ec943111de9e9e1338844106b4 +ARG MINGIT_VERSION=2.51.0 +ARG MINGIT_SHA256=c2c955a21fa99889d83f485f24fa5d9a38fffc2d509d4022385510e11c26b250 + +RUN $ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue'; ` + [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; ` + $env:chocolateyVersion = $env:CHOCOLATEY_VERSION; ` + Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')); ` + & C:\ProgramData\chocolatey\bin\choco.exe install llvm --version=$env:LLVM_VERSION --yes --no-progress --limit-output; ` + if ($LASTEXITCODE -notin @(0, 1605, 1614, 1641, 3010)) { throw "Chocolatey failed to install LLVM $env:LLVM_VERSION with exit code $LASTEXITCODE" } + +RUN $ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue'; ` + Invoke-WebRequest -Uri "https://github.com/Kitware/CMake/releases/download/v$env:CMAKE_VERSION/cmake-$env:CMAKE_VERSION-windows-x86_64.msi" -OutFile C:\cmake.msi; ` + if ((Get-FileHash C:\cmake.msi -Algorithm SHA256).Hash -ne $env:CMAKE_SHA256) { throw 'CMake installer checksum mismatch' }; ` + $process = Start-Process msiexec.exe -ArgumentList '/i','C:\cmake.msi','/qn','/norestart','ADD_CMAKE_TO_PATH=System' -Wait -PassThru; ` + if ($process.ExitCode -notin @(0, 3010)) { throw "CMake installer failed with exit code $($process.ExitCode)" }; ` + Remove-Item C:\cmake.msi + +RUN $ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue'; ` + Invoke-WebRequest -Uri "https://github.com/git-for-windows/git/releases/download/v$env:MINGIT_VERSION.windows.1/MinGit-$env:MINGIT_VERSION-64-bit.zip" -OutFile C:\mingit.zip; ` + if ((Get-FileHash C:\mingit.zip -Algorithm SHA256).Hash -ne $env:MINGIT_SHA256) { throw 'MinGit archive checksum mismatch' }; ` + Expand-Archive C:\mingit.zip -DestinationPath C:\Git; ` + Remove-Item C:\mingit.zip + +RUN $ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue'; ` + Invoke-WebRequest -Uri "https://github.com/PowerShell/PowerShell/releases/download/v$env:POWERSHELL_VERSION/PowerShell-$env:POWERSHELL_VERSION-win-x64.msi" -OutFile C:\powershell.msi; ` + if ((Get-FileHash C:\powershell.msi -Algorithm SHA256).Hash -ne $env:POWERSHELL_SHA256) { throw 'PowerShell installer checksum mismatch' }; ` + $process = Start-Process msiexec.exe -ArgumentList '/i','C:\powershell.msi','/qn','/norestart','ADD_PATH=1' -Wait -PassThru; ` + if ($process.ExitCode -notin @(0, 3010)) { throw "PowerShell installer failed with exit code $($process.ExitCode)" }; ` + Remove-Item C:\powershell.msi + +ENV PATH="C:\Program Files\PowerShell\7;C:\Program Files\CMake\bin;C:\Program Files\LLVM\bin;C:\Git\cmd;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0" + +RUN clang-cl.exe --version; ` + if ($LASTEXITCODE -ne 0) { throw 'clang-cl validation failed' }; ` + if ((clang-cl.exe --version | Select-Object -First 1) -notmatch 'clang version 20\.1\.8\b') { throw 'Unexpected clang-cl version' }; ` + cmake.exe --version; ` + if ($LASTEXITCODE -ne 0) { throw 'CMake validation failed' }; ` + if ((cmake.exe --version | Select-Object -First 1) -ne 'cmake version 3.31.6') { throw 'Unexpected CMake version' }; ` + pwsh.exe --version; ` + if ($LASTEXITCODE -ne 0) { throw 'PowerShell validation failed' }; ` + git.exe --version; ` + if ($LASTEXITCODE -ne 0) { throw 'Git validation failed' }; ` + if (-not (Test-Path 'C:\BuildTools\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja\ninja.exe')) { throw 'Visual Studio Ninja is missing' }; ` + if (-not (Test-Path 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe')) { throw 'Visual Studio locator is missing' }; ` + git.exe config --global --add safe.directory C:/workspace + +WORKDIR C:\workspace +ENTRYPOINT ["pwsh.exe", "-NoLogo", "-NoProfile"] diff --git a/docs/BuildPipeline.md b/docs/BuildPipeline.md index 3be8773..c8f2f19 100644 --- a/docs/BuildPipeline.md +++ b/docs/BuildPipeline.md @@ -7,8 +7,9 @@ command. A complete local build is: tools/Build.ps1 -Scope All ``` -This builds MSVC Release and the representative MSVC Debug cell, clang-cl -Release, native Clang Debug coverage, GCC 13 core-only Release, GCC 14 Release, +This builds MSVC Release and the representative MSVC Debug cell, the +caller-selected Windows clang-cl Release, native Clang Debug coverage, GCC 13 +core-only Release, GCC 14 Release, and Clang 22 Release plus the representative ASan+UBSan Debug cell. It builds the correctness, ABI, sanitizer, consumer, coverage, probe, example, and header-validation artifacts, plus the mandatory optimized generated-code gates @@ -17,6 +18,13 @@ GCC 14, or Clang 22 Debug cells. Debug, sanitizer, and coverage cells do not compile Register generated-code fixtures. The command does not compile benchmark targets or run any executable. +GitHub Actions runs the Windows clang-cl Release ownership twice: the explicit +LLVM 20.1.8 compatibility floor inside a dedicated Visual Studio Build Tools +container, and LLVM 22.1.7 on the native Windows runner. The native job obtains +LLVM and CMake 4.4.0 through Chocolatey. Clang coverage belongs only to the LLVM +22.1.7 job. The LLVM 20 cell is not a claim about Visual Studio's default or +optional bundled Clang version. + Before starting compiler cells, `Build.ps1` performs two focused validations. `tools/Validate-PipelineTooling.ps1` validates matrix topology, pipeline regressions, configured ownership rules, and no-rebuild behavior once for the @@ -55,7 +63,10 @@ The complete `All` scope requires a Windows x64 host with: - Visual Studio 2022 and the MSVC x64 C++ tools; - LLVM 20 or newer with `clang-cl`, `clang++`, `llvm-profdata`, `llvm-cov`, and - `llvm-readobj` available on `PATH`; + `llvm-readobj` available on `PATH`; the GitHub workflow qualifies LLVM 20.1.8 + as its explicit Windows clang-cl compatibility floor in a dedicated container + and installs LLVM 22.1.7 and CMake 4.4.0 through Chocolatey for the newer + native compiler and coverage cells; - CMake 3.31 or newer; and - Docker Desktop with a running Linux-container daemon. @@ -324,10 +335,13 @@ create additional consumer contracts. ## Diagnostic runners and cleanup -`Run-NativeMatrix.ps1` and `Run-ContainerMatrix.ps1` are lower-level diagnostic -and CI implementation interfaces. Normal repository workflows use `Build.ps1`, -`Run-Tests.ps1`, `Build-Benchmarks.ps1`, and `Run-Benchmarks.ps1`; the -lower-level scripts do not define additional mandatory modes. +`Run-NativeMatrix.ps1`, `Run-ContainerMatrix.ps1`, and +`Run-WindowsClang20Container.ps1` are lower-level diagnostic and CI +implementation interfaces. Normal repository workflows use `Build.ps1`, +`Run-Tests.ps1`, `Build-Benchmarks.ps1`, and `Run-Benchmarks.ps1`; the lower-level +scripts do not define additional mandatory modes. The Windows container runner +is the exception used to isolate the LLVM 20.1.8 compatibility cell from a +host's newer LLVM installation. Container images and selected Linux fingerprint roots can be removed with: diff --git a/docs/ContainerValidation.md b/docs/ContainerValidation.md index ce2eb9c..1b0c956 100644 --- a/docs/ContainerValidation.md +++ b/docs/ContainerValidation.md @@ -5,6 +5,18 @@ Clang 22 validation. The same Dockerfiles, Compose definition, entrypoint, and PowerShell runner are used locally and in GitHub Actions. Native jobs remain authoritative for MSVC, clang-cl, Windows ABI behavior, and vector calling-convention behavior. +The Windows clang-cl 20 compatibility cell is isolated separately in +`containers/Dockerfile.windows-clang20`. That Windows Server Core image contains +Visual Studio 2022 Build Tools, Chocolatey-provisioned LLVM 20.1.8, CMake +3.31.6, PowerShell 7.5.3, and MinGit. +`tools/Run-WindowsClang20Container.ps1` runs the ordinary native +`ClangCl` build, test, and benchmark commands inside it. It requires a Windows +Docker engine; it is intentionally not part of the Linux Compose matrix. Visual +Studio supplies the MSVC ABI, standard library, SDK, linker, and build +environment, while the standalone LLVM installation defines SimdLib's explicit +clang-cl 20 compatibility floor. The image does not represent Visual Studio's +default compiler selection or its optional bundled Clang version. + ## Environment contract | Service | Scope | Base | Compiler | diff --git a/docs/PartialRegisterOperationLedger.md b/docs/PartialRegisterOperationLedger.md new file mode 100644 index 0000000..c20b726 --- /dev/null +++ b/docs/PartialRegisterOperationLedger.md @@ -0,0 +1,266 @@ +# PartialRegister operation ledger + +## Purpose + +This document records the current public contract for `PartialRegister`. It +applies to every method and operator currently exposed by `Register`; no +Register surface may be silently omitted. + +`PartialRegister` is a value containing a fixed prefix of logical SIMD lanes. +It is not a partial load helper, a dynamically sized value, a mask, or a +subclass of `Register`. + +## Type and representation contract + +The primary value type is: + +`PartialRegister` + +It is available exactly when all of the following are true: + +- `RegisterAvailable` is satisfied. +- `active_lane_count` is greater than zero. +- `active_lane_count` is smaller than the native lane count for + `Api`. +- For a 256-bit register, the active payload extends into the upper 128-bit + group: `active_lane_count * sizeof(element_t) * 8 > 128`. + +The strict upper bound deliberately reserves a fully populated native register +for `Register`. + +`PartialRegister` is `final`, non-polymorphic, non-allocating, and contains +exactly one `Api::vector_t`. It has no base class, +runtime metadata, active-lane mask member, proxy, or secondary native value. +The active count is compile-time metadata only. + +The public metadata has these meanings: + +| Name | Meaning | +| --- | --- | +| `register_width` | Physical native-register width in bits. | +| `byte_count` | Physical native-register size in bytes. | +| `native_lane_count` | Number of `element_t` lanes in the physical register. | +| `lane_count` | Number of logical active lanes. | +| `active_byte_count` | `lane_count * sizeof(element_t)`. | +| `inactive_lane_count` | `native_lane_count - lane_count`. | + +Logical lanes are the contiguous low prefix with indices +`[0, lane_count)`. Native lanes at indices `[lane_count, native_lane_count)` +are inactive and must have the all-bits-zero representation. For floating +point lanes this is positive zero, not merely a value that compares equal to +zero. + +The complete-register aliases `element_type`, `api_type`, `native_type`, and +`mask_type` remain available. `mask_type` names +`PartialRegisterMask`. + +## Construction, native interoperation, and conversions + +The native value is the sole public non-static data member, matching +`Register`'s aggregate object model and avoiding custom-constructor codegen. +Direct aggregate initialization has a documented precondition that the native +value already contains an all-bits-zero inactive suffix. Checked observation +boundaries validate that precondition; ordinary operation results and imports +establish the invariant themselves. + +The implementation provides two explicit native boundaries: + +- `from_native(native_type)` accepts one native value and clears its inactive + suffix before constructing the result. +- `to_native()` returns a by-value native copy whose inactive suffix is known + to be zero. + +These are the sanitizing and validating native interoperation paths. Direct +access to `native` remains available for Register-equivalent low-level use, +subject to the aggregate-initialization precondition above. + +All element and byte transfer APIs use the logical extent. `load_bytes()` and +`store_bytes()` respectively consume and produce exactly `active_byte_count` +bytes. The aligned transfer APIs retain the existing `byte_count` alignment +precondition, even though they transfer only the active logical extent. + +Conversions between public value types are explicit: + +| Source | Destination | Contract | +| --- | --- | --- | +| PartialRegister | Register with the same `element_t` and width | `to_register()` returns the complete register whose high lanes are the known zero suffix. | +| Register with the same `element_t` and width | PartialRegister | `from_register()` copies the low logical prefix and discards every remaining lane. | +| SimdVector | PartialRegister | No direct conversion. Callers stage through `to_array()` and `from_array()` so the width and logical extent remain explicit. | +| PartialRegister | SimdVector | No direct conversion. Callers stage through `to_array()` and an explicitly matching SimdVector construction path. | + +`NativePartialRegister` is the target-selected +alias. It selects 256 bits only when that width is available and the active +payload crosses into its upper 128-bit group; otherwise it selects an available +valid 128-bit specialization. + +`Aliases.h` also provides `partial_[u]int{8,16,32,64}x{native-lane-count}` +alias templates. The 128-bit spellings are available under SSE4.2 and the +256-bit spellings are additionally gated by AVX2; each takes the logical active +lane count as its sole template argument. + +`PartialRegister` is audited through the same `IRegister` structural concept +family as `Register`. Those concepts describe the shared register-shaped API +while each concrete type retains its own logical-lane contract. Raw-byte +transfer concepts therefore use `lane_count * sizeof(element_type)` as their +logical extent. + +`SimdLib::Register` owns both register-shaped public types. It already carries +the C++23 explicit-object and compiler-boundary requirements needed by +`PartialRegister`, so a separate CMake interface target would only duplicate +the same contract. The core `SimdLib::SimdLib` target remains C++20. + +## Evaluation rule + +For a lane-preserving value operation, the active result is the corresponding +`Api` result for the active lanes. Inactive inputs are not logical operands. +The implementation must therefore supply safe inactive native operands where +the `Api` instruction would otherwise observe an invalid value, and it must +clear every inactive result lane before constructing a PartialRegister result. + +The following terms are used in the ledger: + +| Term | Meaning | +| --- | --- | +| Clean | The operation is zero-closed for inactive inputs. It may construct directly from its Api result after that property is qualified for every supported cell. | +| Project | Clear the inactive result suffix after the Api operation. | +| Neutralize | Replace inactive input lanes with the operation's neutral, valid values before the Api operation, then project the result. | +| Logical | Apply the operation only to the documented logical prefix, rather than reducing or selecting complete native lanes. | +| Re-map | Produce a PartialRegister result with the documented destination active count and clear its inactive suffix. | +| Complete result | Return the existing complete Register result type because the intrinsic result layout is sparse or not a logical prefix. | + +Every returned `PartialRegister` preserves the invariant. Where Register +documents an active result lane as unspecified, PartialRegister retains that +same specified-versus-unspecified status for the corresponding logical lane; +it never permits an inactive lane to be unspecified or nonzero. + +## Constructors, transfer, and observation + +| Register surface | PartialRegister contract | Ledger action | +| --- | --- | --- | +| `zero()` | All physical lanes are zero. | Clean | +| `broadcast(value)` | Writes `value` to the logical prefix and zero to the inactive suffix. | Project | +| `from_lanes(lanes...)` | Requires exactly `lane_count` arguments in low-to-high logical order. | Project | +| `from_array(source)` | Requires `std::array`. | Project | +| `load(source)` | Consumes exactly `std::span`. | Project | +| `load_aligned(source)` | Same logical extent as `load`; pointer alignment remains `byte_count`. | Project | +| `load_bytes(source)` | Consumes exactly `std::span`. | Project | +| `store(destination)` | Produces exactly `lane_count` elements. | Logical | +| `store_aligned(destination)` | Same logical extent as `store`; pointer alignment remains `byte_count`. | Logical | +| `store_bytes(destination)` | Produces exactly `active_byte_count` bytes. | Logical | +| `to_array()` | Returns `std::array` in low-to-high logical order. | Logical | +| `lane()` | Requires `index < lane_count`. | Logical | +| `with_lane(replacement)` | Requires `index < lane_count`; replaces only that logical lane. | Project | + +## Arithmetic and specialized operations + +| Register surface | PartialRegister contract | Ledger action | +| --- | --- | --- | +| binary `operator+`, binary `operator-`, `operator*`, unary `operator-` | Operate pairwise over logical lanes and return the same PartialRegister specialization. | Clean | +| `operator/`, `operator%` | Operate pairwise over logical lanes. Inactive divisors are replaced with one before calling Api so they cannot create an inactive divide-by-zero or remainder-by-zero path. | Neutralize | +| `min(rhs)`, `max(rhs)` | Retain the Register operation's active-lane intrinsic semantics. Zero inactive inputs remain zero, so no suffix projection is required. | Clean | +| `absolute()`, `sqrt()`, `average(rhs)`, `multiply_add(rhs, addend)`, `add_saturated(rhs)`, `subtract_saturated(rhs)`, `add_subtract(rhs)` | Retain the Register operation's active-lane intrinsic semantics and return the same PartialRegister specialization. | Project | +| `magnitude()` | Retains the Register operation's documented 128-bit-group semantics for the logical prefix. Inactive lanes are zero inputs, do not contribute to group values, and are cleared after the operation. Register-specified sparse logical lanes retain their documented status. | Project | +| `magnitude_checked()` | Returns `partial_magnitude_checked_result_t`, whose logical prefix ends after the magnitude and overflow lane of the final occupied 128-bit group. Intermediate lanes retain the underlying API's documented unspecified status. It becomes `Register` when the final required status lane fills the native result. | Re-map | +| `normalize()` | Computes each occupied 128-bit group's magnitude through `Api` from zero-padded inputs, then clears the suffix. Every permitted 256-bit geometry occupies both physical groups, so there is no wholly inactive group requiring a fabricated divisor. Floating-environment status remains subject to the selected compiler's floating-point model, matching the existing `Register` contract. | Project | +| `horizontal_add(rhs)`, `horizontal_subtract(rhs)`, `horizontal_add_saturated(rhs)`, `horizontal_subtract_saturated(rhs)` | Retain the underlying intrinsic order, expose its low `lane_count` output lanes as the logical result prefix, and clear the suffix. | Project | +| `multiply_add_adjacent(rhs)` | Normally returns a contiguous promoted result with `ceil(lane_count / 2)` logical lanes; a final unmatched source lane is paired with zero. For 256-bit 64-bit sources, the intrinsic instead places per-group results in physical lanes zero and two, so the result is a complete `Register` preserving that sparse layout. | Re-map or complete result | +| `multiply_add_unsigned_signed_bytes(rhs)` | Returns a contiguous signed 16-bit result with `ceil(active_byte_count / 2)` logical lanes; a final unmatched byte is paired with zero. | Re-map | +| `sum_absolute_byte_differences(rhs)` | Returns a contiguous unsigned 64-bit result with `ceil(active_byte_count / 8)` logical lanes; incomplete final eight-byte groups use zero for their inactive input bytes. | Re-map | +| `multi_sum_absolute_byte_differences(rhs)` | Returns the existing `multi_sad_result_t` complete Register type. Its immediate-selected output layout is not a contiguous logical-prefix layout. Inactive source bytes are zero. | Complete result | +| `min_position()`, `max_position()` | Consider only logical lanes and return an index in `[0, lane_count)`, choosing the first logical occurrence on ties. | Logical | +| `dot_product(rhs)` | Retains Register's immediate-controlled active-lane semantics and rejects immediate controls that select an inactive output lane. That restriction itself guarantees a zero suffix, so no redundant projection is performed. | Clean | + +`normalize()` retains `Register`'s absence of a checks-enabled precondition: +zero-magnitude active groups follow the selected floating-point behavior and +are covered as an exceptional-result case, while the inactive suffix is still +projected to bitwise zero. Division and modulus retain their active-lane +nonzero and signed-minimum preconditions; checks-enabled coverage first proves +that inactive zero divisor lanes are neutralized and then triggers each active +failure case. + +The promoted type in `multiply_add_adjacent()` is the same type selected by +`multiply_add_adjacent_result_t`; the distinct logical-result alias is +`partial_multiply_add_adjacent_result_t`. The other two contiguous specialized +results similarly use `partial_byte_multiply_add_result_t` and +`partial_sad_result_t`. Checked magnitude uses +`partial_magnitude_checked_result_t` so an occupied group's overflow lane is +never discarded merely because its final source lane was the group's first, +without exposing source-prefix lanes beyond the final defined status lane. +Each alias selects `PartialRegister` while an inactive target suffix remains +and the result crosses every required physical 128-bit group. It selects +`Register` when the meaningful result count fills the target register or when +a 256-bit result is sparse or would otherwise be confined to the low 128-bit +group. This avoids discarding a valid final result lane or forming a forbidden +`PartialRegister` specialization. `multi_sad_result_t` +intentionally remains complete because its immediate-selected output layout is +not a contiguous prefix. + +## Bitwise, masks, and comparisons + +| Register surface | PartialRegister contract | Ledger action | +| --- | --- | --- | +| `operator&`, `operator|`, `operator^`, `andnot(rhs)` | Operate over the logical prefix and return the same PartialRegister specialization. | Clean | +| `operator~` | Complements logical lanes and clears the inactive suffix. | Project | +| `movemask()` | Returns the underlying native-granularity mask with every bit sourced solely from active bytes; high result bits are zero. | Logical | +| `lane_sign_bits()` | Returns one sign bit for each logical lane; unused high bits are zero. | Logical | +| `compare_equal(rhs)`, `compare_greater(rhs)`, `compare_greater_equal(rhs)`, `compare_less(rhs)`, `compare_less_equal(rhs)` | Return `PartialRegisterMask` with false inactive predicate lanes. | Project | +| `operator==`, `operator!=` | Compare logical lanes only. Floating NaN and signed-zero behavior matches Register for those lanes. | Logical | + +`PartialRegisterMask` has one +native predicate register, the same compile-time active prefix, and an +all-zero inactive suffix. Its RegisterMask-compatible surface is `any()`, +`all()`, `none()`, `bits()`, `operator&`, `operator|`, `operator^`, +`operator~`, and `select(when_true, when_false)`. `all()` considers only the +logical prefix. `operator~` clears its inactive suffix, and `select()` always +returns a projected PartialRegister. The mask keeps RegisterMask's deliberate +absence of compound assignment operators. + +## Shift operations + +| Register surface | PartialRegister contract | Ledger action | +| --- | --- | --- | +| `operator<<(count)`, `logical_shift_right(count)`, `operator>>(count)` | Apply the corresponding per-lane Register behavior to logical lanes. Existing count preconditions and signedness behavior are retained. | Clean | +| `shift_bytes_left_slow(count)`, `shift_bytes_left()` | Shift the active byte payload toward higher logical byte indices. Bytes shifted beyond `active_byte_count` are discarded; newly introduced low bytes are zero. | Logical | +| `shift_bytes_right_slow(count)`, `shift_bytes_right()` | Shift the active byte payload toward lower logical byte indices. Bytes shifted beyond the low logical boundary are discarded; newly introduced high logical bytes are zero. | Logical | +| `shift_bits_left_slow(count)`, `shift_bits_left()` | Shift the active bit payload toward higher logical bit indices. Bits shifted beyond `active_byte_count * CHAR_BIT` are discarded. | Logical | +| `shift_bits_right_slow(count)`, `shift_bits_right()` | Shift the active bit payload toward lower logical bit indices. Bits shifted beyond the low logical boundary are discarded. | Logical | + +The slow and immediate forms retain Register's width, availability, and count +constraints. Their logical payload extent replaces Register's complete native +byte or bit extent. + +## Rearrangement and conversion operations + +| Register surface | PartialRegister contract | Ledger action | +| --- | --- | --- | +| `lower_half()` | A valid 256-bit PartialRegister necessarily has more than 128 active bits, so extraction always returns the complete `Register` low half. Counts below or equal to the destination capacity cannot produce a partial result under the source type's upper-half constraint; counts above it are truncated to the complete low half. | Re-map | +| `unpack_low(rhs)`, `unpack_high(rhs)` | Retain the underlying intrinsic order and expose its low `lane_count` output lanes as the logical result prefix. `unpack_low` clears values that can spill into the suffix; `unpack_high` is zero-closed for active-prefix operands and needs no cleanup. | Project | +| `shuffle()` | Requires exactly `lane_count` selectors and every selector to be less than `lane_count`; returns the selected logical prefix. | Logical | +| `shuffle_bytes()` | Requires exactly `active_byte_count` selectors and every selector to be less than `active_byte_count`; returns the selected logical byte prefix. | Logical | +| `shuffle_low()`, `shuffle_high()` | Retain Register's immediate selector semantics where they affect logical lanes, then clear the suffix. | Project | +| `blend(rhs)` | Retains Register's immediate selector semantics for logical lanes, then clears the suffix. | Project | +| `bit_cast()` | Requires `active_byte_count` to be divisible by `sizeof(target_t)` and returns a partial or complete result exposing `active_byte_count / sizeof(target_t)` lanes. Because bit-casting preserves the active bit extent, every valid 256-bit source also produces a result whose active extent reaches the upper half. | Re-map | +| `convert()` | Returns a partial or complete target with one meaningful target lane per active source lane for the same supported Api conversion cells. | Re-map | +| `widen_low()` | Returns a partial or complete target exposing `min(lane_count, Api::element_count)` consumed source lanes. A derived 256-bit result that would leave its upper half entirely inactive is unavailable. Register exposes no narrowing counterpart, so PartialRegister does not add one. | Re-map | + +Any derived result that fills its destination register is represented by the +corresponding complete `Register`. Any derived partial result must satisfy +`PartialRegisterAvailable`, including the 256-bit upper-half rule. + +## Surface and performance commitments + +Every counterpart preserves Register's static/member form, template parameter +order, `[[nodiscard]]`, `constexpr`, `noexcept`, `SIMD_FLAGS`, and Api-based +availability requirements unless this ledger explicitly changes a logical +extent, selector domain, partial result type, or partial-only precondition. + +PartialRegister also preserves Register's deliberate absence of +`operator+=`, `operator-=`, `operator*=`, `operator/=`, `operator%=`, +`operator&=`, `operator|=`, `operator^=`, `operator<<=`, and `operator>>=`. + +The accepted performance baseline is one native register passed and returned +with the same calling-convention intent as Register. A PartialRegister wrapper +may emit the instructions required to build safe inactive operands or to clear +the inactive suffix. It may not introduce allocations, runtime lane-count +loads, proxy objects, hidden aggregate members, or wrapper-only work unrelated +to the documented invariant. diff --git a/docs/PartialRegisterQualification.md b/docs/PartialRegisterQualification.md new file mode 100644 index 0000000..29d32b3 --- /dev/null +++ b/docs/PartialRegisterQualification.md @@ -0,0 +1,320 @@ +# PartialRegister design and qualification + +## Scope + +This document is the durable design and qualification record for +`PartialRegister` and +`PartialRegisterMask`. The declaration-level contract is maintained in +[PartialRegisterOperationLedger.md](PartialRegisterOperationLedger.md). +Generated-code ownership and accepted-difference policy are recorded below; +the executable symbol inventories and exact instruction differences are +generated by the validation gates. + +Individual compiler runs remain generated artifacts under `out/pipeline`; the +execution record below identifies the exact artifacts used for the current +qualification without treating generated files as source-controlled truth. + +## Approved contract + +`PartialRegister` is an independent, final, standard-layout aggregate. It does +not derive from `Register`. Each specialization stores exactly one +`Api::vector_t`, with no base class, allocation, +runtime lane count, proxy, or second register. The compile-time active lanes +form one contiguous low-lane prefix. Every inactive lane has the all-bits-zero +representation, including positive zero for floating-point elements. + +A specialization is available when the corresponding Register cell exists, +the active count is nonzero and strictly less than the native lane count, and a +256-bit active payload crosses the 128-bit-group boundary. The last restriction +avoids representing a wholly low-half value with an unnecessarily 256-bit +partial type. `Register` remains the preferred spelling when every native lane +is active. + +Public element and byte transfers use the logical active extent. Native imports +through `from_native()` and `from_register()` sanitize the inactive suffix; +`to_native()` and `to_register()` expose a known-clean value. Direct aggregate +initialization retains the documented precondition that the supplied native +value is already clean, and checked observation boundaries validate it. + +## Result and invariant rules + +Every operation is classified in the operation ledger. The implementation uses +five strategies: + +| Strategy | Rule | +| --- | --- | +| Clean | Construct directly from an `Api` result proven to map zero inactive inputs to zero inactive outputs. | +| Project | Clear the inactive suffix after an operation that can populate it. | +| Neutralize | Replace inactive inputs with safe neutral values before the `Api` call, then project the result. | +| Logical | Observe or move only the active prefix and never expose inactive lanes. | +| Re-map | Return the documented partial or complete target type for a changed lane geometry. | + +Lane-preserving operations return the same specialization unless the ledger +documents another result. Comparisons return `PartialRegisterMask`; mask +reductions inspect active lanes only. Bit casts preserve active bits, numeric +conversions preserve meaningful source lanes, and widening consumes the +documented low source prefix. A derived result uses `Register` when it fills the +destination register or when the hardware result layout is sparse rather than a +contiguous prefix. In particular, 256-bit 64-bit adjacent multiply-add results +retain their sparse physical lanes in a complete `Register`. + +Division and modulus neutralize inactive divisors so padding cannot trigger a +precondition failure. Logical byte and bit shifts operate over the active +payload. Rearrangements constrain selectors to the active logical domain and +project any suffix that could be populated. Scalar reductions, equality, and +position operations ignore inactive lanes. + +## Public usage + +Construction, arithmetic, comparison, selection, rearrangement, conversion, +and logical-extent storage can be composed without naming `Api`, `native`, or +`Detail`: + +```cpp +#include + +#include +#include + +using Tail = SimdLib::PartialRegister; + +const auto source = Tail::from_array(std::array{10, 20, 30}); +const auto sum = source + Tail::broadcast(5); +const auto selected = sum.compare_less(Tail::broadcast(30)) + .select(sum, Tail::zero()); +const auto reordered = selected.shuffle<2, 0, 1>(); +const auto floats = reordered.convert(); +const auto bits = floats.bit_cast(); + +std::array destination{}; +bits.store(destination); +``` + +Widening has its own result geometry: + +```cpp +using Source = SimdLib::PartialRegister; +const auto widened = Source::from_lanes(1, 2, 3, 4, 5, 6, 7) + .widen_low(); +``` + +The technical reference distinguishes this fixed active-prefix value from a +complete `Register`, a fixed logical `SimdVector`, and collection-owned dynamic +tail handling. + +## Supported matrix + +| Dimension | Qualified cells | +| --- | --- | +| Architecture | x86-64 | +| Width and ISA | 128-bit under SSE4.2; 256-bit additionally under AVX2 | +| Elements | Signed and unsigned 8-, 16-, 32-, and 64-bit integers; `float`; `double` | +| Windows | MSVC 19.44, clang-cl 20.1.8, and clang-cl 22.1.7, C++23 register interface | +| Linux | GCC 14.2 and Clang 22.1 on pinned Alpine/musl images | +| Core-only compatibility | GCC 13.2 compiles the C++20 umbrella with the register interface unavailable | +| Sanitizers | Clang 22 Debug with ASan and UBSan | +| Optimized code generation | Release wrapper/raw enforcement for every supported register compiler and both ISA profiles | + +The exact observed patch versions and executable identities belong to the +execution record. Compiler floors are changed only after the complete +correctness, package, ABI, and generated-code matrix passes. + +## Evidence ownership + +| Contract area | Direct active-lane evidence | Direct inactive-lane evidence | Additional owner | +| --- | --- | --- | --- | +| Construction, transfers, observations | PartialRegister runtime matrix, canaries, compile-failure extents | Bitwise suffix checks for every supported geometry | Header, ODR, and installed consumer probes | +| Arithmetic and reductions | Independent scalar-oracle arithmetic and specialized suites | Suffix checks and neutral-divisor checks | Checks-enabled precondition executable | +| Bitwise, shifts, and comparisons | Runtime and constexpr operation matrices | NOT, shifts, predicates, and selection explicitly verify zero/false suffixes | PartialRegisterMask generated-code record | +| Rearrangement and conversion | Exhaustive selectors and source/target matrices | Result suffix checked for every supported result geometry | General generated-code record | +| Object model and ABI | Layout matrix and 64 paired boundary symbols per ISA profile | Imports sanitize and checked boundaries validate | ABI wrapper/raw record | +| Performance-sensitive expressions | Behavioral suites remain authoritative | Raw mirrors perform identical required invariant work | Generated records, symbol inventories, and instruction-difference sidecars | + +Compile-time surface matrices compare the complete applicable Register surface +with the partial sibling and then apply the ledger's partial-only extent and +result constraints. Each generated comparison record owns its selected symbol +set, wrapper/raw profiles, validator, and instruction-difference sidecar. + +## Generated-code ownership + +The raw fixtures perform the same active-lane masking, inactive-divisor +neutralization, result projection, and normalization required by the public +wrapper. A raw expression that omits required invariant work is not an +acceptable baseline. + +| Fixture pair | Contract | Profiles | Gate | +| --- | --- | --- | --- | +| `PartialRegisterMaskCodegen.cpp` / `PartialRegisterMaskCodegenRaw.cpp` | Predicate composition, native import, closed addition, and neutralized division | SSE4.2/128, AVX2/256 | `PartialRegisterMaskCodegen` | +| `PartialRegisterArithmeticCodegen.cpp` / `PartialRegisterArithmeticCodegenRaw.cpp` | Unary and binary arithmetic, division/modulus, reductions, and specialized and type-changing results | SSE4.2/128, AVX2/256 | `PartialRegisterArithmeticCodegen` | +| `PartialRegisterGeneralCodegen.cpp` / `PartialRegisterGeneralCodegenRaw.cpp` | Construction, active-extent transfer, bitwise work, shifts, comparison and mask use, rearrangement, conversion, and composition | SSE4.2/128, AVX2/256 | `PartialRegisterGeneral` | +| `PartialRegisterAbi.cpp` / `PartialRegisterAbiRaw.cpp` | Value parameters and returns, reassignment, mask/scalar/type-changing results, opaque calls, and register pressure | SSE4.2/128, AVX2/256 | `PartialRegisterAbi` | + +All four gates are strict Release `RegisterCodegen` dependencies. The ABI +fixtures cover every available active count for all ten element types and +assert native-vector size and alignment. Each width owns 56 type/count identity +boundaries plus eight named ABI shapes, for 64 wrapper/raw symbol pairs per ISA +profile. + +## Generated-code exception policy + +Every optimized comparison runs in `ENFORCE` mode. Exact parity is preferred. +A compiler-specific difference is accepted only when the complete normalized +wrapper and raw profiles match pinned SHA-256 values. Each accepted record owns +the compiler identity and version, ISA profile, symbol mapping, +`instruction-differences.txt`, reason identifier, and validation test. A changed +instruction, symbol set, or profile hash fails the build rather than silently +refreshing the exception. + +`cmake/development/PartialRegisterCodegenProfiles.json` is the authoritative +configuration for retained PartialRegister differences. Each entry identifies +the compiler and accepted versions, profile, register width, ISA, reason, and +wrapper/raw SHA-256 values. Configuration fails on malformed schema members, +invalid hashes, or multiple entries matching the active compiler cell; the +CMake gate contains selection and validation logic but no retained hash data. + +MSVC accepts only the following hash-pinned difference categories: + +| Exception | Exact instruction categories | +| --- | --- | +| `msvc-gs-predicate-composition-cookie` | `/GS` prologue, check call, and epilogue around predicate composition | +| `msvc-equivalent-import-normalization-gs-and-value-operation-allocation` | Import normalization and `/GS` frame shape, equivalent unaligned moves, and scalar-division scheduling/register allocation | +| `msvc-equivalent-unaligned-moves-and-register-allocation` | Equivalent unaligned moves, commutative operand choice, and temporary-register allocation | +| `msvc-equivalent-vector-moves-mask-materialization-register-allocation-and-gs-cookie` | `/GS`, equivalent unaligned moves, memory-versus-register mask materialization, and operand allocation | +| `msvc-partial-invariant-boundary-normalization-and-gs-cookie` | Required inactive-suffix normalization at checked boundaries plus `/GS` sequences | + +Clang-family exceptions are limited to pinned commutative operand selection, +register selection, and scalar division/modulus scheduling. GCC raw mirrors +preserve wrapper operand roles for commutative operations so GCC 14 requires +exact arithmetic parity instead of retaining fixture-induced moves or +scheduling differences. Exact affected symbols and instructions live in each +record's generated `symbols.txt` and `instruction-differences.txt`; no blanket +record-only optimized cell is part of the qualification. + +## Validation procedure + +The final tree is validated in this order: + +1. Run repository tooling validation and the public-consumer boundary check. +2. Run formatting and static analysis over every changed C++ production, test, + code-generation, example, and benchmark source. +3. Build and test the complete native MSVC, clang-cl, and coverage selection. +4. Build and test the pinned GCC 13, GCC 14, Clang 22, and Clang 22 ASan/UBSan + container selection. +5. Inspect the installed header set and downstream PartialRegister consumer. +6. Build and run supplemental benchmarks only after every correctness and + strict generated-code gate is green. +7. Run final tooling validation, ownership/stale-reference audits, and + `git diff --check`. + +The canonical commands are repository wrappers documented in +[BuildPipeline.md](BuildPipeline.md) and [ContainerValidation.md](ContainerValidation.md). +Test operations consume source-bound build receipts and reject stale artifacts; +benchmark operations similarly require completed validation and benchmark-build +manifests. + +## Qualification execution record + +The qualified source digest is +`f95d3d754dd4eb4b1455beed169a88861e2ae9f852bb34946abcfefafe46f7a1`. +The repository wrappers were invoked with PowerShell array binding: + +```powershell +& './tools/Build.ps1' -Scope Native -Compiler @('Msvc', 'ClangCl', 'ClangCoverage') +& './tools/Run-Tests.ps1' -Scope Native -Compiler @('Msvc', 'ClangCl', 'ClangCoverage') +& './tools/Build.ps1' -Scope Containers -Compiler @('Gcc13', 'Gcc14', 'Clang22') +& './tools/Run-Tests.ps1' -Scope Containers -Compiler @('Gcc13', 'Gcc14', 'Clang22') +``` + +Both builds and both test operations exited successfully. Native build receipt +`out/pipeline/provenance/build-b843ea241ca9693b.json` binds the qualified cell +roots and manifests. Their `provenance/fingerprint.json`, +`provenance/validation-build.manifest`, and `reports/main-test.xml` files record +MSVC 19.44 (toolset 14.51.36252.0), clang-cl 22.1.8, and these results: MSVC +Release 347/347 plus 2/2 consumer tests, MSVC Debug 278/278, clang-cl Release +350/350 plus 2/2 consumer tests, and Clang coverage 320/320. Coverage mapped 318 +profiles to 25 executables with none excluded; its artifacts are +`windows-clang-coverage/debug-coverage-e7e9b8a32e45e5fb/build/coverage.info` +and `coverage-provenance.tsv` beneath `out/pipeline`. + +Container build receipt `out/pipeline/provenance/build-a127b3f977182090.json` +binds the four qualified roots and manifests. Their +`provenance/environment.txt`, `provenance/validation-build.manifest`, and +`reports/main-test.xml` files record GCC 13.2.1, GCC 14.2.0, and Clang 22.1.3 +in the pinned Alpine images, with CMake 4.4.0. GCC 13 Release passed 232/232 +plus 1/1 consumer test; GCC 14 Release and Clang 22 Release each passed 350/350 +plus 2/2 consumers. Clang 22 Debug passed 320/320 under ASan and UBSan with no +sanitizer or runtime-error diagnostic. + +Every GCC 14 and Clang 22 PartialRegister record is `exact-parity`, has no +exception, and runs in `ENFORCE` mode. Clang-cl has six exact records and four +hash-pinned compiler exceptions. MSVC has ten hash-pinned compiler exceptions. +All twenty Windows records and all twenty Linux records passed their owning +validators; exact symbols and deltas remain in the sidecars described above. + +The installed-package build owns installation, downstream configuration, and +downstream compilation; CTest only executes the two prebuilt consumers. The +MSVC installed prefix contains 26 `SimdLib` public files and three CMake package +files. It includes `PartialRegister.h`, `PartialRegisterFwd.h`, +`PartialRegisterMask.h`, `IPartialRegisterMask.h`, `Aliases.h`, and the umbrella +header. Installed-header ODR, installed-package PartialRegister consumption, +and external consumers passed on the final tree. + +Clang-format 22.1.8 accepted all 73 changed C++ files. Clang-tidy parsed all 35 +changed translation units represented in the analysis compilation database; +the remaining entries are intentional compile-failure fixtures or separately +built consumers. Its warnings were reviewed: intrinsic portability, +`#pragma once`, and Catch registration dominate; the remaining findings were +intentional low-level semantics or harmless test-helper suggestions, with no +confirmed defect. Both public documentation snippets compiled as C++23 with +clang-cl `/W4 /WX` using only the public include directory. Evidence is under +`out/qualification/partial-register-static-analysis`. + +The current tooling receipt is +`out/pipeline/provenance/pipeline-validation-1822737a5dbb61cd.json`; it covers +the 22-cell matrix, eight operations, and 46 public-consumer sources. The +repository's digest cache reused that receipt when the validation, public +consumer, matrix, and pipeline-regression commands all passed again after the +benchmark executions. The post-benchmark `git diff --check` was also clean. +Ownership, +registration, stale-name, declaration/ledger, generated-symbol, and installed +header audits found no unresolved discrepancy. Supplemental benchmark commands +and artifacts are recorded below after the already-green correctness matrix. + +### Supplemental benchmark receipt + +After all validation cells passed, these commands both exited successfully: + +```powershell +& './tools/Build-Benchmarks.ps1' -Scope All +& './tools/Run-Benchmarks.ps1' -Scope All +``` + +Complete `benchmark-build.manifest` files and +`reports/benchmark-execution.txt` artifacts exist in the MSVC, clang-cl, +GCC 13, GCC 14, and Clang 22 Release roots. GCC 13 is intentionally core-only +and has no PartialRegister cases. The other four artifacts contain seven +PartialRegister measurements with 25 samples each. Means in nanoseconds are: + +| Compiler | 128 add | Raw 128 add | 256 add | Raw 256 add | 256 divide | Compare/select | Shuffle | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| MSVC 19.44 | 0.435451 | 1.18814 | 1.62896 | 0.914979 | 9.70701 | 2.24395 | 0.425957 | +| clang-cl 22.1.8 | 0.462351 | 0.275849 | 0.354168 | 0.348874 | 6.57357 | 0.431666 | 0.316893 | +| GCC 14.2 | 0.290716 | 0.295982 | 0.344326 | 0.366258 | 9.61257 | 0.487729 | 0.236518 | +| Clang 22.1.3 | 0.322882 | 0.345057 | 0.307846 | 0.415625 | 6.03566 | 0.623137 | 0.245278 | + +These single-host Catch2 microbenchmarks are supplemental. Sub-nanosecond rows, +high-variance observations, and estimated zero-duration batches prevent stable +absolute-latency or speedup claims. They are not performance thresholds and do +not override the correctness or generated-code evidence. + +## Supported limitations + +- The public register-value interface requires the supported C++23 explicit-object + implementation; the core library remains C++20. +- Only x86-64 SSE4.2 128-bit and AVX2 256-bit cells are qualified. AVX-512, + 512-bit values, other architectures, and 32-bit targets are unsupported. +- A 256-bit partial specialization must have active data in both 128-bit groups. +- Dynamic collection tails remain owned by collection algorithms; the active + count of a `PartialRegister` is compile-time state. +- Benchmarks are supplemental measurements with no pass/fail speed threshold and + never override correctness, sanitizer, ABI, or generated-code results. diff --git a/docs/PublicNamespace.md b/docs/PublicNamespace.md index b55b7fc..2a48d56 100644 --- a/docs/PublicNamespace.md +++ b/docs/PublicNamespace.md @@ -33,8 +33,11 @@ the rename preserves the complete member API rather than selecting a subset. | Automatically sized complete-register value | `SimdLib::NativeRegister` | | Explicit-width complete-register value | `SimdLib::Register` | | Complete-register predicate value | `SimdLib::RegisterMask` | +| Automatically sized partial-register value | `SimdLib::NativePartialRegister` | +| Explicit-width partial-register value | `SimdLib::PartialRegister` | +| Partial-register predicate value | `SimdLib::PartialRegisterMask` | | Fixed logical SIMD value | `SimdLib::SimdVector` | -| Fixed-width complete-register aliases | C++23 root `SimdLib::*x*` and `SimdLib::Vector*` aliases | +| Fixed-width register aliases | C++23 root complete-register aliases and `partial_*` alias templates | | Bit manipulation | `SimdLib::Bmi` | | Unsigned wide integer | `SimdLib::uint128_t` | | Byte-mask resampling | `SimdLib::SimdResample` | @@ -51,6 +54,21 @@ preferred entry point when consumers do not require a fixed register width. Explicit `Register` is required when storage layout or an ABI contract must remain stable across target configurations. +`PartialRegister` is the sibling +value type for one native register whose contiguous low-lane prefix is logical +data and whose remaining lanes are always all-bits-zero. Use +`NativePartialRegister` when target-selected width +is appropriate, and use the explicit-width spelling at storage or ABI +boundaries. `PartialRegisterMask` provides the matching active-prefix predicate. + +`SimdVector` remains the fixed logical vector type; +it is not a register-tail policy. Collection operations such as `SimdAlgo` +continue to own iteration and dynamic final-batch handling. In short, +`Register` means every native lane is active, `PartialRegister` means a +compile-time low-lane prefix is active in one native register, `SimdVector` +means one fixed logical value, and collection algorithms decide how a sequence +is divided into complete and partial work. + `NativeApi` remains the preferred backend facade for C++20, collection helpers, compatibility code, and specialized low-level operations. It selects the 256-bit facade when the compile target enables it and otherwise diff --git a/docs/RegisterCodegenAudit.md b/docs/RegisterCodegenAudit.md deleted file mode 100644 index 0ae3cb3..0000000 --- a/docs/RegisterCodegenAudit.md +++ /dev/null @@ -1,174 +0,0 @@ -# Permanent Generated-Code Suite Audit - -This document defines the ownership and retention policy for SimdLib's permanent -generated-code fixtures. The machine-readable, per-symbol decision ledger is -[`RegisterCodegenSymbolAudit.csv`](RegisterCodegenSymbolAudit.csv). - -## Contract categories - -Every retained symbol belongs to exactly one category: - -| Category | Permanent observable contract | -|---|---| -| Public abstraction parity | A public `Register` operation adds no work relative to the matching public `Api` operation. | -| ABI boundary | A non-inlined `Register`, `RegisterMask`, explicit-object mirror, or native-vector signature preserves the documented calling boundary. | -| Compiler-attribute enforcement | `SIMD_FLAGS(...)` and the legacy declaration attributes produce the same ABI and generated code, including inlining and stack restrictions. | -| Instruction-property guarantee | A feature mode or immediate form retains a required instruction property, such as fused multiply-add presence or absence. | -| Composed-expression optimization | Multiple public operations optimize as one expression without wrapper temporaries or repeated work. | -| Register-pressure behavior | Simultaneously live values and opaque calls do not introduce wrapper-specific spills or reloads. | -| Explicitly diagnostic evidence | The artifact records compiler behavior but is excluded from zero-overhead pass/fail claims. | - -The ledger has one row for each source-level fixture symbol. Force-inline and -flatten helper symbols are intentionally absent from optimized objects. Reuse of -a symbol name across width or ISA configurations is represented by its `applicability` -field. Feature-mode comparisons that deliberately compile the same symbol twice, -such as FMA enabled and disabled, identify both records in that row. - -## Retained symbol ownership - -| Owning fixture | Symbols | Category coverage | Distinct purpose | -|---|---:|---|---| -| `RegisterCodegenFixture.h` | 23 | Public parity, composition, instruction property, register pressure | Protects expression and lifetime behavior that an isolated operation cannot represent. | -| `RegisterTypeMatrixCodegenFixture.h` | 442 | Public parity | Canonical isolated operation matrix over every supported element type, register width, and ISA profile. | -| `RegisterSpecializedCodegenFixture.h` | 138 | Public parity | Covers specialized arithmetic and reduction methods that are absent from the basic type matrix. | -| `RegisterFmaCodegenFixture.h` | 2 | Instruction property | Isolates the two multiply-add symbols so FMA presence and absence cannot be satisfied by unrelated code. | -| `RegisterRearrangementCodegenFixture.h` | 181 | Public parity | Covers immediate selectors, complete-register shuffles, bit casts, numeric conversions, lower halves, and widening cells. | -| `RegisterAbi.cpp` | 12 | ABI boundary | Separates explicit-object signature mirrors from real downstream `Register` and `RegisterMask` boundaries. | -| `RegisterDefaultAbi.cpp` | 1 | Explicitly diagnostic evidence | Records the platform-default aggregate convention without treating it as a supported zero-overhead boundary. | -| `MethodFlagsFlagged.cpp` | 11 | Compiler-attribute enforcement | Compares `SIMD_FLAGS(...)` with equivalent raw compiler attributes and checks inlining and stack restrictions. | - -The total is 810 retained source-level symbols. The CSV ledger is authoritative -for individual decisions; the table above is only a fixture summary. - -## Raw-baseline policy - -Public zero-overhead fixtures compare `Register` with the narrowest equivalent -public `Api` expression. A raw translation unit must not call `Register`, an -implementation specialization, or an extension helper. Sharing the production -implementation beneath the two public layers is intentional: the independent -boundary under test is the `Register` abstraction itself. - -ABI fixtures instead compare aggregate signatures with native-vector signatures. -Method-flag fixtures compare `SIMD_FLAGS(...)` declarations with equivalent -raw compiler-attribute declarations. The platform-default ABI fixture is a paired -diagnostic recording rather than an equality gate. - -## Comparison records and owning validation - -Each record appears exactly once in its profile's generated -`all-records.txt`. The profile also writes disjoint `enforced-records.txt` and -`diagnostic-records.txt` indexes. Release validation requires every enforced -record to report `ENFORCE`; a record-only result can appear only in the -diagnostic index and cannot satisfy that gate. `RegisterExpressionCodegen` -and `RegisterConsumerAbi` are build-only orchestration targets and do -not own validation. - -| Record | Symbol selection | Wrapper input | Raw input | Owning validation | -|---|---|---|---|---| -| `primary-composition` | Memory-capable and composed primary symbols | `RegisterCodegen.cpp` | `RegisterCodegenRaw.cpp` | `RegisterCodegen.` | -| `register-only` | Register-only primary symbols | `RegisterCodegen.cpp` | `RegisterCodegenRaw.cpp` | `RegisterCodegen.` | -| `reassignment` | Ordinary reassignment arithmetic | `RegisterCodegen.cpp` | `RegisterCodegenRaw.cpp` | `RegisterCodegen.` | -| `specialized` | All FMA-independent specialized symbols | `RegisterSpecializedCodegen.cpp` | `RegisterSpecializedCodegenRaw.cpp` | `RegisterCodegen.` | -| `fma-disabled` | `multiply_add_f32` and `multiply_add_f64` | `RegisterFmaCodegen.cpp` with FMA disabled | `RegisterFmaCodegenRaw.cpp` with FMA disabled | `RegisterCodegen.` | -| `fma-enabled` | `multiply_add_f32` and `multiply_add_f64` | `RegisterFmaCodegen.cpp` with FMA enabled | `RegisterFmaCodegenRaw.cpp` with FMA enabled | AVX2 `RegisterCodegen.` | -| `rearrangement-conversion` | All applicable rearrangement symbols | `RegisterRearrangementCodegen.cpp` | `RegisterRearrangementCodegenRaw.cpp` | `RegisterCodegen.` | -| `common-type-matrix` | All applicable non-modulus type-matrix symbols | `RegisterTypeMatrixCodegen.cpp` | `RegisterTypeMatrixCodegenRaw.cpp` | `RegisterCodegen.` | -| `modulus-type-matrix` | Integer modulus symbols | `RegisterTypeMatrixCodegen.cpp` | `RegisterTypeMatrixCodegenRaw.cpp` | `RegisterCodegen.` | -| `abi` | Explicit-object ABI mirrors | `RegisterAbi.cpp` | `RegisterAbiRaw.cpp` | `RegisterCodegen.` | -| `consumer-abi` | Real downstream Register and RegisterMask boundaries | `RegisterAbi.cpp` | `RegisterAbiRaw.cpp` | `RegisterCodegen.` | -| `default-abi` | Platform-default aggregate boundary | `RegisterDefaultAbi.cpp` | `RegisterDefaultAbiRaw.cpp` | `RegisterCodegen.` | -| `method-flags` | `SIMD_FLAGS(...)` declaration fixtures | `MethodFlagsFlagged.cpp` | `MethodFlagsRaw.cpp` | `MethodFlagsCodegen` | - -SSE4.2/128 owns 11 Register records because it has no FMA-enabled record. -AVX2/128 and AVX2/256 each own 12. The method-flags comparison is owned by its -single configuration-probe validation. - -Unified native and container runners aggregate only these CMake-owned indexes. -They do not recursively discover residual JSON files in reused build trees, so -retired artifacts cannot acquire validation ownership. Ordinary Debug, -sanitizer, and coverage profiles configure no Register codegen targets or -indexes. Explicit diagnostic profiles contain only record-only codegen targets. - -## Source and build inventory - -| Fixture family | Complete source inventory | -|---|---| -| Primary | `tests/codegen/RegisterCodegen.cpp`, `RegisterCodegenRaw.cpp`, and `RegisterCodegenFixture.h` | -| Specialized | `tests/codegen/RegisterSpecializedCodegen.cpp`, `RegisterSpecializedCodegenRaw.cpp`, and `RegisterSpecializedCodegenFixture.h` | -| FMA | `tests/codegen/RegisterFmaCodegen.cpp`, `RegisterFmaCodegenRaw.cpp`, and `RegisterFmaCodegenFixture.h` | -| Rearrangement | `tests/codegen/RegisterRearrangementCodegen.cpp`, `RegisterRearrangementCodegenRaw.cpp`, and `RegisterRearrangementCodegenFixture.h` | -| Type matrix | `tests/codegen/RegisterTypeMatrixCodegen.cpp`, `RegisterTypeMatrixCodegenRaw.cpp`, and `RegisterTypeMatrixCodegenFixture.h` | -| Explicit-object and consumer ABI | `tests/codegen/RegisterAbi.cpp` and `RegisterAbiRaw.cpp` | -| Platform-default ABI | `tests/codegen/RegisterDefaultAbi.cpp` and `RegisterDefaultAbiRaw.cpp` | -| Method attributes | `tests/method_flags/codegen/MethodFlagsFlagged.cpp` and `MethodFlagsRaw.cpp` | - -`cmake/development/RegisterCodegen.cmake` owns the per-profile object targets, -records, aggregate build targets, policy-separated record indexes, and three -Register CTests. -`cmake/development/MethodFlagsCodegen.cmake` owns the method-flags pair and its -CTest. `CompareRegisterCodegen.cmake`, `RecordRegisterDefaultAbi.cmake`, -`ValidateCodegenRecords.cmake`, `ValidateRegisterCodegenProfile.cmake`, -`VerifyCodegenProfileIsolation.cmake`, `VerifyMethodFlagsCodegen.cmake`, and -`VerifyMethodFlagsCodegenRecords.cmake` are the complete comparison, diagnostic, -record-integrity, profile-isolation, and attribute-verification script inputs. - -Every `` suffix is one of `128Sse42`, `128Avx2`, or `256Avx2`: - -| Target family | Complete generated target inventory | -|---|---| -| Primary objects | `RegisterCodegenWrapper`, `RegisterCodegenRaw` | -| Default ABI objects | `RegisterDefaultAbiWrapper`, `RegisterDefaultAbiRaw` | -| Explicit-object and consumer ABI objects | `RegisterAbiWrapper`, `RegisterAbiRaw` | -| Specialized objects | `RegisterSpecializedWrapper`, `RegisterSpecializedRaw` | -| FMA-disabled objects | `RegisterFmaDisabledWrapper`, `RegisterFmaDisabledRaw` | -| FMA-enabled objects | `RegisterFmaEnabledWrapper`, `RegisterFmaEnabledRaw` for AVX2 profiles | -| Rearrangement objects | `RegisterRearrangementWrapper`, `RegisterRearrangementRaw` | -| Type-matrix objects | `RegisterTypeMatrixWrapper`, `RegisterTypeMatrixRaw` | -| Register orchestration | `RegisterExpressionCodegen`, `RegisterConsumerAbi`, `RegisterCodegen`, and `RegisterCodegen` | -| Method attributes | `MethodFlagsCodegenFlagged`, `MethodFlagsCodegenLegacy`, and `MethodFlagsCodegen` | - -The orchestration targets do not define additional contracts. The complete CTest -inventory is `RegisterCodegen.128Sse42`, `RegisterCodegen.128Avx2`, -`RegisterCodegen.256Avx2`, and `MethodFlagsCodegen`. -## Artifact and documentation inventory - -Register artifacts live below: - -- `register-codegen/sse42/128`; -- `register-codegen/avx2/128`; and -- `register-codegen/avx2/256`. - -Method-attribute artifacts live below `method-flags-codegen`. Default pipeline -publication uses Release roots. `tools/Record-Codegen.ps1` creates a separate -selected Debug or Clang sanitizer record set and dedicated provenance containing -compiler flags, stack-protector mode, disassembly tools, source identity, record -hashes, and separate compilation and comparison timings. - -Documentation references have these roles: - -| Documentation | Role | -|---|---| -| `RegisterQualification.md` | Supported compiler/profile matrix, enforcement policy, and diagnostic exception ledger. | -| `RegisterProposal.md` | Public zero-overhead and ABI requirements. | -| `RegisterImplementationMatrix.md` | Public-operation-to-generated-code traceability. | -| `MethodFlagsContract.md` | Compiler-attribute promises, compiler mappings, and extension policy. | -| `BuildPipeline.md` and `ContainerValidation.md` | Reproduction commands and execution-reporting boundaries. | -| `SimdLibDevelopment.todo`, `TestCoverageExpansion.todo`, and `project.todo` | Active planning and project backlog; not normative pass claims. | -| `README.md` and `wiki/Technical-Reference.md` | User-facing support and performance guidance. | - -## Removed redundant fixtures - -| Removed fixture or symbol family | Redundancy reason | -|---|---| -| `LogicalShuffleCodegenRaw.cpp` and `LogicalShuffleIntrinsic` | Reimplemented the intrinsic algorithm; public `Register::shuffle` versus public `Api::shuffle` is the permanent boundary. | -| Handwritten scalar remainder baselines | Duplicated the selected production algorithm; algorithm comparison belongs in execution evidence or benchmarks. | -| Direct type-matrix implementation-layer runtime extract/insert symbols | Compared `Api` with its implementation rather than testing a public `Register` contract. | -| Primary isolated unary, binary, scalar, mask, construction, transfer, arithmetic, sign-bit, and runtime per-lane shift symbols | Duplicated canonical isolated type-matrix cells. | -| Uninstantiated aggregate `evaluate` and `transfer` helpers | Emitted no permanent contract and added fixture complexity. | -| Specialized fixtures rebuilt under both FMA modes | Revalidated FMA-independent symbols; only isolated multiply-add cells require the mode split. | -| Overlapping lane and full-primary comparison records | Revalidated symbols already owned by narrower nonoverlapping records. | -| Identity-return fixtures for unavailable operation/type cells | Produced code without a supported public operation and could hide availability mistakes. | - -Temporary candidate-implementation comparisons are not permanent fixtures. -Reusable throughput or latency investigations belong in benchmarks; one-time -compiler decisions belong in execution reporting. diff --git a/docs/RegisterCodegenPolicyProposal.md b/docs/RegisterCodegenPolicyProposal.md new file mode 100644 index 0000000..6198e3f --- /dev/null +++ b/docs/RegisterCodegenPolicyProposal.md @@ -0,0 +1,253 @@ +# Register Code-Generation Policy Convergence Proposal + +Status: proposed + +This document proposes a single enforcement model for the `Register` and +`PartialRegister` generated-code suites. It is a decision artifact, not a +second qualification contract. Once the proposal is implemented and the final +policy is incorporated into the qualification documents, this file should be +removed. + +## Decision summary + +Optimized Release comparisons should enforce generated-code expectations by +default for every supported compiler, ISA, width, and fixture profile. A +comparison may pass in one of three ways: + +1. normalized wrapper and raw instruction streams are identical; +2. a narrow semantic recognizer accepts one documented compiler transformation; +3. an exact compiler-qualified wrapper/raw hash pair matches a reviewed retained + difference. + +Record-only comparisons remain useful for deliberately diagnostic +configurations, but they do not satisfy or weaken an optimized zero-overhead +claim. The current blanket SSE4.2 diagnostic classification should therefore be +replaced with per-profile evidence, and broad records should be divided wherever +one compiler behavior currently relaxes unrelated symbols. + +## Problem + +The two generated-code suites currently express similar guarantees through +different policies: + +- `PartialRegister` enforces every optimized comparison and stores reviewed, + compiler-qualified wrapper/raw fingerprints in + `../cmake/development/PartialRegisterCodegenProfiles.json`. +- `Register` enforces AVX2 by default but records the complete SSE4.2 corpus as + diagnostic, even when the compared instruction streams are exact. +- MSVC forces the broad `primary-composition` profile and AVX2/256 modulus + profile into record-only mode. The latter is currently exact and the former + combines `/GS`-affected operations with unrelated operations. +- Several `Register` comparisons omit `CODEGEN_PROFILE`, causing their records + to share the non-descriptive `default` identity. +- [RegisterImplementationMatrix.md](RegisterImplementationMatrix.md) describes + strict SSE4.2 gates for clang-cl, Clang, and GCC, while + [RegisterQualification.md](RegisterQualification.md) and the executable CMake + policy classify all SSE4.2 comparisons as diagnostic. + +This inconsistency makes a recorded success ambiguous: it may mean exact parity, +a reviewed difference, or merely that the comparison was allowed to differ. + +## Goals + +- Give every optimized supported cell an unambiguous, enforceable disposition. +- Make exact parity the default rather than maintaining allowlists for exact + compiler output. +- Retain only narrow, reviewed compiler differences and detect any drift in + either side of such a difference. +- Keep deterministic semantic exceptions in code when they can be recognized + more safely than by whole-record hashes. +- Prevent one exceptional symbol family from weakening unrelated symbols. +- Use the same retained-profile schema and validation rules for `Register` and + `PartialRegister` without forcing their fixtures or comparators into one file. +- Keep documentation claims identical to the policy executed by CMake and CI. + +## Non-goals + +- Changing the public `Register` or `PartialRegister` API. +- Requiring identical code across different compiler versions. +- Treating Debug, sanitizer, or platform-default ABI observations as + zero-overhead evidence. +- Replacing instruction inspection with performance benchmarks. +- Accepting a new fingerprint only because a compiler upgrade produced it. + +## Proposed enforcement model + +### Comparison dispositions + +| Disposition | Meaning | Qualification value | +| --- | --- | --- | +| Exact | Normalized wrapper and raw instructions match. | Satisfies the optimized gate. | +| Semantic exception | A narrowly scoped recognizer proves one documented instruction pattern and all remaining instructions match. | Satisfies the optimized gate as an explicit exception. | +| Retained difference | The wrapper and raw streams differ, but both hashes match a reviewed entry for the exact compiler identity and profile. | Satisfies the optimized gate as an explicit exception. | +| Diagnostic | The result is recorded without requiring parity or a retained difference. | Does not satisfy an optimized gate or support a zero-overhead claim. | + +An optimized Release comparison must use one of the first three dispositions. +Diagnostic mode is reserved for explicitly selected investigation builds and +observational contracts such as the platform-default ABI record. + +### Evaluation order + +The comparison engine should evaluate a strict record in this order: + +1. normalize and compare the complete selected wrapper/raw symbol corpus; +2. apply an applicable narrow semantic recognizer; +3. look up an exact retained-profile entry and verify both normalized hashes; +4. fail with the instruction difference and the unmatched profile identity. + +This keeps ordinary exact output free of stored fingerprints. A retained entry +is consulted only after a real difference has been found. + +### Retained-profile identity + +Every comparison must provide a stable `CODEGEN_PROFILE`. A retained entry is +identified by at least: + +- value type (`Register` or `PartialRegister`); +- compiler family and compiler version; +- profile name; +- register width and ISA profile; +- wrapper hash and raw hash; +- a concise reason naming the accepted transformation. + +Operating system, ABI mode, FMA mode, or another compilation dimension must be +part of the identity when it can change the selected code. Compiler-version +wildcards should be rejected unless the compiler family exposes no stable +version boundary and the qualification document explicitly justifies that +choice. + +Missing, duplicate, malformed, stale, or ambiguous entries are configuration +errors. Entries not exercised by the qualified matrix should also fail policy +validation so obsolete exceptions cannot accumulate silently. + +## Corpus boundaries + +The existing isolated records remain separate: immediate shifts, register-only, +reassignment, ABI, consumer ABI, specialized operations, FMA modes, +rearrangement/conversion, common type matrix, and modulus type matrix. + +The broad `primary-composition` comparison should be divided by compiler-relevant +behavior. At minimum, pure composition, memory transfer/mutation, and opaque-call +boundaries must not share one disposition. The final split should be driven by +the generated symbol differences, with each record containing only symbols that +can legitimately share one exception reason. + +The missing stable `Register` profile names should be made explicit, including +`register-only`, `reassignment`, `abi`, and `consumer-abi`. The platform-default +ABI record remains observational and should be named independently rather than +participating in strict wrapper/raw policy. + +The existing exact MSVC `/GS` recognizers should remain semantic exceptions. +They describe bounded instruction sequences more precisely than a whole-record +fingerprint. Any `/GS` behavior that cannot be recognized narrowly should be +isolated before considering a retained hash. + +## Configuration layout + +The steady-state layout should separate orchestration, retained data, and +comparison logic: + +- `cmake/development/RegisterCodegen.cmake` owns `Register` fixture targets, + profile boundaries, and comparison registration. +- `cmake/development/PartialRegisterCodegen.cmake` owns `PartialRegister` + fixture targets, profile boundaries, and comparison registration. +- `cmake/development/RetainedCodegenProfiles.cmake` owns the shared JSON schema, + validation, and exact lookup rules. +- `cmake/development/RegisterCodegenProfiles.json` owns reviewed `Register` + retained differences. +- `cmake/development/PartialRegisterCodegenProfiles.json` continues to own + reviewed `PartialRegister` retained differences. +- The comparison scripts continue to own disassembly normalization and + type-specific semantic recognizers. + +The shared loader should not know fixture names or decide which comparisons are +diagnostic. Conversely, the orchestration files should contain no hashes and +should not duplicate JSON parsing or schema validation. + +## Evidence required before policy changes + +The complete optimized `Register` corpus must be regenerated for the supported +matrix before reclassifying records: + +- MSVC 19.44 on Windows; +- clang-cl 20.1.8 on Windows; +- the qualified clang-cl 22 version on Windows; +- Clang 22 on the pinned Linux image; +- GCC 14 on the pinned Linux image. + +For each compiler, run SSE4.2/128, AVX2/128, and AVX2/256 where available, with +both FMA dispositions where the fixture defines them. Clang-cl 20 evidence is a +required input, not something inferred from clang-cl 22. + +Each non-exact record must be reviewed at symbol and instruction level. The +review must establish whether the difference is: + +- a deterministic pattern suitable for a narrow semantic recognizer; +- a compiler-qualified allocation or scheduling difference suitable for an + exact retained hash pair; or +- real wrapper overhead, in which case the optimized gate remains failing and + the zero-overhead claim must exclude that precise cell. + +Retained hashes should be reproduced by a second clean build with the same +qualified toolchain before they are committed. A compiler upgrade requires a +new review; it must not inherit a nearby version's hashes. + +## Documentation ownership + +[RegisterQualification.md](RegisterQualification.md) remains authoritative for +the supported compiler matrix, optimized claims, diagnostic exclusions, and +accepted exceptions. [PartialRegisterQualification.md](PartialRegisterQualification.md) +has the equivalent responsibility for `PartialRegister`. + +[RegisterImplementationMatrix.md](RegisterImplementationMatrix.md) should link +to the qualification contract instead of restating a stronger platform policy. +The README should contain only a short user-facing summary. Generated comparison +records remain execution evidence and are not permanent repository documents. + +Documentation and executable policy must change together. No document should +claim strict coverage for a cell that CMake records diagnostically, and no +strictly enforced cell should remain described as diagnostic. + +## Adoption sequence + +1. Assign stable names to every `Register` comparison and collect the complete + qualified compiler evidence without changing pass/fail policy. +2. Split broad records until every observed difference has one owning symbol + family and reason. +3. Introduce the shared retained-profile loader and migrate the existing + `PartialRegister` parser without changing its accepted entries. +4. Add only reviewed `Register` retained differences, preserving the existing + semantic recognizers where they are narrower. +5. Remove blanket SSE4.2 and stale per-profile record-only overrides, making all + optimized Release comparisons strict by default. +6. Re-run the complete local/container matrix and Windows CI, including the + dedicated clang-cl 20 and clang-cl 22 jobs. +7. Reconcile the qualification contract, implementation matrix, and README with + the final evidence-backed boundary. + +## Acceptance criteria + +The proposal is satisfied when: + +- every optimized Release comparison has a unique stable profile identity; +- exact records enforce without a JSON entry; +- every non-exact passing record is owned by one semantic recognizer or one + exact compiler-qualified JSON entry; +- no broad record-only switch masks an unrelated optimized comparison; +- Debug, sanitizer, and observational ABI records are visibly diagnostic and + excluded from optimized claims; +- the supported MSVC, clang-cl 20, clang-cl 22, Clang, and GCC matrices pass + their required correctness, ABI, and generated-code gates; +- negative tests reject malformed, duplicate, ambiguous, stale, and incorrect + retained-profile data; and +- the qualification documents, implementation matrix, README, CMake policy, + and CI jobs describe the same boundary. + +## Alternatives not recommended + +Keeping all SSE4.2 comparisons diagnostic preserves the current ambiguity and +provides no regression gate for records that are already exact. Hashing every +record creates unnecessary churn and turns ordinary compiler output into policy +data. Maintaining hashes directly in CMake mixes data with orchestration and +makes review harder. Finally, a single broad hash for the composition corpus +would preserve the same excessive exception boundary in a different format. diff --git a/docs/RegisterCodegenSymbolAudit.csv b/docs/RegisterCodegenSymbolAudit.csv deleted file mode 100644 index d652218..0000000 --- a/docs/RegisterCodegenSymbolAudit.csv +++ /dev/null @@ -1,811 +0,0 @@ -"symbol","owning_fixture","applicability","contract_category","comparison_baseline","comparison_record","owning_validation","decision","rationale" -"simdlib_abi_binary","tests/codegen/RegisterAbi.cpp","SSE4.2/128; AVX2/128; AVX2/256","ABI boundary","tests/codegen/RegisterAbiRaw.cpp matching native-vector signature","abi","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The explicit-object aggregate mirror isolates one non-inlined SIMD_FLAGS(...) signature shape from operation semantics." -"simdlib_abi_mask","tests/codegen/RegisterAbi.cpp","SSE4.2/128; AVX2/128; AVX2/256","ABI boundary","tests/codegen/RegisterAbiRaw.cpp matching native-vector signature","abi","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The explicit-object aggregate mirror isolates one non-inlined SIMD_FLAGS(...) signature shape from operation semantics." -"simdlib_abi_mutate","tests/codegen/RegisterAbi.cpp","SSE4.2/128; AVX2/128; AVX2/256","ABI boundary","tests/codegen/RegisterAbiRaw.cpp matching native-vector signature","abi","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The explicit-object aggregate mirror isolates one non-inlined SIMD_FLAGS(...) signature shape from operation semantics." -"simdlib_abi_native","tests/codegen/RegisterAbi.cpp","SSE4.2/128; AVX2/128; AVX2/256","ABI boundary","tests/codegen/RegisterAbiRaw.cpp matching native-vector signature","abi","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The explicit-object aggregate mirror isolates one non-inlined SIMD_FLAGS(...) signature shape from operation semantics." -"simdlib_abi_scalar","tests/codegen/RegisterAbi.cpp","SSE4.2/128; AVX2/128; AVX2/256","ABI boundary","tests/codegen/RegisterAbiRaw.cpp matching native-vector signature","abi","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The explicit-object aggregate mirror isolates one non-inlined SIMD_FLAGS(...) signature shape from operation semantics." -"simdlib_abi_store","tests/codegen/RegisterAbi.cpp","SSE4.2/128; AVX2/128; AVX2/256","ABI boundary","tests/codegen/RegisterAbiRaw.cpp matching native-vector signature","abi","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The explicit-object aggregate mirror isolates one non-inlined SIMD_FLAGS(...) signature shape from operation semantics." -"simdlib_abi_ternary","tests/codegen/RegisterAbi.cpp","SSE4.2/128; AVX2/128; AVX2/256","ABI boundary","tests/codegen/RegisterAbiRaw.cpp matching native-vector signature","abi","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The explicit-object aggregate mirror isolates one non-inlined SIMD_FLAGS(...) signature shape from operation semantics." -"simdlib_abi_unary","tests/codegen/RegisterAbi.cpp","SSE4.2/128; AVX2/128; AVX2/256","ABI boundary","tests/codegen/RegisterAbiRaw.cpp matching native-vector signature","abi","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The explicit-object aggregate mirror isolates one non-inlined SIMD_FLAGS(...) signature shape from operation semantics." -"simdlib_consumer_abi_mask_pass","tests/codegen/RegisterAbi.cpp","SSE4.2/128; AVX2/128; AVX2/256","ABI boundary","tests/codegen/RegisterAbiRaw.cpp matching native-vector signature","consumer-abi","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","A real public Register or RegisterMask crosses the downstream non-inlined SIMD_FLAGS(...) boundary and is compared with the native signature." -"simdlib_consumer_abi_mask_return","tests/codegen/RegisterAbi.cpp","SSE4.2/128; AVX2/128; AVX2/256","ABI boundary","tests/codegen/RegisterAbiRaw.cpp matching native-vector signature","consumer-abi","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","A real public Register or RegisterMask crosses the downstream non-inlined SIMD_FLAGS(...) boundary and is compared with the native signature." -"simdlib_consumer_abi_register_pass","tests/codegen/RegisterAbi.cpp","SSE4.2/128; AVX2/128; AVX2/256","ABI boundary","tests/codegen/RegisterAbiRaw.cpp matching native-vector signature","consumer-abi","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","A real public Register or RegisterMask crosses the downstream non-inlined SIMD_FLAGS(...) boundary and is compared with the native signature." -"simdlib_consumer_abi_register_return","tests/codegen/RegisterAbi.cpp","SSE4.2/128; AVX2/128; AVX2/256","ABI boundary","tests/codegen/RegisterAbiRaw.cpp matching native-vector signature","consumer-abi","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","A real public Register or RegisterMask crosses the downstream non-inlined SIMD_FLAGS(...) boundary and is compared with the native signature." -"simdlib_codegen_aligned_transfer","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","composed-expression optimization","tests/codegen/RegisterCodegenRaw.cpp public Api expression","primary-composition","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","Aligned load and aligned store must optimize as one transfer chain; isolated load/store cells do not cover the chain." -"simdlib_codegen_basic_bitwise","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","composed-expression optimization","tests/codegen/RegisterCodegenRaw.cpp public Api expression","register-only","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","Chained bitwise operators including public andnot polarity must collapse to the Api expression." -"simdlib_codegen_basic_broadcast_chain","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","composed-expression optimization","tests/codegen/RegisterCodegenRaw.cpp public Api expression","register-only","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","Multiple scalar broadcasts in an arithmetic chain must add no wrapper work." -"simdlib_codegen_basic_shift_left_immediate","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","instruction-property guarantee","tests/codegen/RegisterCodegenRaw.cpp public Api expression","register-only","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","A compile-time shift count must retain the immediate public operation code shape." -"simdlib_codegen_broadcast_reuse","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","composed-expression optimization","tests/codegen/RegisterCodegenRaw.cpp public Api expression","register-only","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","A reused broadcast value must remain common and avoid redundant wrapper work." -"simdlib_codegen_byte_transfer","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","composed-expression optimization","tests/codegen/RegisterCodegenRaw.cpp public Api expression","primary-composition","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","Byte-span load and store must optimize as one transfer chain; isolated load/store cells do not cover the chain." -"simdlib_codegen_complete_byte_shift","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128","public abstraction parity","tests/codegen/RegisterCodegenRaw.cpp public Api expression","primary-composition","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2","retain","The explicit slow runtime complete-register byte shift must match the Api operation without wrapper storage." -"simdlib_codegen_complete_shift_runtime","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128","public abstraction parity","tests/codegen/RegisterCodegenRaw.cpp public Api expression","primary-composition","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2","retain","The explicit slow runtime complete-register bit shift must match the Api operation without wrapper storage." -"simdlib_codegen_complete_shift_static","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128","public abstraction parity","tests/codegen/RegisterCodegenRaw.cpp public Api expression","primary-composition","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2","retain","Static complete-register bit shifting must match the Api operation." -"simdlib_codegen_lane_last","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterCodegenRaw.cpp public Api expression","register-only","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","Nonzero constant-index lane extraction protects the highest-lane public path." -"simdlib_codegen_load_operate_store","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","composed-expression optimization","tests/codegen/RegisterCodegenRaw.cpp public Api expression","primary-composition","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","Load, arithmetic, and store must optimize as one memory-capable expression." -"simdlib_codegen_mask_all","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","composed-expression optimization","tests/codegen/RegisterCodegenRaw.cpp public Api expression","register-only","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","Comparison followed by all-lane reduction must compose without wrapper overhead." -"simdlib_codegen_mask_any","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","composed-expression optimization","tests/codegen/RegisterCodegenRaw.cpp public Api expression","register-only","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","Comparison followed by any-lane reduction must compose without wrapper overhead." -"simdlib_codegen_mask_bits","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","composed-expression optimization","tests/codegen/RegisterCodegenRaw.cpp public Api expression","register-only","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","Comparison followed by compact-mask extraction must compose without wrapper overhead." -"simdlib_codegen_mask_combine","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","composed-expression optimization","tests/codegen/RegisterCodegenRaw.cpp public Api expression","register-only","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","Comparison and predicate union must compose without wrapper overhead." -"simdlib_codegen_mask_select","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","composed-expression optimization","tests/codegen/RegisterCodegenRaw.cpp public Api expression","register-only","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","Comparison and selection must compose without wrapper overhead." -"simdlib_codegen_mutate","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","composed-expression optimization","tests/codegen/RegisterCodegenRaw.cpp public Api expression","primary-composition","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","A caller-owned native reference updated through a local Register must not acquire wrapper overhead." -"simdlib_codegen_native","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterCodegenRaw.cpp public Api expression","register-only","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","Aggregate wrapping and native-member observation must add no instructions." -"simdlib_codegen_opaque","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","register-pressure behavior","tests/codegen/RegisterCodegenRaw.cpp public Api expression","primary-composition","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","A wrapped value kept live across an opaque call must match raw spill and reload behavior." -"simdlib_codegen_pressure","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","register-pressure behavior","tests/codegen/RegisterCodegenRaw.cpp public Api expression","register-only","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","Multiple simultaneously live wrapper values must match the raw register-pressure expression." -"simdlib_codegen_reassignment_arithmetic","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","composed-expression optimization","tests/codegen/RegisterCodegenRaw.cpp public Api expression","reassignment","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","Successive ordinary assignments must match the equivalent nested Api arithmetic." -"simdlib_codegen_special_members","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterCodegenRaw.cpp public Api expression","register-only","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","Aggregate copy construction and assignment must add no runtime work." -"simdlib_codegen_ternary","tests/codegen/RegisterCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","composed-expression optimization","tests/codegen/RegisterCodegenRaw.cpp public Api expression","register-only","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","Operator multiply-add composition must collapse to the equivalent Api expression." -"simdlib_codegen_default","tests/codegen/RegisterDefaultAbi.cpp","SSE4.2/128; AVX2/128; AVX2/256","explicitly diagnostic evidence","tests/codegen/RegisterDefaultAbiRaw.cpp native-vector platform-default signature","default-abi","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The platform-default aggregate calling convention is recorded for diagnosis and is excluded from zero-overhead pass/fail claims." -"simdlib_fma_codegen_multiply_add_f32","tests/codegen/RegisterFmaCodegenFixture.h","SSE4.2/128 FMA-disabled; AVX2/128 and AVX2/256 FMA-disabled and FMA-enabled","instruction-property guarantee","tests/codegen/RegisterFmaCodegenRaw.cpp matching public Api::multiply_add operation","fma-disabled; fma-enabled on AVX2","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated f32 multiply-add cell enforces both fusion absence and fusion presence in the corresponding feature mode." -"simdlib_fma_codegen_multiply_add_f64","tests/codegen/RegisterFmaCodegenFixture.h","SSE4.2/128 FMA-disabled; AVX2/128 and AVX2/256 FMA-disabled and FMA-enabled","instruction-property guarantee","tests/codegen/RegisterFmaCodegenRaw.cpp matching public Api::multiply_add operation","fma-disabled; fma-enabled on AVX2","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated f64 multiply-add cell enforces both fusion absence and fusion presence in the corresponding feature mode." -"simdlib_rearrangement_codegen_bit_cast_f32_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f32-to-f32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f32_f64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f32-to-f64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f32_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f32-to-i16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f32_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f32-to-i32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f32_i64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f32-to-i64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f32_i8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f32-to-i8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f32_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f32-to-u16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f32_u32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f32-to-u32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f32_u64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f32-to-u64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f32_u8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f32-to-u8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f64_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f64-to-f32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f64_f64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f64-to-f64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f64_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f64-to-i16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f64_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f64-to-i32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f64_i64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f64-to-i64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f64_i8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f64-to-i8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f64_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f64-to-u16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f64_u32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f64-to-u32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f64_u64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f64-to-u64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_f64_u8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f64-to-u8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i16_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i16-to-f32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i16_f64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i16-to-f64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i16_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i16-to-i16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i16_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i16-to-i32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i16_i64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i16-to-i64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i16_i8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i16-to-i8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i16_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i16-to-u16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i16_u32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i16-to-u32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i16_u64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i16-to-u64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i16_u8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i16-to-u8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i32_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i32-to-f32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i32_f64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i32-to-f64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i32_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i32-to-i16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i32_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i32-to-i32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i32_i64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i32-to-i64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i32_i8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i32-to-i8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i32_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i32-to-u16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i32_u32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i32-to-u32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i32_u64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i32-to-u64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i32_u8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i32-to-u8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i64_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i64-to-f32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i64_f64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i64-to-f64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i64_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i64-to-i16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i64_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i64-to-i32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i64_i64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i64-to-i64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i64_i8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i64-to-i8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i64_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i64-to-u16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i64_u32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i64-to-u32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i64_u64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i64-to-u64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i64_u8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i64-to-u8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i8_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i8-to-f32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i8_f64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i8-to-f64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i8_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i8-to-i16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i8_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i8-to-i32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i8_i64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i8-to-i64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i8_i8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i8-to-i8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i8_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i8-to-u16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i8_u32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i8-to-u32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i8_u64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i8-to-u64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_i8_u8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i8-to-u8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u16_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u16-to-f32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u16_f64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u16-to-f64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u16_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u16-to-i16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u16_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u16-to-i32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u16_i64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u16-to-i64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u16_i8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u16-to-i8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u16_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u16-to-u16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u16_u32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u16-to-u32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u16_u64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u16-to-u64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u16_u8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u16-to-u8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u32_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u32-to-f32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u32_f64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u32-to-f64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u32_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u32-to-i16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u32_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u32-to-i32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u32_i64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u32-to-i64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u32_i8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u32-to-i8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u32_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u32-to-u16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u32_u32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u32-to-u32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u32_u64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u32-to-u64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u32_u8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u32-to-u8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u64_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u64-to-f32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u64_f64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u64-to-f64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u64_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u64-to-i16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u64_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u64-to-i32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u64_i64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u64-to-i64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u64_i8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u64-to-i8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u64_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u64-to-u16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u64_u32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u64-to-u32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u64_u64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u64-to-u64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u64_u8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u64-to-u8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u8_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u8-to-f32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u8_f64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u8-to-f64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u8_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u8-to-i16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u8_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u8-to-i32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u8_i64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u8-to-i64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u8_i8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u8-to-i8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u8_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u8-to-u16 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u8_u32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u8-to-u32 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u8_u64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u8-to-u64 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_bit_cast_u8_u8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u8-to-u8 full-width bit-cast cell independently protects that public template instantiation." -"simdlib_rearrangement_codegen_blend_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The immediate blend rearrangement for f32 must match its public Api operation." -"simdlib_rearrangement_codegen_blend_f64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The immediate blend rearrangement for f64 must match its public Api operation." -"simdlib_rearrangement_codegen_blend_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The immediate blend rearrangement for i16 must match its public Api operation." -"simdlib_rearrangement_codegen_blend_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The immediate blend rearrangement for i32 must match its public Api operation." -"simdlib_rearrangement_codegen_blend_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The immediate blend rearrangement for u16 must match its public Api operation." -"simdlib_rearrangement_codegen_blend_u32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The immediate blend rearrangement for u32 must match its public Api operation." -"simdlib_rearrangement_codegen_byte_shuffle_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2","retain","The 128-bit complete byte reversal must match the public Api shuffle." -"simdlib_rearrangement_codegen_byte_shuffle_i32_cross","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.256Avx2","retain","The 256-bit i32_cross selector pattern protects the corresponding local or cross-half public byte-shuffle path." -"simdlib_rearrangement_codegen_byte_shuffle_i32_local","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.256Avx2","retain","The 256-bit i32_local selector pattern protects the corresponding local or cross-half public byte-shuffle path." -"simdlib_rearrangement_codegen_byte_shuffle_i32_mixed","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.256Avx2","retain","The 256-bit i32_mixed selector pattern protects the corresponding local or cross-half public byte-shuffle path." -"simdlib_rearrangement_codegen_convert_f32_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The f32-to-i32 numeric conversion must match the public Api operation." -"simdlib_rearrangement_codegen_convert_i32_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The i32-to-f32 numeric conversion must match the public Api operation." -"simdlib_rearrangement_codegen_convert_u32_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The u32-to-f32 numeric conversion must match the public Api operation." -"simdlib_rearrangement_codegen_logical_shuffle_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The width-specific logical shuffle for f32 protects complete-register selector lowering, including cross-128-bit movement at 256 bits." -"simdlib_rearrangement_codegen_logical_shuffle_f64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The width-specific logical shuffle for f64 protects complete-register selector lowering, including cross-128-bit movement at 256 bits." -"simdlib_rearrangement_codegen_logical_shuffle_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The width-specific logical shuffle for i16 protects complete-register selector lowering, including cross-128-bit movement at 256 bits." -"simdlib_rearrangement_codegen_logical_shuffle_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The width-specific logical shuffle for i32 protects complete-register selector lowering, including cross-128-bit movement at 256 bits." -"simdlib_rearrangement_codegen_logical_shuffle_i64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The width-specific logical shuffle for i64 protects complete-register selector lowering, including cross-128-bit movement at 256 bits." -"simdlib_rearrangement_codegen_logical_shuffle_i8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The width-specific logical shuffle for i8 protects complete-register selector lowering, including cross-128-bit movement at 256 bits." -"simdlib_rearrangement_codegen_logical_shuffle_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The width-specific logical shuffle for u16 protects complete-register selector lowering, including cross-128-bit movement at 256 bits." -"simdlib_rearrangement_codegen_logical_shuffle_u32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The width-specific logical shuffle for u32 protects complete-register selector lowering, including cross-128-bit movement at 256 bits." -"simdlib_rearrangement_codegen_logical_shuffle_u64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The width-specific logical shuffle for u64 protects complete-register selector lowering, including cross-128-bit movement at 256 bits." -"simdlib_rearrangement_codegen_logical_shuffle_u8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The width-specific logical shuffle for u8 protects complete-register selector lowering, including cross-128-bit movement at 256 bits." -"simdlib_rearrangement_codegen_lower_half_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.256Avx2","retain","The f32 lower-half conversion changes register width and must match the Api boundary." -"simdlib_rearrangement_codegen_lower_half_f64","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.256Avx2","retain","The f64 lower-half conversion changes register width and must match the Api boundary." -"simdlib_rearrangement_codegen_lower_half_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.256Avx2","retain","The i16 lower-half conversion changes register width and must match the Api boundary." -"simdlib_rearrangement_codegen_lower_half_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.256Avx2","retain","The i32 lower-half conversion changes register width and must match the Api boundary." -"simdlib_rearrangement_codegen_lower_half_i64","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.256Avx2","retain","The i64 lower-half conversion changes register width and must match the Api boundary." -"simdlib_rearrangement_codegen_lower_half_i8","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.256Avx2","retain","The i8 lower-half conversion changes register width and must match the Api boundary." -"simdlib_rearrangement_codegen_lower_half_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.256Avx2","retain","The u16 lower-half conversion changes register width and must match the Api boundary." -"simdlib_rearrangement_codegen_lower_half_u32","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.256Avx2","retain","The u32 lower-half conversion changes register width and must match the Api boundary." -"simdlib_rearrangement_codegen_lower_half_u64","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.256Avx2","retain","The u64 lower-half conversion changes register width and must match the Api boundary." -"simdlib_rearrangement_codegen_lower_half_u8","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.256Avx2","retain","The u8 lower-half conversion changes register width and must match the Api boundary." -"simdlib_rearrangement_codegen_shuffle_high_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The immediate shuffle_high rearrangement for i16 must match its public Api operation." -"simdlib_rearrangement_codegen_shuffle_high_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The immediate shuffle_high rearrangement for u16 must match its public Api operation." -"simdlib_rearrangement_codegen_shuffle_low_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The immediate shuffle_low rearrangement for i16 must match its public Api operation." -"simdlib_rearrangement_codegen_shuffle_low_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The immediate shuffle_low rearrangement for u16 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_high_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_high rearrangement for f32 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_high_f64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_high rearrangement for f64 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_high_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_high rearrangement for i16 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_high_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_high rearrangement for i32 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_high_i64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_high rearrangement for i64 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_high_i8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_high rearrangement for i8 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_high_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_high rearrangement for u16 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_high_u32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_high rearrangement for u32 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_high_u64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_high rearrangement for u64 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_high_u8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_high rearrangement for u8 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_low_f32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_low rearrangement for f32 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_low_f64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_low rearrangement for f64 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_low_i16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_low rearrangement for i16 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_low_i32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_low rearrangement for i32 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_low_i64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_low rearrangement for i64 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_low_i8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_low rearrangement for i8 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_low_u16","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_low rearrangement for u16 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_low_u32","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_low rearrangement for u32 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_low_u64","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_low rearrangement for u64 must match its public Api operation." -"simdlib_rearrangement_codegen_unpack_low_u8","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated unpack_low rearrangement for u8 must match its public Api operation." -"simdlib_rearrangement_codegen_widen_i16_i32_128","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2","retain","The explicit low-lane i16-to-i32 128-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_i16_i32_256","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/128 source to 256-bit result","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Avx2","retain","The explicit low-lane i16-to-i32 256-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_i16_i64_128","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2","retain","The explicit low-lane i16-to-i64 128-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_i16_i64_256","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/128 source to 256-bit result","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Avx2","retain","The explicit low-lane i16-to-i64 256-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_i32_i64_128","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2","retain","The explicit low-lane i32-to-i64 128-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_i32_i64_256","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/128 source to 256-bit result","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Avx2","retain","The explicit low-lane i32-to-i64 256-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_i8_i16_128","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2","retain","The explicit low-lane i8-to-i16 128-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_i8_i16_256","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/128 source to 256-bit result","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Avx2","retain","The explicit low-lane i8-to-i16 256-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_i8_i32_128","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2","retain","The explicit low-lane i8-to-i32 128-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_i8_i32_256","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/128 source to 256-bit result","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Avx2","retain","The explicit low-lane i8-to-i32 256-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_i8_i64_128","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2","retain","The explicit low-lane i8-to-i64 128-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_i8_i64_256","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/128 source to 256-bit result","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Avx2","retain","The explicit low-lane i8-to-i64 256-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_u16_u32_128","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2","retain","The explicit low-lane u16-to-u32 128-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_u16_u32_256","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/128 source to 256-bit result","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Avx2","retain","The explicit low-lane u16-to-u32 256-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_u16_u64_128","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2","retain","The explicit low-lane u16-to-u64 128-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_u16_u64_256","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/128 source to 256-bit result","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Avx2","retain","The explicit low-lane u16-to-u64 256-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_u32_u64_128","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2","retain","The explicit low-lane u32-to-u64 128-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_u32_u64_256","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/128 source to 256-bit result","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Avx2","retain","The explicit low-lane u32-to-u64 256-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_u8_u16_128","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2","retain","The explicit low-lane u8-to-u16 128-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_u8_u16_256","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/128 source to 256-bit result","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Avx2","retain","The explicit low-lane u8-to-u16 256-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_u8_u32_128","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2","retain","The explicit low-lane u8-to-u32 128-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_u8_u32_256","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/128 source to 256-bit result","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Avx2","retain","The explicit low-lane u8-to-u32 256-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_u8_u64_128","tests/codegen/RegisterRearrangementCodegenFixture.h","SSE4.2/128; AVX2/128","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2","retain","The explicit low-lane u8-to-u64 128-bit widening result must match the public Api operation." -"simdlib_rearrangement_codegen_widen_u8_u64_256","tests/codegen/RegisterRearrangementCodegenFixture.h","AVX2/128 source to 256-bit result","public abstraction parity","tests/codegen/RegisterRearrangementCodegenRaw.cpp matching public Api operation","rearrangement-conversion","RegisterCodegen.128Avx2","retain","The explicit low-lane u8-to-u64 256-bit widening result must match the public Api operation." -"simdlib_specialized_codegen_absolute_f32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::absolute specialization for f32 must match its public Api operation." -"simdlib_specialized_codegen_absolute_f64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::absolute specialization for f64 must match its public Api operation." -"simdlib_specialized_codegen_absolute_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::absolute specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_absolute_i32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::absolute specialization for i32 must match its public Api operation." -"simdlib_specialized_codegen_absolute_i64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::absolute specialization for i64 must match its public Api operation." -"simdlib_specialized_codegen_absolute_i8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::absolute specialization for i8 must match its public Api operation." -"simdlib_specialized_codegen_absolute_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::absolute specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_absolute_u32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::absolute specialization for u32 must match its public Api operation." -"simdlib_specialized_codegen_absolute_u64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::absolute specialization for u64 must match its public Api operation." -"simdlib_specialized_codegen_absolute_u8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::absolute specialization for u8 must match its public Api operation." -"simdlib_specialized_codegen_add_saturated_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated add_saturated specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_add_saturated_i8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated add_saturated specialization for i8 must match its public Api operation." -"simdlib_specialized_codegen_add_saturated_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated add_saturated specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_add_saturated_u8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated add_saturated specialization for u8 must match its public Api operation." -"simdlib_specialized_codegen_add_subtract_f32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated add_subtract specialization for f32 must match its public Api operation." -"simdlib_specialized_codegen_add_subtract_f64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated add_subtract specialization for f64 must match its public Api operation." -"simdlib_specialized_codegen_average_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated average specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_average_u8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated average specialization for u8 must match its public Api operation." -"simdlib_specialized_codegen_byte_multiply_add_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated byte_multiply_add specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_byte_multiply_add_i32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated byte_multiply_add specialization for i32 must match its public Api operation." -"simdlib_specialized_codegen_byte_multiply_add_i64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated byte_multiply_add specialization for i64 must match its public Api operation." -"simdlib_specialized_codegen_byte_multiply_add_i8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated byte_multiply_add specialization for i8 must match its public Api operation." -"simdlib_specialized_codegen_byte_multiply_add_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated byte_multiply_add specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_byte_multiply_add_u32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated byte_multiply_add specialization for u32 must match its public Api operation." -"simdlib_specialized_codegen_byte_multiply_add_u64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated byte_multiply_add specialization for u64 must match its public Api operation." -"simdlib_specialized_codegen_byte_multiply_add_u8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated byte_multiply_add specialization for u8 must match its public Api operation." -"simdlib_specialized_codegen_dot_product_f32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated dot_product specialization for f32 must match its public Api operation." -"simdlib_specialized_codegen_dot_product_f64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated dot_product specialization for f64 must match its public Api operation." -"simdlib_specialized_codegen_horizontal_add_f32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated horizontal_add specialization for f32 must match its public Api operation." -"simdlib_specialized_codegen_horizontal_add_f64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated horizontal_add specialization for f64 must match its public Api operation." -"simdlib_specialized_codegen_horizontal_add_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated horizontal_add specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_horizontal_add_i32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated horizontal_add specialization for i32 must match its public Api operation." -"simdlib_specialized_codegen_horizontal_add_saturated_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated horizontal_add_saturated specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_horizontal_add_saturated_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated horizontal_add_saturated specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_horizontal_add_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated horizontal_add specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_horizontal_add_u32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated horizontal_add specialization for u32 must match its public Api operation." -"simdlib_specialized_codegen_horizontal_subtract_f32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated horizontal_subtract specialization for f32 must match its public Api operation." -"simdlib_specialized_codegen_horizontal_subtract_f64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated horizontal_subtract specialization for f64 must match its public Api operation." -"simdlib_specialized_codegen_horizontal_subtract_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated horizontal_subtract specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_horizontal_subtract_i32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated horizontal_subtract specialization for i32 must match its public Api operation." -"simdlib_specialized_codegen_horizontal_subtract_saturated_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated horizontal_subtract_saturated specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_horizontal_subtract_saturated_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated horizontal_subtract_saturated specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_horizontal_subtract_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated horizontal_subtract specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_horizontal_subtract_u32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated horizontal_subtract specialization for u32 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_checked_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated checked-magnitude specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_checked_i32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated checked-magnitude specialization for i32 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_checked_i64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated checked-magnitude specialization for i64 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_checked_i8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated checked-magnitude specialization for i8 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_checked_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated checked-magnitude specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_checked_u32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated checked-magnitude specialization for u32 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_checked_u64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated checked-magnitude specialization for u64 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_checked_u8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated checked-magnitude specialization for u8 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_f32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::magnitude specialization for f32 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_f64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::magnitude specialization for f64 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::magnitude specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_i32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::magnitude specialization for i32 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_i64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::magnitude specialization for i64 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_i8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::magnitude specialization for i8 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::magnitude specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_u32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::magnitude specialization for u32 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_u64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::magnitude specialization for u64 must match its public Api operation." -"simdlib_specialized_codegen_magnitude_u8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::magnitude specialization for u8 must match its public Api operation." -"simdlib_specialized_codegen_max_f32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::max specialization for f32 must match its public Api operation." -"simdlib_specialized_codegen_max_f64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::max specialization for f64 must match its public Api operation." -"simdlib_specialized_codegen_max_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::max specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_max_i32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::max specialization for i32 must match its public Api operation." -"simdlib_specialized_codegen_max_i64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::max specialization for i64 must match its public Api operation." -"simdlib_specialized_codegen_max_i8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::max specialization for i8 must match its public Api operation." -"simdlib_specialized_codegen_max_position_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated max_position specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_max_position_i32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated max_position specialization for i32 must match its public Api operation." -"simdlib_specialized_codegen_max_position_i64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated max_position specialization for i64 must match its public Api operation." -"simdlib_specialized_codegen_max_position_i8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated max_position specialization for i8 must match its public Api operation." -"simdlib_specialized_codegen_max_position_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated max_position specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_max_position_u32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated max_position specialization for u32 must match its public Api operation." -"simdlib_specialized_codegen_max_position_u64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated max_position specialization for u64 must match its public Api operation." -"simdlib_specialized_codegen_max_position_u8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated max_position specialization for u8 must match its public Api operation." -"simdlib_specialized_codegen_max_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::max specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_max_u32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::max specialization for u32 must match its public Api operation." -"simdlib_specialized_codegen_max_u64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::max specialization for u64 must match its public Api operation." -"simdlib_specialized_codegen_max_u8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::max specialization for u8 must match its public Api operation." -"simdlib_specialized_codegen_min_f32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::min specialization for f32 must match its public Api operation." -"simdlib_specialized_codegen_min_f64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::min specialization for f64 must match its public Api operation." -"simdlib_specialized_codegen_min_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::min specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_min_i32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::min specialization for i32 must match its public Api operation." -"simdlib_specialized_codegen_min_i64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::min specialization for i64 must match its public Api operation." -"simdlib_specialized_codegen_min_i8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::min specialization for i8 must match its public Api operation." -"simdlib_specialized_codegen_min_position_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated min_position specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_min_position_i32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated min_position specialization for i32 must match its public Api operation." -"simdlib_specialized_codegen_min_position_i64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated min_position specialization for i64 must match its public Api operation." -"simdlib_specialized_codegen_min_position_i8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated min_position specialization for i8 must match its public Api operation." -"simdlib_specialized_codegen_min_position_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated min_position specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_min_position_u32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated min_position specialization for u32 must match its public Api operation." -"simdlib_specialized_codegen_min_position_u64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated min_position specialization for u64 must match its public Api operation." -"simdlib_specialized_codegen_min_position_u8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated min_position specialization for u8 must match its public Api operation." -"simdlib_specialized_codegen_min_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::min specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_min_u32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::min specialization for u32 must match its public Api operation." -"simdlib_specialized_codegen_min_u64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::min specialization for u64 must match its public Api operation." -"simdlib_specialized_codegen_min_u8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::min specialization for u8 must match its public Api operation." -"simdlib_specialized_codegen_multi_sad_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated multi_sad specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_multi_sad_i32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated multi_sad specialization for i32 must match its public Api operation." -"simdlib_specialized_codegen_multi_sad_i64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated multi_sad specialization for i64 must match its public Api operation." -"simdlib_specialized_codegen_multi_sad_i8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated multi_sad specialization for i8 must match its public Api operation." -"simdlib_specialized_codegen_multi_sad_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated multi_sad specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_multi_sad_u32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated multi_sad specialization for u32 must match its public Api operation." -"simdlib_specialized_codegen_multi_sad_u64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated multi_sad specialization for u64 must match its public Api operation." -"simdlib_specialized_codegen_multi_sad_u8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated multi_sad specialization for u8 must match its public Api operation." -"simdlib_specialized_codegen_multiply_add_adjacent_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated multiply_add_adjacent specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_multiply_add_adjacent_i32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated multiply_add_adjacent specialization for i32 must match its public Api operation." -"simdlib_specialized_codegen_multiply_add_adjacent_i64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated multiply_add_adjacent specialization for i64 must match its public Api operation." -"simdlib_specialized_codegen_multiply_add_adjacent_i8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated multiply_add_adjacent specialization for i8 must match its public Api operation." -"simdlib_specialized_codegen_multiply_add_adjacent_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated multiply_add_adjacent specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_multiply_add_adjacent_u32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated multiply_add_adjacent specialization for u32 must match its public Api operation." -"simdlib_specialized_codegen_multiply_add_adjacent_u64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated multiply_add_adjacent specialization for u64 must match its public Api operation." -"simdlib_specialized_codegen_multiply_add_adjacent_u8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated multiply_add_adjacent specialization for u8 must match its public Api operation." -"simdlib_specialized_codegen_normalize_f32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated normalize specialization for f32 must match its public Api operation." -"simdlib_specialized_codegen_normalize_f64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated normalize specialization for f64 must match its public Api operation." -"simdlib_specialized_codegen_sqrt_f32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::sqrt specialization for f32 must match its public Api operation." -"simdlib_specialized_codegen_sqrt_f64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::sqrt specialization for f64 must match its public Api operation." -"simdlib_specialized_codegen_sqrt_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::sqrt specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_sqrt_i32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::sqrt specialization for i32 must match its public Api operation." -"simdlib_specialized_codegen_sqrt_i64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::sqrt specialization for i64 must match its public Api operation." -"simdlib_specialized_codegen_sqrt_i8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::sqrt specialization for i8 must match its public Api operation." -"simdlib_specialized_codegen_sqrt_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::sqrt specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_sqrt_u32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::sqrt specialization for u32 must match its public Api operation." -"simdlib_specialized_codegen_sqrt_u64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::sqrt specialization for u64 must match its public Api operation." -"simdlib_specialized_codegen_sqrt_u8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated Register::sqrt specialization for u8 must match its public Api operation." -"simdlib_specialized_codegen_subtract_saturated_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated subtract_saturated specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_subtract_saturated_i8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated subtract_saturated specialization for i8 must match its public Api operation." -"simdlib_specialized_codegen_subtract_saturated_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated subtract_saturated specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_subtract_saturated_u8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated subtract_saturated specialization for u8 must match its public Api operation." -"simdlib_specialized_codegen_sum_absolute_byte_differences_i16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated sum_absolute_byte_differences specialization for i16 must match its public Api operation." -"simdlib_specialized_codegen_sum_absolute_byte_differences_i32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated sum_absolute_byte_differences specialization for i32 must match its public Api operation." -"simdlib_specialized_codegen_sum_absolute_byte_differences_i64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated sum_absolute_byte_differences specialization for i64 must match its public Api operation." -"simdlib_specialized_codegen_sum_absolute_byte_differences_i8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated sum_absolute_byte_differences specialization for i8 must match its public Api operation." -"simdlib_specialized_codegen_sum_absolute_byte_differences_u16","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated sum_absolute_byte_differences specialization for u16 must match its public Api operation." -"simdlib_specialized_codegen_sum_absolute_byte_differences_u32","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated sum_absolute_byte_differences specialization for u32 must match its public Api operation." -"simdlib_specialized_codegen_sum_absolute_byte_differences_u64","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated sum_absolute_byte_differences specialization for u64 must match its public Api operation." -"simdlib_specialized_codegen_sum_absolute_byte_differences_u8","tests/codegen/RegisterSpecializedCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterSpecializedCodegenRaw.cpp matching public Api operation","specialized","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated sum_absolute_byte_differences specialization for u8 must match its public Api operation." -"simdlib_type_matrix_add_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated add cell for f32 protects that public Register specialization." -"simdlib_type_matrix_add_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated add cell for f64 protects that public Register specialization." -"simdlib_type_matrix_add_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated add cell for i16 protects that public Register specialization." -"simdlib_type_matrix_add_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated add cell for i32 protects that public Register specialization." -"simdlib_type_matrix_add_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated add cell for i64 protects that public Register specialization." -"simdlib_type_matrix_add_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated add cell for i8 protects that public Register specialization." -"simdlib_type_matrix_add_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated add cell for u16 protects that public Register specialization." -"simdlib_type_matrix_add_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated add cell for u32 protects that public Register specialization." -"simdlib_type_matrix_add_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated add cell for u64 protects that public Register specialization." -"simdlib_type_matrix_add_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated add cell for u8 protects that public Register specialization." -"simdlib_type_matrix_bitwise_and_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_and cell for f32 protects that public Register specialization." -"simdlib_type_matrix_bitwise_and_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_and cell for f64 protects that public Register specialization." -"simdlib_type_matrix_bitwise_and_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_and cell for i16 protects that public Register specialization." -"simdlib_type_matrix_bitwise_and_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_and cell for i32 protects that public Register specialization." -"simdlib_type_matrix_bitwise_and_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_and cell for i64 protects that public Register specialization." -"simdlib_type_matrix_bitwise_and_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_and cell for i8 protects that public Register specialization." -"simdlib_type_matrix_bitwise_and_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_and cell for u16 protects that public Register specialization." -"simdlib_type_matrix_bitwise_and_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_and cell for u32 protects that public Register specialization." -"simdlib_type_matrix_bitwise_and_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_and cell for u64 protects that public Register specialization." -"simdlib_type_matrix_bitwise_and_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_and cell for u8 protects that public Register specialization." -"simdlib_type_matrix_bitwise_andnot_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_andnot cell for f32 protects that public Register specialization." -"simdlib_type_matrix_bitwise_andnot_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_andnot cell for f64 protects that public Register specialization." -"simdlib_type_matrix_bitwise_andnot_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_andnot cell for i16 protects that public Register specialization." -"simdlib_type_matrix_bitwise_andnot_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_andnot cell for i32 protects that public Register specialization." -"simdlib_type_matrix_bitwise_andnot_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_andnot cell for i64 protects that public Register specialization." -"simdlib_type_matrix_bitwise_andnot_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_andnot cell for i8 protects that public Register specialization." -"simdlib_type_matrix_bitwise_andnot_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_andnot cell for u16 protects that public Register specialization." -"simdlib_type_matrix_bitwise_andnot_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_andnot cell for u32 protects that public Register specialization." -"simdlib_type_matrix_bitwise_andnot_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_andnot cell for u64 protects that public Register specialization." -"simdlib_type_matrix_bitwise_andnot_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_andnot cell for u8 protects that public Register specialization." -"simdlib_type_matrix_bitwise_not_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_not cell for f32 protects that public Register specialization." -"simdlib_type_matrix_bitwise_not_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_not cell for f64 protects that public Register specialization." -"simdlib_type_matrix_bitwise_not_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_not cell for i16 protects that public Register specialization." -"simdlib_type_matrix_bitwise_not_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_not cell for i32 protects that public Register specialization." -"simdlib_type_matrix_bitwise_not_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_not cell for i64 protects that public Register specialization." -"simdlib_type_matrix_bitwise_not_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_not cell for i8 protects that public Register specialization." -"simdlib_type_matrix_bitwise_not_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_not cell for u16 protects that public Register specialization." -"simdlib_type_matrix_bitwise_not_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_not cell for u32 protects that public Register specialization." -"simdlib_type_matrix_bitwise_not_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_not cell for u64 protects that public Register specialization." -"simdlib_type_matrix_bitwise_not_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_not cell for u8 protects that public Register specialization." -"simdlib_type_matrix_bitwise_or_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_or cell for f32 protects that public Register specialization." -"simdlib_type_matrix_bitwise_or_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_or cell for f64 protects that public Register specialization." -"simdlib_type_matrix_bitwise_or_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_or cell for i16 protects that public Register specialization." -"simdlib_type_matrix_bitwise_or_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_or cell for i32 protects that public Register specialization." -"simdlib_type_matrix_bitwise_or_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_or cell for i64 protects that public Register specialization." -"simdlib_type_matrix_bitwise_or_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_or cell for i8 protects that public Register specialization." -"simdlib_type_matrix_bitwise_or_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_or cell for u16 protects that public Register specialization." -"simdlib_type_matrix_bitwise_or_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_or cell for u32 protects that public Register specialization." -"simdlib_type_matrix_bitwise_or_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_or cell for u64 protects that public Register specialization." -"simdlib_type_matrix_bitwise_or_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_or cell for u8 protects that public Register specialization." -"simdlib_type_matrix_bitwise_xor_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_xor cell for f32 protects that public Register specialization." -"simdlib_type_matrix_bitwise_xor_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_xor cell for f64 protects that public Register specialization." -"simdlib_type_matrix_bitwise_xor_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_xor cell for i16 protects that public Register specialization." -"simdlib_type_matrix_bitwise_xor_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_xor cell for i32 protects that public Register specialization." -"simdlib_type_matrix_bitwise_xor_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_xor cell for i64 protects that public Register specialization." -"simdlib_type_matrix_bitwise_xor_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_xor cell for i8 protects that public Register specialization." -"simdlib_type_matrix_bitwise_xor_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_xor cell for u16 protects that public Register specialization." -"simdlib_type_matrix_bitwise_xor_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_xor cell for u32 protects that public Register specialization." -"simdlib_type_matrix_bitwise_xor_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_xor cell for u64 protects that public Register specialization." -"simdlib_type_matrix_bitwise_xor_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated bitwise_xor cell for u8 protects that public Register specialization." -"simdlib_type_matrix_broadcast_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated broadcast cell for f32 protects that public Register specialization." -"simdlib_type_matrix_broadcast_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated broadcast cell for f64 protects that public Register specialization." -"simdlib_type_matrix_broadcast_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated broadcast cell for i16 protects that public Register specialization." -"simdlib_type_matrix_broadcast_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated broadcast cell for i32 protects that public Register specialization." -"simdlib_type_matrix_broadcast_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated broadcast cell for i64 protects that public Register specialization." -"simdlib_type_matrix_broadcast_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated broadcast cell for i8 protects that public Register specialization." -"simdlib_type_matrix_broadcast_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated broadcast cell for u16 protects that public Register specialization." -"simdlib_type_matrix_broadcast_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated broadcast cell for u32 protects that public Register specialization." -"simdlib_type_matrix_broadcast_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated broadcast cell for u64 protects that public Register specialization." -"simdlib_type_matrix_broadcast_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated broadcast cell for u8 protects that public Register specialization." -"simdlib_type_matrix_compare_equal_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_equal cell for f32 protects that public Register specialization." -"simdlib_type_matrix_compare_equal_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_equal cell for f64 protects that public Register specialization." -"simdlib_type_matrix_compare_equal_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_equal cell for i16 protects that public Register specialization." -"simdlib_type_matrix_compare_equal_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_equal cell for i32 protects that public Register specialization." -"simdlib_type_matrix_compare_equal_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_equal cell for i64 protects that public Register specialization." -"simdlib_type_matrix_compare_equal_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_equal cell for i8 protects that public Register specialization." -"simdlib_type_matrix_compare_equal_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_equal cell for u16 protects that public Register specialization." -"simdlib_type_matrix_compare_equal_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_equal cell for u32 protects that public Register specialization." -"simdlib_type_matrix_compare_equal_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_equal cell for u64 protects that public Register specialization." -"simdlib_type_matrix_compare_equal_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_equal cell for u8 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_equal_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater_equal cell for f32 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_equal_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater_equal cell for f64 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_equal_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater_equal cell for i16 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_equal_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater_equal cell for i32 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_equal_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater_equal cell for i64 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_equal_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater_equal cell for i8 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_equal_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater_equal cell for u16 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_equal_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater_equal cell for u32 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_equal_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater_equal cell for u64 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_equal_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater_equal cell for u8 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater cell for f32 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater cell for f64 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater cell for i16 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater cell for i32 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater cell for i64 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater cell for i8 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater cell for u16 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater cell for u32 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater cell for u64 protects that public Register specialization." -"simdlib_type_matrix_compare_greater_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_greater cell for u8 protects that public Register specialization." -"simdlib_type_matrix_compare_less_equal_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less_equal cell for f32 protects that public Register specialization." -"simdlib_type_matrix_compare_less_equal_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less_equal cell for f64 protects that public Register specialization." -"simdlib_type_matrix_compare_less_equal_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less_equal cell for i16 protects that public Register specialization." -"simdlib_type_matrix_compare_less_equal_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less_equal cell for i32 protects that public Register specialization." -"simdlib_type_matrix_compare_less_equal_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less_equal cell for i64 protects that public Register specialization." -"simdlib_type_matrix_compare_less_equal_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less_equal cell for i8 protects that public Register specialization." -"simdlib_type_matrix_compare_less_equal_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less_equal cell for u16 protects that public Register specialization." -"simdlib_type_matrix_compare_less_equal_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less_equal cell for u32 protects that public Register specialization." -"simdlib_type_matrix_compare_less_equal_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less_equal cell for u64 protects that public Register specialization." -"simdlib_type_matrix_compare_less_equal_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less_equal cell for u8 protects that public Register specialization." -"simdlib_type_matrix_compare_less_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less cell for f32 protects that public Register specialization." -"simdlib_type_matrix_compare_less_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less cell for f64 protects that public Register specialization." -"simdlib_type_matrix_compare_less_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less cell for i16 protects that public Register specialization." -"simdlib_type_matrix_compare_less_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less cell for i32 protects that public Register specialization." -"simdlib_type_matrix_compare_less_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less cell for i64 protects that public Register specialization." -"simdlib_type_matrix_compare_less_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less cell for i8 protects that public Register specialization." -"simdlib_type_matrix_compare_less_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less cell for u16 protects that public Register specialization." -"simdlib_type_matrix_compare_less_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less cell for u32 protects that public Register specialization." -"simdlib_type_matrix_compare_less_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less cell for u64 protects that public Register specialization." -"simdlib_type_matrix_compare_less_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated compare_less cell for u8 protects that public Register specialization." -"simdlib_type_matrix_construct_array_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_array transfer cell for f32 protects that public Register specialization." -"simdlib_type_matrix_construct_array_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_array transfer cell for f64 protects that public Register specialization." -"simdlib_type_matrix_construct_array_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_array transfer cell for i16 protects that public Register specialization." -"simdlib_type_matrix_construct_array_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_array transfer cell for i32 protects that public Register specialization." -"simdlib_type_matrix_construct_array_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_array transfer cell for i64 protects that public Register specialization." -"simdlib_type_matrix_construct_array_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_array transfer cell for i8 protects that public Register specialization." -"simdlib_type_matrix_construct_array_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_array transfer cell for u16 protects that public Register specialization." -"simdlib_type_matrix_construct_array_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_array transfer cell for u32 protects that public Register specialization." -"simdlib_type_matrix_construct_array_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_array transfer cell for u64 protects that public Register specialization." -"simdlib_type_matrix_construct_array_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_array transfer cell for u8 protects that public Register specialization." -"simdlib_type_matrix_construct_lanes_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_lanes transfer cell for f32 protects that public Register specialization." -"simdlib_type_matrix_construct_lanes_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_lanes transfer cell for f64 protects that public Register specialization." -"simdlib_type_matrix_construct_lanes_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_lanes transfer cell for i16 protects that public Register specialization." -"simdlib_type_matrix_construct_lanes_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_lanes transfer cell for i32 protects that public Register specialization." -"simdlib_type_matrix_construct_lanes_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_lanes transfer cell for i64 protects that public Register specialization." -"simdlib_type_matrix_construct_lanes_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_lanes transfer cell for i8 protects that public Register specialization." -"simdlib_type_matrix_construct_lanes_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_lanes transfer cell for u16 protects that public Register specialization." -"simdlib_type_matrix_construct_lanes_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_lanes transfer cell for u32 protects that public Register specialization." -"simdlib_type_matrix_construct_lanes_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_lanes transfer cell for u64 protects that public Register specialization." -"simdlib_type_matrix_construct_lanes_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated construct_lanes transfer cell for u8 protects that public Register specialization." -"simdlib_type_matrix_divide_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated divide cell for f32 protects that public Register specialization." -"simdlib_type_matrix_divide_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated divide cell for f64 protects that public Register specialization." -"simdlib_type_matrix_divide_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated divide cell for i16 protects that public Register specialization." -"simdlib_type_matrix_divide_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated divide cell for i32 protects that public Register specialization." -"simdlib_type_matrix_divide_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated divide cell for i64 protects that public Register specialization." -"simdlib_type_matrix_divide_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated divide cell for i8 protects that public Register specialization." -"simdlib_type_matrix_divide_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated divide cell for u16 protects that public Register specialization." -"simdlib_type_matrix_divide_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated divide cell for u32 protects that public Register specialization." -"simdlib_type_matrix_divide_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated divide cell for u64 protects that public Register specialization." -"simdlib_type_matrix_divide_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated divide cell for u8 protects that public Register specialization." -"simdlib_type_matrix_equal_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result equal cell for f32 protects that public Register specialization." -"simdlib_type_matrix_equal_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result equal cell for f64 protects that public Register specialization." -"simdlib_type_matrix_equal_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result equal cell for i16 protects that public Register specialization." -"simdlib_type_matrix_equal_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result equal cell for i32 protects that public Register specialization." -"simdlib_type_matrix_equal_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result equal cell for i64 protects that public Register specialization." -"simdlib_type_matrix_equal_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result equal cell for i8 protects that public Register specialization." -"simdlib_type_matrix_equal_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result equal cell for u16 protects that public Register specialization." -"simdlib_type_matrix_equal_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result equal cell for u32 protects that public Register specialization." -"simdlib_type_matrix_equal_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result equal cell for u64 protects that public Register specialization." -"simdlib_type_matrix_equal_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result equal cell for u8 protects that public Register specialization." -"simdlib_type_matrix_extract_first_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result extract_first cell for f32 protects that public Register specialization." -"simdlib_type_matrix_extract_first_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result extract_first cell for f64 protects that public Register specialization." -"simdlib_type_matrix_extract_first_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result extract_first cell for i16 protects that public Register specialization." -"simdlib_type_matrix_extract_first_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result extract_first cell for i32 protects that public Register specialization." -"simdlib_type_matrix_extract_first_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result extract_first cell for i64 protects that public Register specialization." -"simdlib_type_matrix_extract_first_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result extract_first cell for i8 protects that public Register specialization." -"simdlib_type_matrix_extract_first_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result extract_first cell for u16 protects that public Register specialization." -"simdlib_type_matrix_extract_first_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result extract_first cell for u32 protects that public Register specialization." -"simdlib_type_matrix_extract_first_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result extract_first cell for u64 protects that public Register specialization." -"simdlib_type_matrix_extract_first_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result extract_first cell for u8 protects that public Register specialization." -"simdlib_type_matrix_insert_last_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated insert_last cell for f32 protects that public Register specialization." -"simdlib_type_matrix_insert_last_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated insert_last cell for f64 protects that public Register specialization." -"simdlib_type_matrix_insert_last_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated insert_last cell for i16 protects that public Register specialization." -"simdlib_type_matrix_insert_last_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated insert_last cell for i32 protects that public Register specialization." -"simdlib_type_matrix_insert_last_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated insert_last cell for i64 protects that public Register specialization." -"simdlib_type_matrix_insert_last_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated insert_last cell for i8 protects that public Register specialization." -"simdlib_type_matrix_insert_last_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated insert_last cell for u16 protects that public Register specialization." -"simdlib_type_matrix_insert_last_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated insert_last cell for u32 protects that public Register specialization." -"simdlib_type_matrix_insert_last_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated insert_last cell for u64 protects that public Register specialization." -"simdlib_type_matrix_insert_last_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated insert_last cell for u8 protects that public Register specialization." -"simdlib_type_matrix_lane_sign_bits_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result lane_sign_bits cell for f32 protects that public Register specialization." -"simdlib_type_matrix_lane_sign_bits_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result lane_sign_bits cell for f64 protects that public Register specialization." -"simdlib_type_matrix_lane_sign_bits_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result lane_sign_bits cell for i16 protects that public Register specialization." -"simdlib_type_matrix_lane_sign_bits_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result lane_sign_bits cell for i32 protects that public Register specialization." -"simdlib_type_matrix_lane_sign_bits_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result lane_sign_bits cell for i64 protects that public Register specialization." -"simdlib_type_matrix_lane_sign_bits_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result lane_sign_bits cell for i8 protects that public Register specialization." -"simdlib_type_matrix_lane_sign_bits_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result lane_sign_bits cell for u16 protects that public Register specialization." -"simdlib_type_matrix_lane_sign_bits_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result lane_sign_bits cell for u32 protects that public Register specialization." -"simdlib_type_matrix_lane_sign_bits_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result lane_sign_bits cell for u64 protects that public Register specialization." -"simdlib_type_matrix_lane_sign_bits_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result lane_sign_bits cell for u8 protects that public Register specialization." -"simdlib_type_matrix_load_aligned_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_aligned transfer cell for f32 protects that public Register specialization." -"simdlib_type_matrix_load_aligned_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_aligned transfer cell for f64 protects that public Register specialization." -"simdlib_type_matrix_load_aligned_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_aligned transfer cell for i16 protects that public Register specialization." -"simdlib_type_matrix_load_aligned_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_aligned transfer cell for i32 protects that public Register specialization." -"simdlib_type_matrix_load_aligned_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_aligned transfer cell for i64 protects that public Register specialization." -"simdlib_type_matrix_load_aligned_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_aligned transfer cell for i8 protects that public Register specialization." -"simdlib_type_matrix_load_aligned_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_aligned transfer cell for u16 protects that public Register specialization." -"simdlib_type_matrix_load_aligned_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_aligned transfer cell for u32 protects that public Register specialization." -"simdlib_type_matrix_load_aligned_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_aligned transfer cell for u64 protects that public Register specialization." -"simdlib_type_matrix_load_aligned_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_aligned transfer cell for u8 protects that public Register specialization." -"simdlib_type_matrix_load_bytes_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_bytes transfer cell for f32 protects that public Register specialization." -"simdlib_type_matrix_load_bytes_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_bytes transfer cell for f64 protects that public Register specialization." -"simdlib_type_matrix_load_bytes_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_bytes transfer cell for i16 protects that public Register specialization." -"simdlib_type_matrix_load_bytes_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_bytes transfer cell for i32 protects that public Register specialization." -"simdlib_type_matrix_load_bytes_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_bytes transfer cell for i64 protects that public Register specialization." -"simdlib_type_matrix_load_bytes_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_bytes transfer cell for i8 protects that public Register specialization." -"simdlib_type_matrix_load_bytes_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_bytes transfer cell for u16 protects that public Register specialization." -"simdlib_type_matrix_load_bytes_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_bytes transfer cell for u32 protects that public Register specialization." -"simdlib_type_matrix_load_bytes_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_bytes transfer cell for u64 protects that public Register specialization." -"simdlib_type_matrix_load_bytes_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load_bytes transfer cell for u8 protects that public Register specialization." -"simdlib_type_matrix_load_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load transfer cell for f32 protects that public Register specialization." -"simdlib_type_matrix_load_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load transfer cell for f64 protects that public Register specialization." -"simdlib_type_matrix_load_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load transfer cell for i16 protects that public Register specialization." -"simdlib_type_matrix_load_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load transfer cell for i32 protects that public Register specialization." -"simdlib_type_matrix_load_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load transfer cell for i64 protects that public Register specialization." -"simdlib_type_matrix_load_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load transfer cell for i8 protects that public Register specialization." -"simdlib_type_matrix_load_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load transfer cell for u16 protects that public Register specialization." -"simdlib_type_matrix_load_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load transfer cell for u32 protects that public Register specialization." -"simdlib_type_matrix_load_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load transfer cell for u64 protects that public Register specialization." -"simdlib_type_matrix_load_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated load transfer cell for u8 protects that public Register specialization." -"simdlib_type_matrix_logical_shift_right_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count logical_shift_right cell for i16 protects that public Register specialization." -"simdlib_type_matrix_logical_shift_right_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count logical_shift_right cell for i32 protects that public Register specialization." -"simdlib_type_matrix_logical_shift_right_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count logical_shift_right cell for i64 protects that public Register specialization." -"simdlib_type_matrix_logical_shift_right_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count logical_shift_right cell for i8 protects that public Register specialization." -"simdlib_type_matrix_logical_shift_right_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count logical_shift_right cell for u16 protects that public Register specialization." -"simdlib_type_matrix_logical_shift_right_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count logical_shift_right cell for u32 protects that public Register specialization." -"simdlib_type_matrix_logical_shift_right_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count logical_shift_right cell for u64 protects that public Register specialization." -"simdlib_type_matrix_logical_shift_right_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count logical_shift_right cell for u8 protects that public Register specialization." -"simdlib_type_matrix_mask_all_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_all cell for f32 protects that public Register specialization." -"simdlib_type_matrix_mask_all_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_all cell for f64 protects that public Register specialization." -"simdlib_type_matrix_mask_all_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_all cell for i16 protects that public Register specialization." -"simdlib_type_matrix_mask_all_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_all cell for i32 protects that public Register specialization." -"simdlib_type_matrix_mask_all_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_all cell for i64 protects that public Register specialization." -"simdlib_type_matrix_mask_all_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_all cell for i8 protects that public Register specialization." -"simdlib_type_matrix_mask_all_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_all cell for u16 protects that public Register specialization." -"simdlib_type_matrix_mask_all_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_all cell for u32 protects that public Register specialization." -"simdlib_type_matrix_mask_all_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_all cell for u64 protects that public Register specialization." -"simdlib_type_matrix_mask_all_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_all cell for u8 protects that public Register specialization." -"simdlib_type_matrix_mask_and_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_and cell for f32 protects that public Register specialization." -"simdlib_type_matrix_mask_and_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_and cell for f64 protects that public Register specialization." -"simdlib_type_matrix_mask_and_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_and cell for i16 protects that public Register specialization." -"simdlib_type_matrix_mask_and_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_and cell for i32 protects that public Register specialization." -"simdlib_type_matrix_mask_and_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_and cell for i64 protects that public Register specialization." -"simdlib_type_matrix_mask_and_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_and cell for i8 protects that public Register specialization." -"simdlib_type_matrix_mask_and_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_and cell for u16 protects that public Register specialization." -"simdlib_type_matrix_mask_and_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_and cell for u32 protects that public Register specialization." -"simdlib_type_matrix_mask_and_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_and cell for u64 protects that public Register specialization." -"simdlib_type_matrix_mask_and_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_and cell for u8 protects that public Register specialization." -"simdlib_type_matrix_mask_any_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_any cell for f32 protects that public Register specialization." -"simdlib_type_matrix_mask_any_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_any cell for f64 protects that public Register specialization." -"simdlib_type_matrix_mask_any_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_any cell for i16 protects that public Register specialization." -"simdlib_type_matrix_mask_any_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_any cell for i32 protects that public Register specialization." -"simdlib_type_matrix_mask_any_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_any cell for i64 protects that public Register specialization." -"simdlib_type_matrix_mask_any_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_any cell for i8 protects that public Register specialization." -"simdlib_type_matrix_mask_any_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_any cell for u16 protects that public Register specialization." -"simdlib_type_matrix_mask_any_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_any cell for u32 protects that public Register specialization." -"simdlib_type_matrix_mask_any_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_any cell for u64 protects that public Register specialization." -"simdlib_type_matrix_mask_any_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_any cell for u8 protects that public Register specialization." -"simdlib_type_matrix_mask_bits_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_bits cell for f32 protects that public Register specialization." -"simdlib_type_matrix_mask_bits_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_bits cell for f64 protects that public Register specialization." -"simdlib_type_matrix_mask_bits_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_bits cell for i16 protects that public Register specialization." -"simdlib_type_matrix_mask_bits_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_bits cell for i32 protects that public Register specialization." -"simdlib_type_matrix_mask_bits_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_bits cell for i64 protects that public Register specialization." -"simdlib_type_matrix_mask_bits_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_bits cell for i8 protects that public Register specialization." -"simdlib_type_matrix_mask_bits_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_bits cell for u16 protects that public Register specialization." -"simdlib_type_matrix_mask_bits_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_bits cell for u32 protects that public Register specialization." -"simdlib_type_matrix_mask_bits_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_bits cell for u64 protects that public Register specialization." -"simdlib_type_matrix_mask_bits_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_bits cell for u8 protects that public Register specialization." -"simdlib_type_matrix_mask_none_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_none cell for f32 protects that public Register specialization." -"simdlib_type_matrix_mask_none_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_none cell for f64 protects that public Register specialization." -"simdlib_type_matrix_mask_none_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_none cell for i16 protects that public Register specialization." -"simdlib_type_matrix_mask_none_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_none cell for i32 protects that public Register specialization." -"simdlib_type_matrix_mask_none_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_none cell for i64 protects that public Register specialization." -"simdlib_type_matrix_mask_none_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_none cell for i8 protects that public Register specialization." -"simdlib_type_matrix_mask_none_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_none cell for u16 protects that public Register specialization." -"simdlib_type_matrix_mask_none_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_none cell for u32 protects that public Register specialization." -"simdlib_type_matrix_mask_none_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_none cell for u64 protects that public Register specialization." -"simdlib_type_matrix_mask_none_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result mask_none cell for u8 protects that public Register specialization." -"simdlib_type_matrix_mask_not_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_not cell for f32 protects that public Register specialization." -"simdlib_type_matrix_mask_not_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_not cell for f64 protects that public Register specialization." -"simdlib_type_matrix_mask_not_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_not cell for i16 protects that public Register specialization." -"simdlib_type_matrix_mask_not_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_not cell for i32 protects that public Register specialization." -"simdlib_type_matrix_mask_not_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_not cell for i64 protects that public Register specialization." -"simdlib_type_matrix_mask_not_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_not cell for i8 protects that public Register specialization." -"simdlib_type_matrix_mask_not_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_not cell for u16 protects that public Register specialization." -"simdlib_type_matrix_mask_not_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_not cell for u32 protects that public Register specialization." -"simdlib_type_matrix_mask_not_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_not cell for u64 protects that public Register specialization." -"simdlib_type_matrix_mask_not_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_not cell for u8 protects that public Register specialization." -"simdlib_type_matrix_mask_or_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_or cell for f32 protects that public Register specialization." -"simdlib_type_matrix_mask_or_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_or cell for f64 protects that public Register specialization." -"simdlib_type_matrix_mask_or_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_or cell for i16 protects that public Register specialization." -"simdlib_type_matrix_mask_or_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_or cell for i32 protects that public Register specialization." -"simdlib_type_matrix_mask_or_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_or cell for i64 protects that public Register specialization." -"simdlib_type_matrix_mask_or_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_or cell for i8 protects that public Register specialization." -"simdlib_type_matrix_mask_or_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_or cell for u16 protects that public Register specialization." -"simdlib_type_matrix_mask_or_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_or cell for u32 protects that public Register specialization." -"simdlib_type_matrix_mask_or_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_or cell for u64 protects that public Register specialization." -"simdlib_type_matrix_mask_or_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_or cell for u8 protects that public Register specialization." -"simdlib_type_matrix_mask_xor_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_xor cell for f32 protects that public Register specialization." -"simdlib_type_matrix_mask_xor_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_xor cell for f64 protects that public Register specialization." -"simdlib_type_matrix_mask_xor_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_xor cell for i16 protects that public Register specialization." -"simdlib_type_matrix_mask_xor_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_xor cell for i32 protects that public Register specialization." -"simdlib_type_matrix_mask_xor_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_xor cell for i64 protects that public Register specialization." -"simdlib_type_matrix_mask_xor_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_xor cell for i8 protects that public Register specialization." -"simdlib_type_matrix_mask_xor_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_xor cell for u16 protects that public Register specialization." -"simdlib_type_matrix_mask_xor_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_xor cell for u32 protects that public Register specialization." -"simdlib_type_matrix_mask_xor_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_xor cell for u64 protects that public Register specialization." -"simdlib_type_matrix_mask_xor_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated mask_xor cell for u8 protects that public Register specialization." -"simdlib_type_matrix_modulus_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","modulus-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated integer modulus cell for i16 is separated so compiler scheduling diagnostics cannot weaken other operations." -"simdlib_type_matrix_modulus_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","modulus-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated integer modulus cell for i32 is separated so compiler scheduling diagnostics cannot weaken other operations." -"simdlib_type_matrix_modulus_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","modulus-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated integer modulus cell for i64 is separated so compiler scheduling diagnostics cannot weaken other operations." -"simdlib_type_matrix_modulus_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","modulus-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated integer modulus cell for i8 is separated so compiler scheduling diagnostics cannot weaken other operations." -"simdlib_type_matrix_modulus_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","modulus-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated integer modulus cell for u16 is separated so compiler scheduling diagnostics cannot weaken other operations." -"simdlib_type_matrix_modulus_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","modulus-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated integer modulus cell for u32 is separated so compiler scheduling diagnostics cannot weaken other operations." -"simdlib_type_matrix_modulus_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","modulus-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated integer modulus cell for u64 is separated so compiler scheduling diagnostics cannot weaken other operations." -"simdlib_type_matrix_modulus_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","modulus-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The isolated integer modulus cell for u8 is separated so compiler scheduling diagnostics cannot weaken other operations." -"simdlib_type_matrix_movemask_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result movemask cell for f32 protects that public Register specialization." -"simdlib_type_matrix_movemask_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result movemask cell for f64 protects that public Register specialization." -"simdlib_type_matrix_movemask_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result movemask cell for i16 protects that public Register specialization." -"simdlib_type_matrix_movemask_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result movemask cell for i32 protects that public Register specialization." -"simdlib_type_matrix_movemask_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result movemask cell for i64 protects that public Register specialization." -"simdlib_type_matrix_movemask_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result movemask cell for i8 protects that public Register specialization." -"simdlib_type_matrix_movemask_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result movemask cell for u16 protects that public Register specialization." -"simdlib_type_matrix_movemask_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result movemask cell for u32 protects that public Register specialization." -"simdlib_type_matrix_movemask_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result movemask cell for u64 protects that public Register specialization." -"simdlib_type_matrix_movemask_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result movemask cell for u8 protects that public Register specialization." -"simdlib_type_matrix_multiply_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated multiply cell for f32 protects that public Register specialization." -"simdlib_type_matrix_multiply_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated multiply cell for f64 protects that public Register specialization." -"simdlib_type_matrix_multiply_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated multiply cell for i16 protects that public Register specialization." -"simdlib_type_matrix_multiply_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated multiply cell for i32 protects that public Register specialization." -"simdlib_type_matrix_multiply_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated multiply cell for i64 protects that public Register specialization." -"simdlib_type_matrix_multiply_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated multiply cell for i8 protects that public Register specialization." -"simdlib_type_matrix_multiply_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated multiply cell for u16 protects that public Register specialization." -"simdlib_type_matrix_multiply_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated multiply cell for u32 protects that public Register specialization." -"simdlib_type_matrix_multiply_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated multiply cell for u64 protects that public Register specialization." -"simdlib_type_matrix_multiply_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated multiply cell for u8 protects that public Register specialization." -"simdlib_type_matrix_negate_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated negate cell for f32 protects that public Register specialization." -"simdlib_type_matrix_negate_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated negate cell for f64 protects that public Register specialization." -"simdlib_type_matrix_negate_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated negate cell for i16 protects that public Register specialization." -"simdlib_type_matrix_negate_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated negate cell for i32 protects that public Register specialization." -"simdlib_type_matrix_negate_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated negate cell for i64 protects that public Register specialization." -"simdlib_type_matrix_negate_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated negate cell for i8 protects that public Register specialization." -"simdlib_type_matrix_negate_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated negate cell for u16 protects that public Register specialization." -"simdlib_type_matrix_negate_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated negate cell for u32 protects that public Register specialization." -"simdlib_type_matrix_negate_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated negate cell for u64 protects that public Register specialization." -"simdlib_type_matrix_negate_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated negate cell for u8 protects that public Register specialization." -"simdlib_type_matrix_not_equal_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result not_equal cell for f32 protects that public Register specialization." -"simdlib_type_matrix_not_equal_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result not_equal cell for f64 protects that public Register specialization." -"simdlib_type_matrix_not_equal_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result not_equal cell for i16 protects that public Register specialization." -"simdlib_type_matrix_not_equal_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result not_equal cell for i32 protects that public Register specialization." -"simdlib_type_matrix_not_equal_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result not_equal cell for i64 protects that public Register specialization." -"simdlib_type_matrix_not_equal_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result not_equal cell for i8 protects that public Register specialization." -"simdlib_type_matrix_not_equal_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result not_equal cell for u16 protects that public Register specialization." -"simdlib_type_matrix_not_equal_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result not_equal cell for u32 protects that public Register specialization." -"simdlib_type_matrix_not_equal_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result not_equal cell for u64 protects that public Register specialization." -"simdlib_type_matrix_not_equal_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated scalar-result not_equal cell for u8 protects that public Register specialization." -"simdlib_type_matrix_observe_array_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated observe_array transfer cell for f32 protects that public Register specialization." -"simdlib_type_matrix_observe_array_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated observe_array transfer cell for f64 protects that public Register specialization." -"simdlib_type_matrix_observe_array_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated observe_array transfer cell for i16 protects that public Register specialization." -"simdlib_type_matrix_observe_array_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated observe_array transfer cell for i32 protects that public Register specialization." -"simdlib_type_matrix_observe_array_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated observe_array transfer cell for i64 protects that public Register specialization." -"simdlib_type_matrix_observe_array_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated observe_array transfer cell for i8 protects that public Register specialization." -"simdlib_type_matrix_observe_array_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated observe_array transfer cell for u16 protects that public Register specialization." -"simdlib_type_matrix_observe_array_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated observe_array transfer cell for u32 protects that public Register specialization." -"simdlib_type_matrix_observe_array_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated observe_array transfer cell for u64 protects that public Register specialization." -"simdlib_type_matrix_observe_array_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated observe_array transfer cell for u8 protects that public Register specialization." -"simdlib_type_matrix_select_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated select cell for f32 protects that public Register specialization." -"simdlib_type_matrix_select_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated select cell for f64 protects that public Register specialization." -"simdlib_type_matrix_select_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated select cell for i16 protects that public Register specialization." -"simdlib_type_matrix_select_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated select cell for i32 protects that public Register specialization." -"simdlib_type_matrix_select_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated select cell for i64 protects that public Register specialization." -"simdlib_type_matrix_select_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated select cell for i8 protects that public Register specialization." -"simdlib_type_matrix_select_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated select cell for u16 protects that public Register specialization." -"simdlib_type_matrix_select_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated select cell for u32 protects that public Register specialization." -"simdlib_type_matrix_select_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated select cell for u64 protects that public Register specialization." -"simdlib_type_matrix_select_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated select cell for u8 protects that public Register specialization." -"simdlib_type_matrix_shift_left_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count shift_left cell for i16 protects that public Register specialization." -"simdlib_type_matrix_shift_left_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count shift_left cell for i32 protects that public Register specialization." -"simdlib_type_matrix_shift_left_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count shift_left cell for i64 protects that public Register specialization." -"simdlib_type_matrix_shift_left_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count shift_left cell for i8 protects that public Register specialization." -"simdlib_type_matrix_shift_left_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count shift_left cell for u16 protects that public Register specialization." -"simdlib_type_matrix_shift_left_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count shift_left cell for u32 protects that public Register specialization." -"simdlib_type_matrix_shift_left_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count shift_left cell for u64 protects that public Register specialization." -"simdlib_type_matrix_shift_left_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count shift_left cell for u8 protects that public Register specialization." -"simdlib_type_matrix_shift_right_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count shift_right cell for i16 protects that public Register specialization." -"simdlib_type_matrix_shift_right_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count shift_right cell for i32 protects that public Register specialization." -"simdlib_type_matrix_shift_right_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count shift_right cell for i64 protects that public Register specialization." -"simdlib_type_matrix_shift_right_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count shift_right cell for i8 protects that public Register specialization." -"simdlib_type_matrix_shift_right_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count shift_right cell for u16 protects that public Register specialization." -"simdlib_type_matrix_shift_right_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count shift_right cell for u32 protects that public Register specialization." -"simdlib_type_matrix_shift_right_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count shift_right cell for u64 protects that public Register specialization." -"simdlib_type_matrix_shift_right_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated runtime-count shift_right cell for u8 protects that public Register specialization." -"simdlib_type_matrix_store_aligned_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_aligned transfer cell for f32 protects that public Register specialization." -"simdlib_type_matrix_store_aligned_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_aligned transfer cell for f64 protects that public Register specialization." -"simdlib_type_matrix_store_aligned_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_aligned transfer cell for i16 protects that public Register specialization." -"simdlib_type_matrix_store_aligned_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_aligned transfer cell for i32 protects that public Register specialization." -"simdlib_type_matrix_store_aligned_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_aligned transfer cell for i64 protects that public Register specialization." -"simdlib_type_matrix_store_aligned_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_aligned transfer cell for i8 protects that public Register specialization." -"simdlib_type_matrix_store_aligned_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_aligned transfer cell for u16 protects that public Register specialization." -"simdlib_type_matrix_store_aligned_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_aligned transfer cell for u32 protects that public Register specialization." -"simdlib_type_matrix_store_aligned_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_aligned transfer cell for u64 protects that public Register specialization." -"simdlib_type_matrix_store_aligned_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_aligned transfer cell for u8 protects that public Register specialization." -"simdlib_type_matrix_store_bytes_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_bytes transfer cell for f32 protects that public Register specialization." -"simdlib_type_matrix_store_bytes_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_bytes transfer cell for f64 protects that public Register specialization." -"simdlib_type_matrix_store_bytes_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_bytes transfer cell for i16 protects that public Register specialization." -"simdlib_type_matrix_store_bytes_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_bytes transfer cell for i32 protects that public Register specialization." -"simdlib_type_matrix_store_bytes_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_bytes transfer cell for i64 protects that public Register specialization." -"simdlib_type_matrix_store_bytes_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_bytes transfer cell for i8 protects that public Register specialization." -"simdlib_type_matrix_store_bytes_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_bytes transfer cell for u16 protects that public Register specialization." -"simdlib_type_matrix_store_bytes_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_bytes transfer cell for u32 protects that public Register specialization." -"simdlib_type_matrix_store_bytes_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_bytes transfer cell for u64 protects that public Register specialization." -"simdlib_type_matrix_store_bytes_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store_bytes transfer cell for u8 protects that public Register specialization." -"simdlib_type_matrix_store_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store transfer cell for f32 protects that public Register specialization." -"simdlib_type_matrix_store_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store transfer cell for f64 protects that public Register specialization." -"simdlib_type_matrix_store_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store transfer cell for i16 protects that public Register specialization." -"simdlib_type_matrix_store_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store transfer cell for i32 protects that public Register specialization." -"simdlib_type_matrix_store_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store transfer cell for i64 protects that public Register specialization." -"simdlib_type_matrix_store_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store transfer cell for i8 protects that public Register specialization." -"simdlib_type_matrix_store_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store transfer cell for u16 protects that public Register specialization." -"simdlib_type_matrix_store_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store transfer cell for u32 protects that public Register specialization." -"simdlib_type_matrix_store_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store transfer cell for u64 protects that public Register specialization." -"simdlib_type_matrix_store_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated store transfer cell for u8 protects that public Register specialization." -"simdlib_type_matrix_subtract_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated subtract cell for f32 protects that public Register specialization." -"simdlib_type_matrix_subtract_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated subtract cell for f64 protects that public Register specialization." -"simdlib_type_matrix_subtract_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated subtract cell for i16 protects that public Register specialization." -"simdlib_type_matrix_subtract_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated subtract cell for i32 protects that public Register specialization." -"simdlib_type_matrix_subtract_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated subtract cell for i64 protects that public Register specialization." -"simdlib_type_matrix_subtract_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated subtract cell for i8 protects that public Register specialization." -"simdlib_type_matrix_subtract_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated subtract cell for u16 protects that public Register specialization." -"simdlib_type_matrix_subtract_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated subtract cell for u32 protects that public Register specialization." -"simdlib_type_matrix_subtract_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated subtract cell for u64 protects that public Register specialization." -"simdlib_type_matrix_subtract_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated subtract cell for u8 protects that public Register specialization." -"simdlib_type_matrix_zero_f32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated zero cell for f32 protects that public Register specialization." -"simdlib_type_matrix_zero_f64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated zero cell for f64 protects that public Register specialization." -"simdlib_type_matrix_zero_i16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated zero cell for i16 protects that public Register specialization." -"simdlib_type_matrix_zero_i32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated zero cell for i32 protects that public Register specialization." -"simdlib_type_matrix_zero_i64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated zero cell for i64 protects that public Register specialization." -"simdlib_type_matrix_zero_i8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated zero cell for i8 protects that public Register specialization." -"simdlib_type_matrix_zero_u16","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated zero cell for u16 protects that public Register specialization." -"simdlib_type_matrix_zero_u32","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated zero cell for u32 protects that public Register specialization." -"simdlib_type_matrix_zero_u64","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated zero cell for u64 protects that public Register specialization." -"simdlib_type_matrix_zero_u8","tests/codegen/RegisterTypeMatrixCodegenFixture.h","SSE4.2/128; AVX2/128; AVX2/256","public abstraction parity","tests/codegen/RegisterTypeMatrixCodegenRaw.cpp matching public Api operation","common-type-matrix","RegisterCodegen.128Sse42; RegisterCodegen.128Avx2; RegisterCodegen.256Avx2","retain","The canonical isolated zero cell for u8 protects that public Register specialization." -"simdlib_method_flags_codegen_binary","tests/method_flags/codegen/MethodFlagsFlagged.cpp","x64 configuration-probe builds; SSE4.2","compiler-attribute enforcement","tests/method_flags/codegen/MethodFlagsRaw.cpp equivalent raw compiler-attribute declaration","method-flags","MethodFlagsCodegen","retain","The SIMD_FLAGS declaration for binary must preserve the raw compiler-attribute ABI/code shape and its stack contract." -"simdlib_method_flags_codegen_flatten","tests/method_flags/codegen/MethodFlagsFlagged.cpp","x64 configuration-probe builds; SSE4.2","compiler-attribute enforcement","tests/method_flags/codegen/MethodFlagsRaw.cpp equivalent raw compiler-attribute declaration","method-flags","MethodFlagsCodegen","retain","The SIMD_FLAGS declaration for flatten must preserve the raw compiler-attribute ABI/code shape and its stack contract." -"simdlib_method_flags_codegen_forceinline","tests/method_flags/codegen/MethodFlagsFlagged.cpp","x64 configuration-probe builds; SSE4.2","compiler-attribute enforcement","tests/method_flags/codegen/MethodFlagsRaw.cpp equivalent raw compiler-attribute declaration","method-flags","MethodFlagsCodegen","retain","The SIMD_FLAGS declaration for forceinline must preserve the raw compiler-attribute ABI/code shape and its stack contract." -"simdlib_method_flags_codegen_load","tests/method_flags/codegen/MethodFlagsFlagged.cpp","x64 configuration-probe builds; SSE4.2","compiler-attribute enforcement","tests/method_flags/codegen/MethodFlagsRaw.cpp equivalent raw compiler-attribute declaration","method-flags","MethodFlagsCodegen","retain","The SIMD_FLAGS declaration for load must preserve the raw compiler-attribute ABI/code shape and its stack contract." -"simdlib_method_flags_codegen_register_result","tests/method_flags/codegen/MethodFlagsFlagged.cpp","x64 configuration-probe builds; SSE4.2","compiler-attribute enforcement","tests/method_flags/codegen/MethodFlagsRaw.cpp equivalent raw compiler-attribute declaration","method-flags","MethodFlagsCodegen","retain","The SIMD_FLAGS declaration for register_result must preserve the raw compiler-attribute ABI/code shape and its stack contract." -"simdlib_method_flags_codegen_scalar_result","tests/method_flags/codegen/MethodFlagsFlagged.cpp","x64 configuration-probe builds; SSE4.2","compiler-attribute enforcement","tests/method_flags/codegen/MethodFlagsRaw.cpp equivalent raw compiler-attribute declaration","method-flags","MethodFlagsCodegen","retain","The SIMD_FLAGS declaration for scalar_result must preserve the raw compiler-attribute ABI/code shape and its stack contract." -"simdlib_method_flags_codegen_store","tests/method_flags/codegen/MethodFlagsFlagged.cpp","x64 configuration-probe builds; SSE4.2","compiler-attribute enforcement","tests/method_flags/codegen/MethodFlagsRaw.cpp equivalent raw compiler-attribute declaration","method-flags","MethodFlagsCodegen","retain","The SIMD_FLAGS declaration for store must preserve the raw compiler-attribute ABI/code shape and its stack contract." -"simdlib_method_flags_codegen_ternary","tests/method_flags/codegen/MethodFlagsFlagged.cpp","x64 configuration-probe builds; SSE4.2","compiler-attribute enforcement","tests/method_flags/codegen/MethodFlagsRaw.cpp equivalent raw compiler-attribute declaration","method-flags","MethodFlagsCodegen","retain","The SIMD_FLAGS declaration for ternary must preserve the raw compiler-attribute ABI/code shape and its stack contract." -"simdlib_method_flags_codegen_unary","tests/method_flags/codegen/MethodFlagsFlagged.cpp","x64 configuration-probe builds; SSE4.2","compiler-attribute enforcement","tests/method_flags/codegen/MethodFlagsRaw.cpp equivalent raw compiler-attribute declaration","method-flags","MethodFlagsCodegen","retain","The SIMD_FLAGS declaration for unary must preserve the raw compiler-attribute ABI/code shape and its stack contract." -"simdlib_method_flags_flatten_leaf","tests/method_flags/codegen/MethodFlagsFlagged.cpp","x64 configuration-probe builds; SSE4.2","compiler-attribute enforcement","paired legacy Flatten helper declaration","method-flags helper-call inspection","MethodFlagsCodegen","retain","The helper must disappear from the flatten caller; the validation rejects any remaining call." -"simdlib_method_flags_force_leaf","tests/method_flags/codegen/MethodFlagsFlagged.cpp","x64 configuration-probe builds; SSE4.2","compiler-attribute enforcement","paired legacy ForceInline helper declaration","method-flags helper-call inspection","MethodFlagsCodegen","retain","The helper must disappear from the forceinline caller; the validation rejects any remaining call." diff --git a/docs/RegisterProposal.md b/docs/RegisterContract.md similarity index 60% rename from docs/RegisterProposal.md rename to docs/RegisterContract.md index ec766e3..56d0cea 100644 --- a/docs/RegisterProposal.md +++ b/docs/RegisterContract.md @@ -1,4 +1,4 @@ -# Register Class Proposal +# Register Contract Status: implemented and qualified public interface. Supported cells and explicit exceptions are controlled by `RegisterQualification.md`. @@ -22,17 +22,20 @@ supports natural expression chaining and keeps `Detail` types out of ordinary call sites. The existing `Api` remains the C++20 interface and a supported compatibility and -backend-facing surface alongside `Register`. Span-wide -algorithms and partial-register handling remain outside `Register`. +backend-facing surface alongside `Register`. Span-wide algorithms remain +outside `Register`. Fixed compile-time partial native values are owned by the +independent sibling `PartialRegister`; its contract and qualification are +documented in `PartialRegisterOperationLedger.md` and +`PartialRegisterQualification.md`. ## Decision status -| Status | Decisions | -| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Controlling requirement | Template order is ``; every hardware lane is active; default construction uses the native zero-register operation; comparison behavior matches the selected hardware intrinsic; the abstraction has zero runtime overhead in supported configurations. | +| Status | Decisions | +| --- | --- | +| Controlling requirement | Template order is ``; every hardware lane is active; default construction uses the native zero-register operation; comparison behavior matches the selected hardware intrinsic; the abstraction has zero runtime overhead in supported configurations. | | Implemented public design | Explicit register width with `NativeRegister` for target-selected width; C++23 explicit-object members for register-consuming operations; explicit scalar broadcast; `RegisterMask` predicates; fixed-extent element and byte transfers; operation names and results defined by the migration ledger. | -| Intentionally excluded | Partial and unsafe loads, automatic lane filling, collection transforms, native-order construction, ambiguous `expand`/`compress`, implementation-specific runtime rearrangements, and multi-register widening results. | -| Qualification contract | The supported compiler, ISA, type, width, generated-code, and non-inlined calling-boundary cells are defined in `docs/RegisterQualification.md`; individual outcomes are emitted as build receipts, reports, provenance files, and logs. | +| Intentionally excluded | Partial and unsafe loads, automatic lane filling, collection transforms, native-order construction, ambiguous `expand`/`compress`, implementation-specific runtime rearrangements, and multi-register widening results. | +| Qualification contract | The supported compiler, ISA, type, width, generated-code, and non-inlined calling-boundary cells are defined in `docs/RegisterQualification.md`; individual outcomes are emitted as build receipts, reports, provenance files, and logs. | ## Motivation @@ -100,17 +103,20 @@ output.store(destination); ## Responsibility boundaries -| Surface | Responsibility | Partial data | -| ----------------------------------------- | ----------------------------------------------- | -------------------------------------- | -| `Register` | One complete hardware register | Rejected | -| `SimdVector` | One fixed logical value | Inactive lanes are managed by the type | -| `SimdAlgo` and future `Tensor` operations | Collections and batches | Tail policy belongs to the algorithm | -| `Api` | Compatibility facade and implementation routing | Existing behavior remains supported | +| Surface | Responsibility | Partial data | +| --- | --- | --- | +| `Register` | One complete hardware register | Rejected | +| `PartialRegister` | One hardware register with a compile-time contiguous active prefix | Inactive suffix is always all-bits-zero | +| `SimdVector` | One fixed logical value | Inactive lanes are managed by the type | +| `SimdAlgo` and future `Tensor` operations | Collections and batches | Tail policy belongs to the algorithm | +| `Api` | Compatibility facade and implementation routing | Existing behavior remains supported | `Register` deliberately has no equivalent to `Api::load_partial`, -`Api::set_partial`, or `Api::setr_partial`. A caller with fewer than -`lane_count` elements must use a higher-level abstraction or explicitly stage -a complete register with a fill policy chosen by that caller. +`Api::set_partial`, or `Api::setr_partial`. A caller with a compile-time logical +prefix can use `PartialRegister`; a collection with a runtime tail continues to +use its owning algorithm's tail policy. Explicitly staging a complete register +remains available when a caller needs a custom fill policy rather than the +PartialRegister all-bits-zero suffix contract. ### Replacement boundary @@ -124,7 +130,8 @@ not mean that every static member currently located on `Api` becomes a `Api` retains these supported responsibilities: - Span-wide `transform` and `transform_pack` collection algorithms. -- Partial-register staging used internally to implement collection tails. +- Dynamic partial-register staging used internally to implement collection + tails. Public fixed-prefix values are represented by `PartialRegister`. - `load_unsafe`, whose dynamic-extent precondition is unsuitable for the restrictive `Register` interface. - Native-order construction and implementation-specific overloads retained for @@ -150,10 +157,10 @@ initial ABI probes have been validated: ```cpp #if defined(__cpp_explicit_this_parameter) && \ - __cpp_explicit_this_parameter >= 202110L + __cpp_explicit_this_parameter >= 202110L #define SIMDLIB_REGISTER_INTERFACE_AVAILABLE 1 #elif defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1944 && \ - defined(_MSVC_LANG) && _MSVC_LANG > 202002L + defined(_MSVC_LANG) && _MSVC_LANG > 202002L #define SIMDLIB_REGISTER_INTERFACE_AVAILABLE 1 #else #define SIMDLIB_REGISTER_INTERFACE_AVAILABLE 0 @@ -205,12 +212,12 @@ ODR hazard. The preprocessor macro is the only public availability query. The core target retains its existing C++20 compiler matrix. Register support is a narrower, separately validated matrix: -| Compiler family | Initial Register floor | Platform | Language mode | Availability path | -| --------------- | ---------------------- | ----------- | ---------------- | ------------------------------------ | -| Microsoft C++ | MSVC 19.44 | Windows x64 | `/std:c++latest` | `_MSC_VER` and `_MSVC_LANG` fallback | -| clang-cl | 20 | Windows x64 | C++23 | Standard feature-test macro | -| Clang | 22 | Linux x64 | C++23 | Standard feature-test macro | -| GCC | 14 | Linux x64 | C++23 | Standard feature-test macro | +| Compiler family | Initial Register floor | Platform | Language mode | Availability path | +| --- | --- | --- | --- | --- | +| Microsoft C++ | MSVC 19.44 | Windows x64 | `/std:c++latest` | `_MSC_VER` and `_MSVC_LANG` fallback | +| clang-cl | 20 | Windows x64 | C++23 | Standard feature-test macro | +| Clang | 22 | Linux x64 | C++23 | Standard feature-test macro | +| GCC | 14 | Linux x64 | C++23 | Standard feature-test macro | Linux x64 GCC 13.2 remains in the core C++20 matrix and must compile the umbrella header with `SIMDLIB_REGISTER_INTERFACE_AVAILABLE == 0`. A compiler is added to the @@ -231,16 +238,16 @@ add_library(SimdLib::Register ALIAS SimdLibRegister) target_link_libraries(SimdLibRegister INTERFACE SimdLib::SimdLib) target_compile_features(SimdLibRegister INTERFACE cxx_std_23) target_compile_options( - SimdLibRegister - INTERFACE $<$:/std:c++latest>) + SimdLibRegister + INTERFACE $<$:/std:c++latest>) target_compile_definitions( - SimdLibRegister - INTERFACE SIMDLIB_REQUIRE_REGISTER_INTERFACE=1) + SimdLibRegister + INTERFACE SIMDLIB_REQUIRE_REGISTER_INTERFACE=1) ``` ```cpp #if defined(SIMDLIB_REQUIRE_REGISTER_INTERFACE) && \ - !SIMDLIB_REGISTER_INTERFACE_AVAILABLE + !SIMDLIB_REGISTER_INTERFACE_AVAILABLE #error "SimdLib::Register requires supported C++23 explicit object parameters." #endif ``` @@ -276,14 +283,14 @@ namespace SimdLib */ template inline constexpr bool is_register_available_v = - SimdLib::is_api_available_v; + SimdLib::is_api_available_v; /** * @brief Constrains a type and width to a supported complete SIMD register. */ template concept RegisterAvailable = - is_register_available_v; + is_register_available_v; /** * @brief Owns one complete SIMD register whose lanes are all active. @@ -291,7 +298,7 @@ concept RegisterAvailable = * @tparam register_width Width of the native register in bits. */ template - requires RegisterAvailable + requires RegisterAvailable class Register final; /** @@ -299,10 +306,10 @@ class Register final; * @tparam element_t Scalar interpretation of each register lane. */ template - requires RegisterAvailable + requires RegisterAvailable using NativeRegister = Register< - element_t, - is_register_available_v ? 256 : 128>; + element_t, + is_register_available_v ? 256 : 128>; } // namespace SimdLib ``` @@ -356,7 +363,7 @@ The operation ledger defines the remaining operation names. * @tparam bits Width of the associated register in bits. */ template - requires RegisterAvailable + requires RegisterAvailable class RegisterMask; /** @@ -365,192 +372,192 @@ class RegisterMask; * @tparam bits Width of the native register in bits. */ template - requires RegisterAvailable + requires RegisterAvailable class Register final { public: - using element_type = element_t; - using api_type = Api; - using native_type = typename api_type::vector_t; - using mask_type = RegisterMask; - - constexpr static inline std::size_t register_width = bits; - constexpr static inline std::size_t byte_count = api_type::byte_count; - constexpr static inline std::size_t lane_count = api_type::element_count; - - /** @brief Owns the complete native register value represented by this aggregate. */ - native_type native = api_type::setzero(); - - /** - * @brief Returns a register with every active lane set to zero. - * @return Fully initialized zero register. - */ - [[nodiscard]] static constexpr Register SIMD_FLAGS(Out, ForceInline) zero() noexcept; - - /** - * @brief Broadcasts one scalar value to every active lane. - * @param value Scalar value to broadcast. - * @return Register containing `value` in every lane. - */ - [[nodiscard]] static constexpr Register SIMD_FLAGS(Out, ForceInline) broadcast( - element_type value) noexcept; - - /** - * @brief Constructs a register from exactly one complete logical lane list. - * @param lanes Values in low-to-high logical lane order. - * @return Register containing all supplied lane values. - */ - template ... lane_types> - requires(sizeof...(lane_types) == lane_count) - [[nodiscard]] static constexpr Register SIMD_FLAGS(Out, ForceInline) from_lanes( - lane_types &&...lanes) noexcept; - - /** - * @brief Constructs a register from one complete fixed-size lane array. - * @param source Source containing every active lane in logical order. - * @return Register containing all source lane values. - */ - [[nodiscard]] static constexpr Register SIMD_FLAGS(Out, ForceInline) from_array( - const std::array &source) noexcept; - - /** - * @brief Loads a complete register from potentially unaligned storage. - * @param source Source containing exactly one register of elements. - * @return Register loaded from `source`. - */ - [[nodiscard]] static Register SIMD_FLAGS(Out, ForceInline) load( - std::span source) noexcept; - - /** - * @brief Loads a complete register from register-aligned storage. - * @param source Aligned source containing exactly one register of elements. - * @return Register loaded from `source`. - */ - [[nodiscard]] static Register SIMD_FLAGS(Out, ForceInline) load_aligned( - std::span source) noexcept; - - /** - * @brief Loads one complete register bit pattern from raw bytes. - * @param source Source containing exactly one register of bytes. - * @return Register containing the source bit pattern. - */ - [[nodiscard]] static Register SIMD_FLAGS(Out, ForceInline) load_bytes( - std::span source) noexcept; - - /** - * @brief Stores every active lane to potentially unaligned storage. - * @param value Register to store. - * @param destination Destination for exactly one register of elements. - */ - void SIMD_FLAGS(In, ForceInline) store( - this Register value, - std::span destination) noexcept; - - /** - * @brief Stores every active lane to register-aligned storage. - * @param value Register to store. - * @param destination Aligned destination for one complete register. - */ - void SIMD_FLAGS(In, ForceInline) store_aligned( - this Register value, - std::span destination) noexcept; - - /** - * @brief Stores the complete register bit pattern to raw bytes. - * @param value Register to store. - * @param destination Destination containing exactly one register of bytes. - */ - void SIMD_FLAGS(In, ForceInline) store_bytes( - this Register value, - std::span destination) noexcept; - - /** - * @brief Copies every active lane into a fixed-size array. - * @param value Register to copy. - * @return Array containing all lanes in low-to-high logical order. - */ - [[nodiscard]] constexpr - std::array SIMD_FLAGS(In, ForceInline) to_array( - this Register value) noexcept; - - /** - * @brief Returns one compile-time-selected lane. - * @tparam index Logical lane index. - * @param value Register containing the selected lane. - * @return Copy of the selected lane. - */ - template - requires(index < lane_count) - [[nodiscard]] constexpr element_type SIMD_FLAGS(In, ForceInline) lane( - this Register value) noexcept; - - /** - * @brief Returns the wrapped native register for intrinsic interoperation. - * @param value Register to unwrap. - * @return Complete native register value. - */ - [[nodiscard]] constexpr native_type SIMD_FLAGS(InOut, ForceInline) native( - this Register value) noexcept; - - /** - * @brief Adds corresponding lanes. - * @param lhs Left-hand register. - * @param rhs Right-hand register. - * @return Per-lane sum. - */ - [[nodiscard]] Register SIMD_FLAGS(InOut, ForceInline) operator+( - this Register lhs, - Register rhs) noexcept; - - /** - * @brief Subtracts corresponding lanes. - * @param lhs Left-hand register. - * @param rhs Right-hand register. - * @return Per-lane difference. - */ - [[nodiscard]] Register SIMD_FLAGS(InOut, ForceInline) operator-( - this Register lhs, - Register rhs) noexcept; - - /** - * @brief Multiplies corresponding lanes. - * @param lhs Left-hand register. - * @param rhs Right-hand register. - * @return Per-lane product. - */ - [[nodiscard]] Register SIMD_FLAGS(InOut, ForceInline) operator*( - this Register lhs, - Register rhs) noexcept; - - /** - * @brief Compares corresponding lanes for equality. - * @param lhs Left-hand register. - * @param rhs Right-hand register. - * @return Register-shaped lane predicate. - */ - [[nodiscard]] constexpr mask_type SIMD_FLAGS(InOut, ForceInline) compare_equal( - this Register lhs, - Register rhs) noexcept; - - /** - * @brief Tests whether every corresponding lane compares equal. - * @param lhs Left-hand register. - * @param rhs Right-hand register. - * @return `true` when all lanes compare equal. - */ - [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) operator==( - this Register lhs, - Register rhs) noexcept; - - /** - * @brief Tests whether any corresponding lane compares unequal. - * @param lhs Left-hand register. - * @param rhs Right-hand register. - * @return `true` when at least one lane compares unequal. - */ - [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) operator!=( - this Register lhs, - Register rhs) noexcept; + using element_type = element_t; + using api_type = Api; + using native_type = typename api_type::vector_t; + using mask_type = RegisterMask; + + constexpr static inline std::size_t register_width = bits; + constexpr static inline std::size_t byte_count = api_type::byte_count; + constexpr static inline std::size_t lane_count = api_type::element_count; + + /** @brief Owns the complete native register value represented by this aggregate. */ + native_type native = api_type::setzero(); + + /** + * @brief Returns a register with every active lane set to zero. + * @return Fully initialized zero register. + */ + [[nodiscard]] static constexpr Register SIMD_FLAGS(Out, ForceInline) zero() noexcept; + + /** + * @brief Broadcasts one scalar value to every active lane. + * @param value Scalar value to broadcast. + * @return Register containing `value` in every lane. + */ + [[nodiscard]] static constexpr Register SIMD_FLAGS(Out, ForceInline) broadcast( + element_type value) noexcept; + + /** + * @brief Constructs a register from exactly one complete logical lane list. + * @param lanes Values in low-to-high logical lane order. + * @return Register containing all supplied lane values. + */ + template ... lane_types> + requires(sizeof...(lane_types) == lane_count) + [[nodiscard]] static constexpr Register SIMD_FLAGS(Out, ForceInline) from_lanes( + lane_types &&...lanes) noexcept; + + /** + * @brief Constructs a register from one complete fixed-size lane array. + * @param source Source containing every active lane in logical order. + * @return Register containing all source lane values. + */ + [[nodiscard]] static constexpr Register SIMD_FLAGS(Out, ForceInline) from_array( + const std::array &source) noexcept; + + /** + * @brief Loads a complete register from potentially unaligned storage. + * @param source Source containing exactly one register of elements. + * @return Register loaded from `source`. + */ + [[nodiscard]] static Register SIMD_FLAGS(Out, ForceInline) load( + std::span source) noexcept; + + /** + * @brief Loads a complete register from register-aligned storage. + * @param source Aligned source containing exactly one register of elements. + * @return Register loaded from `source`. + */ + [[nodiscard]] static Register SIMD_FLAGS(Out, ForceInline) load_aligned( + std::span source) noexcept; + + /** + * @brief Loads one complete register bit pattern from raw bytes. + * @param source Source containing exactly one register of bytes. + * @return Register containing the source bit pattern. + */ + [[nodiscard]] static Register SIMD_FLAGS(Out, ForceInline) load_bytes( + std::span source) noexcept; + + /** + * @brief Stores every active lane to potentially unaligned storage. + * @param value Register to store. + * @param destination Destination for exactly one register of elements. + */ + void SIMD_FLAGS(In, ForceInline) store( + this Register value, + std::span destination) noexcept; + + /** + * @brief Stores every active lane to register-aligned storage. + * @param value Register to store. + * @param destination Aligned destination for one complete register. + */ + void SIMD_FLAGS(In, ForceInline) store_aligned( + this Register value, + std::span destination) noexcept; + + /** + * @brief Stores the complete register bit pattern to raw bytes. + * @param value Register to store. + * @param destination Destination containing exactly one register of bytes. + */ + void SIMD_FLAGS(In, ForceInline) store_bytes( + this Register value, + std::span destination) noexcept; + + /** + * @brief Copies every active lane into a fixed-size array. + * @param value Register to copy. + * @return Array containing all lanes in low-to-high logical order. + */ + [[nodiscard]] constexpr + std::array SIMD_FLAGS(In, ForceInline) to_array( + this Register value) noexcept; + + /** + * @brief Returns one compile-time-selected lane. + * @tparam index Logical lane index. + * @param value Register containing the selected lane. + * @return Copy of the selected lane. + */ + template + requires(index < lane_count) + [[nodiscard]] constexpr element_type SIMD_FLAGS(In, ForceInline) lane( + this Register value) noexcept; + + /** + * @brief Returns the wrapped native register for intrinsic interoperation. + * @param value Register to unwrap. + * @return Complete native register value. + */ + [[nodiscard]] constexpr native_type SIMD_FLAGS(InOut, ForceInline) native( + this Register value) noexcept; + + /** + * @brief Adds corresponding lanes. + * @param lhs Left-hand register. + * @param rhs Right-hand register. + * @return Per-lane sum. + */ + [[nodiscard]] Register SIMD_FLAGS(InOut, ForceInline) operator+( + this Register lhs, + Register rhs) noexcept; + + /** + * @brief Subtracts corresponding lanes. + * @param lhs Left-hand register. + * @param rhs Right-hand register. + * @return Per-lane difference. + */ + [[nodiscard]] Register SIMD_FLAGS(InOut, ForceInline) operator-( + this Register lhs, + Register rhs) noexcept; + + /** + * @brief Multiplies corresponding lanes. + * @param lhs Left-hand register. + * @param rhs Right-hand register. + * @return Per-lane product. + */ + [[nodiscard]] Register SIMD_FLAGS(InOut, ForceInline) operator*( + this Register lhs, + Register rhs) noexcept; + + /** + * @brief Compares corresponding lanes for equality. + * @param lhs Left-hand register. + * @param rhs Right-hand register. + * @return Register-shaped lane predicate. + */ + [[nodiscard]] constexpr mask_type SIMD_FLAGS(InOut, ForceInline) compare_equal( + this Register lhs, + Register rhs) noexcept; + + /** + * @brief Tests whether every corresponding lane compares equal. + * @param lhs Left-hand register. + * @param rhs Right-hand register. + * @return `true` when all lanes compare equal. + */ + [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) operator==( + this Register lhs, + Register rhs) noexcept; + + /** + * @brief Tests whether any corresponding lane compares unequal. + * @param lhs Left-hand register. + * @param rhs Right-hand register. + * @return `true` when at least one lane compares unequal. + */ + [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) operator!=( + this Register lhs, + Register rhs) noexcept; }; ``` @@ -583,7 +590,7 @@ Arithmetic and bitwise operators should initially accept only another ```cpp const auto adjusted = values * FloatRegister::broadcast(scale) + - FloatRegister::broadcast(offset); + FloatRegister::broadcast(offset); ``` This is intentionally more restrictive than `SimdVector`. It makes broadcast @@ -629,148 +636,148 @@ predicate lanes. * @tparam bits Width of the associated register in bits. */ template - requires RegisterAvailable + requires RegisterAvailable class RegisterMask final { public: - using register_type = Register; - using api_type = typename register_type::api_type; - using native_type = typename register_type::native_type; - using bits_type = std::conditional_t< - (register_type::lane_count <= 32), - std::uint32_t, - std::uint64_t>; - - constexpr static inline std::size_t register_width = bits; - constexpr static inline std::size_t lane_count = register_type::lane_count; - - /** - * @brief Owns the complete native predicate value represented by this aggregate. - * @pre Every logical lane is either all-zero or all-one when initialized directly. - */ - native_type native = api_type::setzero(); - - /** - * @brief Tests whether any predicate lane is set. - * @param value Predicate register to test. - * @return `true` when at least one lane is true. - */ - [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) any( - this RegisterMask value) noexcept; - - /** - * @brief Tests whether every predicate lane is set. - * @param value Predicate register to test. - * @return `true` when every lane is true. - */ - [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) all( - this RegisterMask value) noexcept; - - /** - * @brief Tests whether no predicate lane is set. - * @param value Predicate register to test. - * @return `true` when every lane is false. - */ - [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) none( - this RegisterMask value) noexcept; - - /** - * @brief Returns one compact bit per logical predicate lane. - * @param value Predicate register to reduce. - * @return Bit `i` set exactly when lane `i` is true. - */ - [[nodiscard]] constexpr bits_type SIMD_FLAGS(In, ForceInline) bits( - this RegisterMask value) noexcept; - - /** - * @brief Returns the wrapped native predicate register for intrinsic - * interoperation. - * @param value Predicate register to unwrap. - * @return Complete native predicate register value. - */ - [[nodiscard]] constexpr native_type SIMD_FLAGS(InOut, ForceInline) native( - this RegisterMask value) noexcept; - - /** - * @brief Selects lanes from two registers according to a predicate. - * @param condition Predicate controlling each selected lane. - * @param when_true Values selected for true predicate lanes. - * @param when_false Values selected for false predicate lanes. - * @return Register containing the selected values. - */ - [[nodiscard]] register_type SIMD_FLAGS(InOut, ForceInline) select( - this RegisterMask condition, - register_type when_true, - register_type when_false) noexcept; - - /** - * @brief Computes the intersection of two predicate registers. - * @param lhs Left-hand predicate register. - * @param rhs Right-hand predicate register. - * @return Predicate that is true where both inputs are true. - */ - [[nodiscard]] constexpr RegisterMask SIMD_FLAGS(InOut, ForceInline) operator&( - this RegisterMask lhs, - RegisterMask rhs) noexcept; - - /** - * @brief Computes the union of two predicate registers. - * @param lhs Left-hand predicate register. - * @param rhs Right-hand predicate register. - * @return Predicate that is true where either input is true. - */ - [[nodiscard]] constexpr RegisterMask SIMD_FLAGS(InOut, ForceInline) operator|( - this RegisterMask lhs, - RegisterMask rhs) noexcept; - - /** - * @brief Computes the exclusive union of two predicate registers. - * @param lhs Left-hand predicate register. - * @param rhs Right-hand predicate register. - * @return Predicate that is true where exactly one input is true. - */ - [[nodiscard]] constexpr RegisterMask SIMD_FLAGS(InOut, ForceInline) operator^( - this RegisterMask lhs, - RegisterMask rhs) noexcept; - - /** - * @brief Inverts every predicate lane. - * @param value Predicate register to invert. - * @return Predicate containing the inverse of every input lane. - */ - [[nodiscard]] constexpr RegisterMask SIMD_FLAGS(InOut, ForceInline) operator~( - this RegisterMask value) noexcept; - - /* - * Disabled compound assignment operators: their convenience does not justify - * the mutable-reference API surface, and MSVC 19.44 emits a redundant 32-byte - * stack-alignment frame for 256-bit wrapper mutation through references. - * Prefer lhs = lhs & rhs, lhs = lhs | rhs, or lhs = lhs ^ rhs. - * - /// @brief Intersects this predicate with another predicate. - /// @param lhs Predicate register to update. - /// @param rhs Right-hand predicate register. - /// @return Reference to the updated predicate. - constexpr auto SIMD_FLAGS(In, ForceInline) operator&=( - this RegisterMask &lhs, - RegisterMask rhs) noexcept -> RegisterMask &; - - /// @brief Unites this predicate with another predicate. - /// @param lhs Predicate register to update. - /// @param rhs Right-hand predicate register. - /// @return Reference to the updated predicate. - constexpr auto SIMD_FLAGS(In, ForceInline) operator|=( - this RegisterMask &lhs, - RegisterMask rhs) noexcept -> RegisterMask &; - - /// @brief Exclusively combines this predicate with another predicate. - /// @param lhs Predicate register to update. - /// @param rhs Right-hand predicate register. - /// @return Reference to the updated predicate. - constexpr auto SIMD_FLAGS(In, ForceInline) operator^=( - this RegisterMask &lhs, - RegisterMask rhs) noexcept -> RegisterMask &; - */ + using register_type = Register; + using api_type = typename register_type::api_type; + using native_type = typename register_type::native_type; + using bits_type = std::conditional_t< + (register_type::lane_count <= 32), + std::uint32_t, + std::uint64_t>; + + constexpr static inline std::size_t register_width = bits; + constexpr static inline std::size_t lane_count = register_type::lane_count; + + /** + * @brief Owns the complete native predicate value represented by this aggregate. + * @pre Every logical lane is either all-zero or all-one when initialized directly. + */ + native_type native = api_type::setzero(); + + /** + * @brief Tests whether any predicate lane is set. + * @param value Predicate register to test. + * @return `true` when at least one lane is true. + */ + [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) any( + this RegisterMask value) noexcept; + + /** + * @brief Tests whether every predicate lane is set. + * @param value Predicate register to test. + * @return `true` when every lane is true. + */ + [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) all( + this RegisterMask value) noexcept; + + /** + * @brief Tests whether no predicate lane is set. + * @param value Predicate register to test. + * @return `true` when every lane is false. + */ + [[nodiscard]] constexpr bool SIMD_FLAGS(In, ForceInline) none( + this RegisterMask value) noexcept; + + /** + * @brief Returns one compact bit per logical predicate lane. + * @param value Predicate register to reduce. + * @return Bit `i` set exactly when lane `i` is true. + */ + [[nodiscard]] constexpr bits_type SIMD_FLAGS(In, ForceInline) bits( + this RegisterMask value) noexcept; + + /** + * @brief Returns the wrapped native predicate register for intrinsic + * interoperation. + * @param value Predicate register to unwrap. + * @return Complete native predicate register value. + */ + [[nodiscard]] constexpr native_type SIMD_FLAGS(InOut, ForceInline) native( + this RegisterMask value) noexcept; + + /** + * @brief Selects lanes from two registers according to a predicate. + * @param condition Predicate controlling each selected lane. + * @param when_true Values selected for true predicate lanes. + * @param when_false Values selected for false predicate lanes. + * @return Register containing the selected values. + */ + [[nodiscard]] register_type SIMD_FLAGS(InOut, ForceInline) select( + this RegisterMask condition, + register_type when_true, + register_type when_false) noexcept; + + /** + * @brief Computes the intersection of two predicate registers. + * @param lhs Left-hand predicate register. + * @param rhs Right-hand predicate register. + * @return Predicate that is true where both inputs are true. + */ + [[nodiscard]] constexpr RegisterMask SIMD_FLAGS(InOut, ForceInline) operator&( + this RegisterMask lhs, + RegisterMask rhs) noexcept; + + /** + * @brief Computes the union of two predicate registers. + * @param lhs Left-hand predicate register. + * @param rhs Right-hand predicate register. + * @return Predicate that is true where either input is true. + */ + [[nodiscard]] constexpr RegisterMask SIMD_FLAGS(InOut, ForceInline) operator|( + this RegisterMask lhs, + RegisterMask rhs) noexcept; + + /** + * @brief Computes the exclusive union of two predicate registers. + * @param lhs Left-hand predicate register. + * @param rhs Right-hand predicate register. + * @return Predicate that is true where exactly one input is true. + */ + [[nodiscard]] constexpr RegisterMask SIMD_FLAGS(InOut, ForceInline) operator^( + this RegisterMask lhs, + RegisterMask rhs) noexcept; + + /** + * @brief Inverts every predicate lane. + * @param value Predicate register to invert. + * @return Predicate containing the inverse of every input lane. + */ + [[nodiscard]] constexpr RegisterMask SIMD_FLAGS(InOut, ForceInline) operator~( + this RegisterMask value) noexcept; + + /* + * Disabled compound assignment operators: their convenience does not justify + * the mutable-reference API surface, and MSVC 19.44 emits a redundant 32-byte + * stack-alignment frame for 256-bit wrapper mutation through references. + * Prefer lhs = lhs & rhs, lhs = lhs | rhs, or lhs = lhs ^ rhs. + * + /// @brief Intersects this predicate with another predicate. + /// @param lhs Predicate register to update. + /// @param rhs Right-hand predicate register. + /// @return Reference to the updated predicate. + constexpr auto SIMD_FLAGS(In, ForceInline) operator&=( + this RegisterMask &lhs, + RegisterMask rhs) noexcept -> RegisterMask &; + + /// @brief Unites this predicate with another predicate. + /// @param lhs Predicate register to update. + /// @param rhs Right-hand predicate register. + /// @return Reference to the updated predicate. + constexpr auto SIMD_FLAGS(In, ForceInline) operator|=( + this RegisterMask &lhs, + RegisterMask rhs) noexcept -> RegisterMask &; + + /// @brief Exclusively combines this predicate with another predicate. + /// @param lhs Predicate register to update. + /// @param rhs Right-hand predicate register. + /// @return Reference to the updated predicate. + constexpr auto SIMD_FLAGS(In, ForceInline) operator^=( + this RegisterMask &lhs, + RegisterMask rhs) noexcept -> RegisterMask &; + */ }; ``` @@ -854,73 +861,73 @@ the explicit-object surface by generated-code and ABI tests. ### Construction and transfer ledger -| Current `Api` operation | Preferred `Register` form | Decision | -| ----------------------------- | ------------------------------------------- | ----------------------------------------------------------- | -| `load` | `Register::load(fixed_span)` | Canonical potentially unaligned full load | -| `load_aligned` | `Register::load_aligned(fixed_span)` | Retained with alignment precondition | -| `load_unaligned` | `Register::load(fixed_span)` | Redundant spelling omitted | -| `load_partial` | None | Partial data belongs to higher-level types | -| `load_unsafe` | None | Dynamic-extent unsafe load remains on `Api` | -| `store` to element span | `value.store(fixed_span)` | Canonical potentially unaligned full store | -| `store_aligned` | `value.store_aligned(fixed_span)` | Retained with alignment precondition | -| `store_unaligned` | `value.store(fixed_span)` | Redundant spelling omitted | -| `store` to fixed byte span | `value.store_bytes(fixed_byte_span)` | Renamed to make bit-pattern transfer explicit | -| `store` to dynamic byte span | None | Dynamic-extent transfer remains compatibility-only on `Api` | -| Fixed-byte `load` | `Register::load_bytes(fixed_byte_span)` | Symmetric bit-pattern transfer | -| `construct(array)` | `Register::from_array(array)` | Static factory; no ambiguous storage constructor | -| `to_array` | `value.to_array()` | Retained as a value conversion | -| `setzero` | Default construction and `Register::zero()` | Uses intrinsic-backed zero construction | -| `set1` | `Register::broadcast(value)` | Explicit scalar broadcast | -| `setr` | `Register::from_lanes(...)` | Requires exactly `lane_count` logical-order values | -| `set` | None | Native intrinsic argument order remains compatibility-only | -| `set_partial`, `setr_partial` | None | No partial or automatically filled lanes | +| Current `Api` operation | Preferred `Register` form | Decision | +| --- | --- | --- | +| `load` | `Register::load(fixed_span)` | Canonical potentially unaligned full load | +| `load_aligned` | `Register::load_aligned(fixed_span)` | Retained with alignment precondition | +| `load_unaligned` | `Register::load(fixed_span)` | Redundant spelling omitted | +| `load_partial` | None | Partial data belongs to higher-level types | +| `load_unsafe` | None | Dynamic-extent unsafe load remains on `Api` | +| `store` to element span | `value.store(fixed_span)` | Canonical potentially unaligned full store | +| `store_aligned` | `value.store_aligned(fixed_span)` | Retained with alignment precondition | +| `store_unaligned` | `value.store(fixed_span)` | Redundant spelling omitted | +| `store` to fixed byte span | `value.store_bytes(fixed_byte_span)` | Renamed to make bit-pattern transfer explicit | +| `store` to dynamic byte span | None | Dynamic-extent transfer remains compatibility-only on `Api` | +| Fixed-byte `load` | `Register::load_bytes(fixed_byte_span)` | Symmetric bit-pattern transfer | +| `construct(array)` | `Register::from_array(array)` | Static factory; no ambiguous storage constructor | +| `to_array` | `value.to_array()` | Retained as a value conversion | +| `setzero` | Default construction and `Register::zero()` | Uses intrinsic-backed zero construction | +| `set1` | `Register::broadcast(value)` | Explicit scalar broadcast | +| `setr` | `Register::from_lanes(...)` | Requires exactly `lane_count` logical-order values | +| `set` | None | Native intrinsic argument order remains compatibility-only | +| `set_partial`, `setr_partial` | None | No partial or automatically filled lanes | ### Arithmetic and reduction ledger -| Current `Api` operation | Preferred `Register` form | Result | -| ------------------------------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------- | -| `add` | `lhs + rhs` | Same register type | -| `subtract` | `lhs - rhs` | Same register type | -| `multiply` | `lhs * rhs` | Same register type | -| `divide` | `lhs / rhs` | Same register type where supported | -| `modulus` | `lhs % rhs` | Same integral register type | -| `negate` | `-value` | Same register type | -| `min` | `lhs.min(rhs)` | Same register type | -| `max` | `lhs.max(rhs)` | Same register type | -| `multiply_add` | `lhs.multiply_add(rhs, addend)` | Same register type | -| `widen` | `value.widen_low()` | Explicit target `Register`; consumed lanes documented | -| `absolute` | `value.absolute()` | Same register type and intrinsic edge behavior | -| `sqrt` | `value.sqrt()` | Same register type where supported | -| `magnitude` | `value.magnitude()` | Floating groups broadcast; integer groups store an unchecked result only in their leading lane | -| `magnitude_checked` | `value.magnitude_checked()` | Integral groups store a saturated result followed by a canonical overflow mask | -| `normalize` | `value.normalize()` | Same floating register type | -| `avg` | `lhs.average(rhs)` | Same register type | -| `add_horizontal` | `lhs.horizontal_add(rhs)` | Same register type | -| `subtract_horizontal` | `lhs.horizontal_subtract(rhs)` | Same register type | -| `multiply_add_adjacent` | `lhs.multiply_add_adjacent(rhs)` | Explicit operation-result Register alias | -| `multiply_add_unsigned_signed_bytes` | `lhs.multiply_add_unsigned_signed_bytes(rhs)` | Explicit signed promoted-result Register alias | -| `sum_absolute_byte_differences` | `lhs.sum_absolute_byte_differences(rhs)` | Explicit unsigned-result Register alias | -| `multi_sum_absolute_byte_differences` | `lhs.multi_sum_absolute_byte_differences(rhs)` | Explicit unsigned-result Register alias | -| `min_position` | `value.min_position()` | `std::size_t` | -| `max_position` | `value.max_position()` | `std::size_t` | -| `add_saturated` | `lhs.add_saturated(rhs)` | Same register type | -| `subtract_saturated` | `lhs.subtract_saturated(rhs)` | Same register type | -| `hadd_saturated` | `lhs.horizontal_add_saturated(rhs)` | Same register type | -| `hsubtract_saturated` | `lhs.horizontal_subtract_saturated(rhs)` | Same register type | -| `add_subtract` | `lhs.add_subtract(rhs)` | Same floating register type | -| `dot_product` | `lhs.dot_product(rhs)` | Same register type with intrinsic-selected output lanes | +| Current `Api` operation | Preferred `Register` form | Result | +| --- | --- | --- | +| `add` | `lhs + rhs` | Same register type | +| `subtract` | `lhs - rhs` | Same register type | +| `multiply` | `lhs * rhs` | Same register type | +| `divide` | `lhs / rhs` | Same register type where supported | +| `modulus` | `lhs % rhs` | Same integral register type | +| `negate` | `-value` | Same register type | +| `min` | `lhs.min(rhs)` | Same register type | +| `max` | `lhs.max(rhs)` | Same register type | +| `multiply_add` | `lhs.multiply_add(rhs, addend)` | Same register type | +| `widen` | `value.widen_low()` | Explicit target `Register`; consumed lanes documented | +| `absolute` | `value.absolute()` | Same register type and intrinsic edge behavior | +| `sqrt` | `value.sqrt()` | Same register type where supported | +| `magnitude` | `value.magnitude()` | Floating groups broadcast; integer groups store an unchecked result only in their leading lane | +| `magnitude_checked` | `value.magnitude_checked()` | Integral groups store a saturated result followed by a canonical overflow mask | +| `normalize` | `value.normalize()` | Same floating register type | +| `avg` | `lhs.average(rhs)` | Same register type | +| `add_horizontal` | `lhs.horizontal_add(rhs)` | Same register type | +| `subtract_horizontal` | `lhs.horizontal_subtract(rhs)` | Same register type | +| `multiply_add_adjacent` | `lhs.multiply_add_adjacent(rhs)` | Explicit operation-result Register alias | +| `multiply_add_unsigned_signed_bytes` | `lhs.multiply_add_unsigned_signed_bytes(rhs)` | Explicit signed promoted-result Register alias | +| `sum_absolute_byte_differences` | `lhs.sum_absolute_byte_differences(rhs)` | Explicit unsigned-result Register alias | +| `multi_sum_absolute_byte_differences` | `lhs.multi_sum_absolute_byte_differences(rhs)` | Explicit unsigned-result Register alias | +| `min_position` | `value.min_position()` | `std::size_t` | +| `max_position` | `value.max_position()` | `std::size_t` | +| `add_saturated` | `lhs.add_saturated(rhs)` | Same register type | +| `subtract_saturated` | `lhs.subtract_saturated(rhs)` | Same register type | +| `hadd_saturated` | `lhs.horizontal_add_saturated(rhs)` | Same register type | +| `hsubtract_saturated` | `lhs.horizontal_subtract_saturated(rhs)` | Same register type | +| `add_subtract` | `lhs.add_subtract(rhs)` | Same floating register type | +| `dot_product` | `lhs.dot_product(rhs)` | Same register type with intrinsic-selected output lanes | Operations whose intrinsic changes the lane type use constrained namespace-level alias templates. Keeping these aliases outside `Register` avoids conditional member declarations or helper-base storage that could complicate the exact one-native-member representation: -| Alias | Exact result mapping | -| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Alias | Exact result mapping | +| --- | --- | | `multiply_add_adjacent_result_t` | `Register` for `int8_t`, `Register` for `uint8_t`, then the corresponding signedness at twice the lane width through 64 bits; 64-bit lanes remain 64-bit | -| `byte_multiply_add_result_t` | `Register` for supported signed/unsigned byte inputs | -| `sad_result_t` | `Register` | -| `multi_sad_result_t` | `Register` | +| `byte_multiply_add_result_t` | `Register` for supported signed/unsigned byte inputs | +| `sad_result_t` | `Register` | +| `multi_sad_result_t` | `Register` | The aliases are declared only when the corresponding backend operation is available. Each public operation names its exact alias as the return type rather @@ -931,20 +938,20 @@ formed mechanically. ### Bitwise and comparison ledger -| Current `Api` operation | Preferred `Register` form | Result | -| ------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | --------------------------------------------------------------- | -| `bitwise_and` | `lhs & rhs` | Same register type | -| `bitwise_or` | `lhs \| rhs` | Same register type | -| `bitwise_xor` | `lhs ^ rhs` | Same register type | -| `bitwise_not` | `~value` | Same register type | -| `bitwise_andnot` | `lhs.andnot(rhs)` | Same register type with existing operand polarity | -| `select` | `mask.select(when_true, when_false)` | Same Register type; canonical predicate remains Register-shaped | -| `movemask` | `value.movemask()` | Scalar mask with the selected intrinsic's native granularity | -| `movemask_slim` | `value.lane_sign_bits()` | Scalar mask with one bit per lane | -| `compare_equal`, `compare_greater`, `compare_greater_equal`, `compare_less`, `compare_less_equal` | Corresponding named comparison | `RegisterMask` preserving native predicates | -| `cmp_eq_mask`, `cmp_gt_mask`, `cmp_ge_mask`, `cmp_lt_mask`, `cmp_le_mask` | No compact-mask Register counterpart | Byte-granular legacy-compatible scalar mask | -| `cmp_eq_slim`, `cmp_gt_slim`, `cmp_ge_slim`, `cmp_lt_slim`, `cmp_le_slim` | Corresponding named comparison followed by `.bits()` | One compact bit per lane | -| Deprecated `cmp_eq`, `cmp_gt`, `cmp_ge`, `cmp_lt`, `cmp_le` | Corresponding explicitly named `cmp_*_mask` method | Byte-granular compatibility spelling | +| Current `Api` operation | Preferred `Register` form | Result | +| --- | --- | --- | +| `bitwise_and` | `lhs & rhs` | Same register type | +| `bitwise_or` | `lhs \| rhs` | Same register type | +| `bitwise_xor` | `lhs ^ rhs` | Same register type | +| `bitwise_not` | `~value` | Same register type | +| `bitwise_andnot` | `lhs.andnot(rhs)` | Same register type with existing operand polarity | +| `select` | `mask.select(when_true, when_false)` | Same Register type; canonical predicate remains Register-shaped | +| `movemask` | `value.movemask()` | Scalar mask with the selected intrinsic's native granularity | +| `movemask_slim` | `value.lane_sign_bits()` | Scalar mask with one bit per lane | +| `compare_equal`, `compare_greater`, `compare_greater_equal`, `compare_less`, `compare_less_equal` | Corresponding named comparison | `RegisterMask` preserving native predicates | +| `cmp_eq_mask`, `cmp_gt_mask`, `cmp_ge_mask`, `cmp_lt_mask`, `cmp_le_mask` | No compact-mask Register counterpart | Byte-granular legacy-compatible scalar mask | +| `cmp_eq_slim`, `cmp_gt_slim`, `cmp_ge_slim`, `cmp_lt_slim`, `cmp_le_slim` | Corresponding named comparison followed by `.bits()` | One compact bit per lane | +| Deprecated `cmp_eq`, `cmp_gt`, `cmp_ge`, `cmp_lt`, `cmp_le` | Corresponding explicitly named `cmp_*_mask` method | Byte-granular compatibility spelling | The legacy scalar comparison-mask layout is not uniform across integral and floating backends. `mask.bits()` deliberately normalizes it to one bit @@ -961,22 +968,22 @@ requires an explicit integer reinterpretation followed by integer comparison. ### Rearrangement ledger -| Current `Api` operation | Preferred `Register` form | Decision | -| -------------------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| `expand` | None | Ambiguous legacy widening alias remains compatibility-only | -| `compress` | None | Ambiguous legacy narrowing alias remains compatibility-only | -| `extract` | `value.lane()` | Compile-time logical lane extraction | -| Runtime `extract_slow` | None | Explicit Api slow path; Register retains compile-time lane access | -| `lower_half` | `value.lower_half()` | Returns `Register` from a 256-bit source | -| `insert` | `value.with_lane(lane)` | Compile-time logical lane replacement | -| `unpack_lo` | `lhs.unpack_low(rhs)` | Wrapped backend result | -| `unpack_hi` | `lhs.unpack_high(rhs)` | Wrapped backend result | -| `shuffle` | `value.shuffle()` | One compile-time logical source-lane selector per output lane | -| `Api::shuffle` | `value.shuffle_bytes()` | One compile-time logical source-byte selector per output byte; result retains `T` | -| Register-selector `shuffle(value, selector)` | None | Native Api runtime control; Register exposes portable logical and byte shuffle forms | -| `shuffle_lo`; `shuffle_lo_slow` | `value.shuffle_low()` | Compile-time immediate form; scalar runtime control remains Api-only | -| `shuffle_hi`; `shuffle_hi_slow` | `value.shuffle_high()` | Compile-time immediate form; scalar runtime control remains Api-only | -| `blend`; register-mask `blend`; `blend_slow` | `lhs.blend(rhs)` | Immediate blend maps directly; predicate selection uses `mask.select(lhs, rhs)`; scalar runtime control remains Api-only | +| Current `Api` operation | Preferred `Register` form | Decision | +| --- | --- | --- | +| `expand` | None | Ambiguous legacy widening alias remains compatibility-only | +| `compress` | None | Ambiguous legacy narrowing alias remains compatibility-only | +| `extract` | `value.lane()` | Compile-time logical lane extraction | +| Runtime `extract_slow` | None | Explicit Api slow path; Register retains compile-time lane access | +| `lower_half` | `value.lower_half()` | Returns `Register` from a 256-bit source | +| `insert` | `value.with_lane(lane)` | Compile-time logical lane replacement | +| `unpack_lo` | `lhs.unpack_low(rhs)` | Wrapped backend result | +| `unpack_hi` | `lhs.unpack_high(rhs)` | Wrapped backend result | +| `shuffle` | `value.shuffle()` | One compile-time logical source-lane selector per output lane | +| `Api::shuffle` | `value.shuffle_bytes()` | One compile-time logical source-byte selector per output byte; result retains `T` | +| Register-selector `shuffle(value, selector)` | None | Native Api runtime control; Register exposes portable logical and byte shuffle forms | +| `shuffle_lo`; `shuffle_lo_slow` | `value.shuffle_low()` | Compile-time immediate form; scalar runtime control remains Api-only | +| `shuffle_hi`; `shuffle_hi_slow` | `value.shuffle_high()` | Compile-time immediate form; scalar runtime control remains Api-only | +| `blend`; register-mask `blend`; `blend_slow` | `lhs.blend(rhs)` | Immediate blend maps directly; predicate selection uses `mask.select(lhs, rhs)`; scalar runtime control remains Api-only | Logical shuffle selectors use low-to-high lane numbering for the element type. The selector count must equal the register lane count, repeated selectors are @@ -996,24 +1003,24 @@ nevertheless remains `Register`. ### Shift and conversion ledger -| Current `Api` operation | Preferred `Register` form | Result | -| ----------------------------------- | ------------------------------------- | ---------------------------------------------------------------- | -| `shift_left` | `value << count` | Per-lane integral shift | -| `shift_right` | `value.logical_shift_right(count)` | Per-lane logical shift for signed or unsigned lanes | -| `shift_right_arithmetic` | `value >> count` | Per-lane arithmetic shift for signed lanes | -| Runtime `shift_bytes_left_slow` | `value.shift_bytes_left_slow(count)` | Complete integral 128-bit register byte shift | -| Compile-time `shift_bytes_left` | `value.shift_bytes_left()` | Complete integral 128- or 256-bit register byte shift | -| Runtime `shift_bytes_right_slow` | `value.shift_bytes_right_slow(count)` | Complete integral 128-bit register byte shift | -| Compile-time `shift_bytes_right` | `value.shift_bytes_right()` | Complete integral 128- or 256-bit register byte shift | -| Runtime `shift_bits_left_slow` | `value.shift_bits_left_slow(count)` | Complete integral 128-bit bit-string shift | -| Compile-time `shift_bits_left` | `value.shift_bits_left()` | Complete integral 128-bit bit-string shift | -| Runtime `shift_bits_right_slow` | `value.shift_bits_right_slow(count)` | Complete integral 128-bit bit-string shift | -| Compile-time shift_bits_right | alue.shift_bits_right() | Complete integral 128-bit bit-string shift | -| it_cast | alue.bit_cast() | Full-width bit-preserving reinterpretation | -| `convert_to_float` | `value.convert()` | `Register` from supported 32-bit integer lanes | -| `convert_to_int` | `value.convert()` | `Register` from float lanes | -| Explicit-target `convert` | `value.convert()` | Explicit target type | -| Inferred-target `convert` | None | Complementary-type inference remains compatibility-only on `Api` | +| Current `Api` operation | Preferred `Register` form | Result | +| --- | --- | --- | +| `shift_left` | `value << count` | Per-lane integral shift | +| `shift_right` | `value.logical_shift_right(count)` | Per-lane logical shift for signed or unsigned lanes | +| `shift_right_arithmetic` | `value >> count` | Per-lane arithmetic shift for signed lanes | +| Runtime `shift_bytes_left_slow` | `value.shift_bytes_left_slow(count)` | Complete integral 128-bit register byte shift | +| Compile-time `shift_bytes_left` | `value.shift_bytes_left()` | Complete integral 128- or 256-bit register byte shift | +| Runtime `shift_bytes_right_slow` | `value.shift_bytes_right_slow(count)` | Complete integral 128-bit register byte shift | +| Compile-time `shift_bytes_right` | `value.shift_bytes_right()` | Complete integral 128- or 256-bit register byte shift | +| Runtime `shift_bits_left_slow` | `value.shift_bits_left_slow(count)` | Complete integral 128-bit bit-string shift | +| Compile-time `shift_bits_left` | `value.shift_bits_left()` | Complete integral 128-bit bit-string shift | +| Runtime `shift_bits_right_slow` | `value.shift_bits_right_slow(count)` | Complete integral 128-bit bit-string shift | +| Compile-time shift_bits_right | alue.shift_bits_right() | Complete integral 128-bit bit-string shift | +| it_cast | alue.bit_cast() | Full-width bit-preserving reinterpretation | +| `convert_to_float` | `value.convert()` | `Register` from supported 32-bit integer lanes | +| `convert_to_int` | `value.convert()` | `Register` from float lanes | +| Explicit-target `convert` | `value.convert()` | Explicit target type | +| Inferred-target `convert` | None | Complementary-type inference remains compatibility-only on `Api` | `operator>>` is available only when it has one unambiguous hardware meaning. Unsigned lanes use the logical shift. Signed lanes use the arithmetic shift. @@ -1023,13 +1030,13 @@ request zero fill. Shift-count behavior is part of the public contract and matches the existing backend operation rather than C++ scalar-shift rules: -| Shift family | Count contract | -| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | -| Per-lane left or logical right | Runtime count must be nonnegative; counts at least the lane width produce zero lanes | -| Per-lane arithmetic right | Runtime count must be nonnegative; counts at least the lane width clamp to `lane_width - 1` and therefore sign-fill | -| 128-bit byte shifts | Counts at most zero return the input; counts at least 16 return zero | -| Runtime 128-bit whole-register bit shifts | Counts at most zero return the input; counts at least 128 return zero | -| Compile-time 128-bit whole-register bit shifts | Negative counts are rejected; counts at least 128 produce zero | +| Shift family | Count contract | +| --- | --- | +| Per-lane left or logical right | Runtime count must be nonnegative; counts at least the lane width produce zero lanes | +| Per-lane arithmetic right | Runtime count must be nonnegative; counts at least the lane width clamp to `lane_width - 1` and therefore sign-fill | +| 128-bit byte shifts | Counts at most zero return the input; counts at least 16 return zero | +| Runtime 128-bit whole-register bit shifts | Counts at most zero return the input; counts at least 128 return zero | +| Compile-time 128-bit whole-register bit shifts | Negative counts are rejected; counts at least 128 produce zero | The implementation must not introduce release-only undefined behavior for a documented count. Negative per-lane shift counts are invalid runtime inputs and @@ -1038,14 +1045,14 @@ follow the SimdLib precondition policy; tests cover the boundary values `0`, ### Collection and internal ledger -| Current `Api` operation | `Register` decision | -| --------------------------------- | --------------------------------------------------------------------- | -| `transform_pack` | Remains a collection algorithm on `Api` or its future algorithm owner | -| Unary in-place `transform` | Remains a collection algorithm | -| Unary separate-output `transform` | Remains a collection algorithm | -| Binary `transform` | Remains a collection algorithm | -| `TransformForMaxPosition` | Internal helper; no public `Register` counterpart | -| `compare_each_element` | Internal fallback helper used by the comparison adapter | +| Current `Api` operation | `Register` decision | +| --- | --- | +| `transform_pack` | Remains a collection algorithm on `Api` or its future algorithm owner | +| Unary in-place `transform` | Remains a collection algorithm | +| Unary separate-output `transform` | Remains a collection algorithm | +| Binary `transform` | Remains a collection algorithm | +| `TransformForMaxPosition` | Internal helper; no public `Register` counterpart | +| `compare_each_element` | Internal fallback helper used by the comparison adapter | All preferred register-local operations return `Register`, `RegisterMask`, or an explicitly documented scalar. No preferred operation exposes a raw intrinsic @@ -1304,14 +1311,14 @@ Representative migration: // Existing interface. using U32Api = SimdLib::Api<128, std::uint32_t>; const auto old_result = U32Api::bitwise_or( - U32Api::add(lhs, rhs), - U32Api::set1(1)); + U32Api::add(lhs, rhs), + U32Api::set1(1)); // Register interface. using U32Register = SimdLib::Register; const auto new_result = - (U32Register{lhs} + U32Register{rhs}) | - U32Register::broadcast(1); + (U32Register{lhs} + U32Register{rhs}) | + U32Register::broadcast(1); ``` ## Validation strategy @@ -1431,9 +1438,9 @@ Tests use the current `Api` as the permanent generated-code parity baseline. Independent scalar references remain necessary in behavioral tests and benchmarks so both public surfaces cannot agree on the same defect unnoticed; those references are not retained as duplicate permanent codegen algorithms. -The complete per-symbol retention and ownership decisions are defined by -`RegisterCodegenSymbolAudit.csv` and summarized with the build and artifact -inventory in `RegisterCodegenAudit.md`. +The generated comparison records are the authoritative per-symbol inventories. +Fixture, baseline, record, and validator ownership is defined by +`RegisterQualification.md`, the fixture sources, and the CMake symbol filters. ## Acceptance criteria diff --git a/docs/RegisterImplementationMatrix.md b/docs/RegisterImplementationMatrix.md index 9a5f32c..6d7cf9f 100644 --- a/docs/RegisterImplementationMatrix.md +++ b/docs/RegisterImplementationMatrix.md @@ -1,7 +1,7 @@ # Register Implementation Matrix -This document makes the accepted design in `RegisterProposal.md` executable and -traceable. The proposal controls semantics; `ApiOperationMatrix.md` controls the +This document makes the accepted design in `RegisterContract.md` executable and +traceable. The contract controls semantics; `ApiOperationMatrix.md` controls the current backend availability matrix; and this matrix records the implemented operation coverage. A disagreement is resolved by correcting the controlling semantic or availability document before implementing the affected operation. @@ -63,7 +63,7 @@ These portability rules do not change a public declaration. | Compact mask bits | `bits_type` is normalized from lane count, is `uint32_t` for initial widths, maps bit `i` to lane `i`, and clears unused bits | 5 | Static assertions and mask-pattern tests | | Comparison semantics | Named comparisons reproduce the selected intrinsic, including signedness, NaNs, signed zero, ordered/unordered predicates, and lane bit patterns | 5 | Runtime, portable, emulated, and constexpr parity | | Whole equality | `operator==` means all lanes compare equal; `operator!=` is its Boolean negation; relational operators are absent | 5 | Boolean and compile-rejection tests | -| Shift counts | Per-lane negative counts are invalid; logical overshifts zero, arithmetic overshifts sign-fill, and byte/whole-register shifts follow the proposal boundary table | 6 | Boundary, precondition, constexpr, and codegen tests | +| Shift counts | Per-lane negative counts are invalid; logical overshifts zero, arithmetic overshifts sign-fill, and byte/whole-register shifts follow the contract boundary table | 6 | Boundary, precondition, constexpr, and codegen tests | | Immediate controls | Every `imm8` is constrained to `0..255`; logical element and byte shuffles require exactly one selector per output lane or byte, permit repeated selectors, and reject selectors outside the complete source register | 7, 8 | Compile-success/failure boundaries | | Rearrangement order | `lower_half()`, unpacking, and shuffling use logical low-to-high lanes or bytes. The 256-bit logical element and byte shuffles may select from the complete source register across the 128-bit boundary; lane-group restrictions remain only on operations whose names or intrinsic contracts specify them | 8 | Independent lane and byte oracles, cross-half selectors, highest-position sentinels, and exact code-generation parity | | Type-changing results | Public operations name the exact constrained namespace-level result alias and never expose a raw intrinsic result | 7 | Type assertions and unsupported-combination rejection | @@ -77,7 +77,7 @@ These portability rules do not change a public declaration. | Excluded surface | Classification | Reason | | --- | --- | --- | -| Partial load/store or lane construction | Higher-level responsibility | Register has no inactive lanes or fill policy | +| Partial load/store or lane construction | Sibling-type responsibility | Register has no inactive lanes or fill policy; fixed compile-time prefixes use `PartialRegister`, while dynamic tails remain collection-owned | | Dynamic-extent `load_unsafe` | `Api` compatibility-only | Its precondition is unsuitable for the restrictive value type | | Native-order `set` | `Api` compatibility-only | Public lane order is logical low-to-high | | Implicit scalar broadcast | Excluded | Broadcast cost and intent remain explicit | @@ -113,7 +113,7 @@ the operation or intentionally leaves it in a compatibility or collection layer. | `load` | `Register::load(fixed_span)` | Implemented | | `load_aligned` | `Register::load_aligned(fixed_span)` | Implemented | | `load_unaligned` | Canonicalized to `Register::load(fixed_span)` | Implemented | -| `load_partial` | No Register operation | Compatibility | +| `load_partial` | No Register operation; fixed-prefix consumers use `PartialRegister::load` | Compatibility and sibling routing | | `load_unsafe` | No Register operation | Compatibility | | Element `store` | `value.store(fixed_span)` | Implemented | | `store_aligned` | `value.store_aligned(fixed_span)` | Implemented | @@ -126,7 +126,7 @@ the operation or intentionally leaves it in a compatibility or collection layer. | `setzero` | Default construction and `Register::zero()` | Implemented | | `set1` | `Register::broadcast(value)` | Implemented | | `setr` | `Register::from_lanes(...)` | Implemented | -| `set`, `set_partial`, `setr_partial` | No Register operation | Compatibility | +| `set`, `set_partial`, `setr_partial` | No Register operation; fixed-prefix consumers use `PartialRegister::from_lanes` or `from_array` | Compatibility and sibling routing | | `add` | `lhs + rhs` | Implemented | | `subtract` | `lhs - rhs` | Implemented | | `multiply` | `lhs * rhs` | Implemented | @@ -289,9 +289,9 @@ compile-time audit; no prose-only availability list can drift independently. | C++20 core | GCC 13.2 | Linux x64; Debug and Release | Existing full public matrix remains supported; Register unavailable | | C++20 core sanitizer | Clang 22.1.8 | Linux x64 Debug, `-O1`, ASan/UBSan, frame pointers | No sanitizer diagnostics | | Register | MSVC 19.44 | Windows x64, `/std:c++latest`; supported ISA profiles | SSE4.2 diagnostics and strict AVX2 gates; memory-writing fixtures retain `/GS` and the exact documented exception | -| Register | clang-cl 20.1.8 | Windows x64, C++23; supported ISA profiles | SSE4.2 diagnostics and strict AVX2 correctness, ABI, and generated-code gates | -| Register | Clang 22.1.8 | Linux x64, C++23; supported ISA profiles | SSE4.2 diagnostics and strict AVX2 correctness, ABI, and generated-code gates | -| Register | GCC 14 or newer | Linux x64, C++23; supported ISA profiles | SSE4.2 diagnostics and strict AVX2 correctness, ABI, and generated-code gates | +| Register and PartialRegister | clang-cl 20.1.8 and 22.1.7 | Windows x64, C++23; supported ISA profiles | Separate strict SSE4.2 and AVX2 correctness, ABI, and generated-code gates for the explicit clang-cl 20 compatibility floor and the newer compiler | +| Register and PartialRegister | Clang 22 or newer | Linux x64, C++23; supported ISA profiles | Strict SSE4.2 and AVX2 correctness, ABI, and generated-code gates | +| Register and PartialRegister | GCC 14 or newer | Linux x64, C++23; supported ISA profiles | Strict SSE4.2 and AVX2 correctness, ABI, and generated-code gates | Linux x64 GCC 13.2 remains the required unavailable-interface probe; it is not a Register compiler. A Register compiler floor is lowered or expanded only after @@ -317,7 +317,7 @@ the complete correctness, layout, ABI, and generated-code gates pass. | Non-inlined ABI mirrors | `tests/codegen/RegisterAbi.cpp`, `tests/codegen/RegisterAbiRaw.cpp` | ABI records owned by `RegisterCodegen128Sse42`, `RegisterCodegen128Avx2`, and `RegisterCodegen256Avx2` | | Register pressure and opaque calls | `tests/codegen/RegisterCodegenFixture.h` | Register code-generation gate | | Code-generation comparison | `cmake/CompareRegisterCodegen.cmake` and checked-in allowlisted normalization rules | CTest mandatory performance gate | -| Permanent generated-code ownership audit | `docs/RegisterCodegenSymbolAudit.csv` and `docs/RegisterCodegenAudit.md` | Per-symbol fixture, baseline, record, validation, and retention traceability | +| Generated-code ownership | `docs/RegisterQualification.md`, fixture sources, CMake symbol filters, and generated comparison records | Fixture, baseline, record, validation, and per-symbol traceability | | Checks-enabled preconditions | `tests/RegisterPreconditionFailure.tests.cpp` | Existing precondition death-test infrastructure | | Sanitizers | Runtime Register and mask sources | Fresh Clang ASan/UBSan configuration | | Supplemental benchmarks | `benchmarks/Register.benchmarks.cpp` | `Benchmarks`; never a correctness/codegen substitute | @@ -325,7 +325,7 @@ the complete correctness, layout, ABI, and generated-code gates pass. Every production class and method has Doxygen documentation. Test and generated-code sources use only public SimdLib declarations except the -proposal-approved narrow internal comparison adapter tests. +contract-approved narrow internal comparison adapter tests. ## Validation ownership diff --git a/docs/RegisterQualification.md b/docs/RegisterQualification.md index 9a76a73..8a1d59e 100644 --- a/docs/RegisterQualification.md +++ b/docs/RegisterQualification.md @@ -7,6 +7,10 @@ artifacts and individual execution results are intentionally not committed; the commands below reproduce them under `build*/register-codegen` or `out/pipeline`. +Public semantics are defined by [RegisterContract.md](RegisterContract.md), and +operation-level implementation traceability is maintained in +[RegisterImplementationMatrix.md](RegisterImplementationMatrix.md). + ## Supported matrix | Dimension | Supported cells | @@ -64,10 +68,10 @@ temporaries, and indirection. The permanent corpus assigns one contract to each fixture and one public raw `Api` baseline to each parity comparison: -The per-symbol ownership, category, baseline, validation owner, retention -decision, and rationale are recorded in -`RegisterCodegenSymbolAudit.csv`; `RegisterCodegenAudit.md` inventories the -source, target, record, CTest, CI-artifact, and documentation boundaries. +Generated `symbols.txt`, `instruction-differences.txt`, and comparison records +are the authoritative per-symbol inventories. The checked-in CMake symbol +filters, fixture sources, and ownership rules below define which records may be +generated and which validator owns them. - `RegisterCodegenFixture.h` retains composed expressions, mask composition and reduction, broadcast reuse, nonzero lane extraction, immediate and complete @@ -91,6 +95,48 @@ source, target, record, CTest, CI-artifact, and documentation boundaries. conversions, bit casts, width changes, and the public `Register::shuffle` versus `Api::shuffle` baseline. +### Fixture and baseline ownership + +| Fixture family | Permanent contract | Comparison baseline | +| --- | --- | --- | +| `RegisterCodegenFixture.h` | Composed expressions, memory paths, special members, reassignment, pressure, and opaque calls | Matching public `Api` expression in `RegisterCodegenRaw.cpp` | +| `RegisterTypeMatrixCodegenFixture.h` | Every available isolated Register and RegisterMask operation/type/width cell | Matching public `Api` expression in `RegisterTypeMatrixCodegenRaw.cpp` | +| `RegisterSpecializedCodegenFixture.h` | FMA-independent specialized arithmetic and reductions | Matching public `Api` expression in `RegisterSpecializedCodegenRaw.cpp` | +| `RegisterFmaCodegenFixture.h` | Multiply-add with FMA explicitly enabled and disabled | Matching public `Api` expression in `RegisterFmaCodegenRaw.cpp` | +| `RegisterRearrangementCodegenFixture.h` | Rearrangements, selectors, bit casts, conversions, lower halves, and widening | Matching public `Api` expression in `RegisterRearrangementCodegenRaw.cpp` | +| `RegisterAbi.cpp` | Explicit-object and real consumer ABI boundaries | Native-vector signatures in `RegisterAbiRaw.cpp` | +| `RegisterDefaultAbi.cpp` | Diagnostic platform-default aggregate ABI | Native-vector signature in `RegisterDefaultAbiRaw.cpp` | +| `MethodFlagsFlagged.cpp` | Compiler-attribute enforcement | Equivalent raw compiler attributes in `MethodFlagsRaw.cpp` | + +Public zero-overhead fixtures compare `Register` with the narrowest equivalent +public `Api` expression. A raw translation unit must not call `Register`, an +implementation specialization, or an extension helper. ABI fixtures compare +aggregate signatures with native-vector signatures; method-flag fixtures +compare `SIMD_FLAGS(...)` with equivalent raw compiler attributes. The +platform-default ABI fixture is diagnostic rather than an equality gate. + +### Comparison record ownership + +Each generated record appears exactly once in its profile's CMake-owned index. +Release validation requires every enforced record to report `ENFORCE`; +record-only results belong only to diagnostic indexes. + +| Record | Symbol selection | Wrapper/raw inputs | Owning validation | +| --- | --- | --- | --- | +| `primary-composition` | Memory-capable and composed primary symbols | `RegisterCodegen.cpp` / `RegisterCodegenRaw.cpp` | `RegisterCodegen.` | +| `register-only` | Register-only primary symbols | `RegisterCodegen.cpp` / `RegisterCodegenRaw.cpp` | `RegisterCodegen.` | +| `reassignment` | Reassignment arithmetic | `RegisterCodegen.cpp` / `RegisterCodegenRaw.cpp` | `RegisterCodegen.` | +| `specialized` | FMA-independent specialized symbols | `RegisterSpecializedCodegen.cpp` / `RegisterSpecializedCodegenRaw.cpp` | `RegisterCodegen.` | +| `fma-disabled` | Floating multiply-add with FMA disabled | `RegisterFmaCodegen.cpp` / `RegisterFmaCodegenRaw.cpp` | `RegisterCodegen.` | +| `fma-enabled` | Floating multiply-add with FMA enabled | `RegisterFmaCodegen.cpp` / `RegisterFmaCodegenRaw.cpp` | AVX2 `RegisterCodegen.` | +| `rearrangement-conversion` | Applicable rearrangement and conversion symbols | `RegisterRearrangementCodegen.cpp` / `RegisterRearrangementCodegenRaw.cpp` | `RegisterCodegen.` | +| `common-type-matrix` | Applicable non-modulus type-matrix symbols | `RegisterTypeMatrixCodegen.cpp` / `RegisterTypeMatrixCodegenRaw.cpp` | `RegisterCodegen.` | +| `modulus-type-matrix` | Integer modulus symbols | `RegisterTypeMatrixCodegen.cpp` / `RegisterTypeMatrixCodegenRaw.cpp` | `RegisterCodegen.` | +| `abi` | Explicit-object ABI mirrors | `RegisterAbi.cpp` / `RegisterAbiRaw.cpp` | `RegisterCodegen.` | +| `consumer-abi` | Downstream Register and RegisterMask boundaries | `RegisterAbi.cpp` / `RegisterAbiRaw.cpp` | `RegisterCodegen.` | +| `default-abi` | Platform-default aggregate boundary | `RegisterDefaultAbi.cpp` / `RegisterDefaultAbiRaw.cpp` | `RegisterCodegen.` | +| `method-flags` | `SIMD_FLAGS(...)` declaration fixtures | `MethodFlagsFlagged.cpp` / `MethodFlagsRaw.cpp` | `MethodFlagsCodegen` | + Handwritten intrinsic or scalar mirrors are algorithm-evaluation tools, not permanent codegen baselines, unless they protect a documented instruction property that the public `Api` baseline cannot express. Behavioral tests and diff --git a/include/SimdLib/Aliases.h b/include/SimdLib/Aliases.h index fbcf2d0..d623adc 100644 --- a/include/SimdLib/Aliases.h +++ b/include/SimdLib/Aliases.h @@ -1,13 +1,112 @@ #pragma once -#include +#include +#include #include namespace SimdLib { #if SIMDLIB_HAS_SSE42 +#pragma region Partial Register Type Aliases (Unsigned) + +/** @brief 128-bit unsigned-byte PartialRegister with a caller-selected logical prefix. */ +template + requires PartialRegisterAvailable +using partial_uint8x16 = PartialRegister; + +/** @brief 128-bit unsigned-word PartialRegister with a caller-selected logical prefix. */ +template + requires PartialRegisterAvailable +using partial_uint16x8 = PartialRegister; + +/** @brief 128-bit unsigned-doubleword PartialRegister with a caller-selected logical prefix. */ +template + requires PartialRegisterAvailable +using partial_uint32x4 = PartialRegister; + +/** @brief 128-bit unsigned-quadword PartialRegister with a caller-selected logical prefix. */ +template + requires PartialRegisterAvailable +using partial_uint64x2 = PartialRegister; + +#pragma endregion + +#pragma region Partial Register Type Aliases (Signed) + +/** @brief 128-bit signed-byte PartialRegister with a caller-selected logical prefix. */ +template + requires PartialRegisterAvailable +using partial_int8x16 = PartialRegister; + +/** @brief 128-bit signed-word PartialRegister with a caller-selected logical prefix. */ +template + requires PartialRegisterAvailable +using partial_int16x8 = PartialRegister; + +/** @brief 128-bit signed-doubleword PartialRegister with a caller-selected logical prefix. */ +template + requires PartialRegisterAvailable +using partial_int32x4 = PartialRegister; + +/** @brief 128-bit signed-quadword PartialRegister with a caller-selected logical prefix. */ +template + requires PartialRegisterAvailable +using partial_int64x2 = PartialRegister; + +#pragma endregion + +#if SIMDLIB_HAS_AVX2 +#pragma region Partial Register Type Aliases (Unsigned AVX2) + +/** @brief 256-bit unsigned-byte PartialRegister with a caller-selected logical prefix. */ +template + requires PartialRegisterAvailable +using partial_uint8x32 = PartialRegister; + +/** @brief 256-bit unsigned-word PartialRegister with a caller-selected logical prefix. */ +template + requires PartialRegisterAvailable +using partial_uint16x16 = PartialRegister; + +/** @brief 256-bit unsigned-doubleword PartialRegister with a caller-selected logical prefix. */ +template + requires PartialRegisterAvailable +using partial_uint32x8 = PartialRegister; + +/** @brief 256-bit unsigned-quadword PartialRegister with a caller-selected logical prefix. */ +template + requires PartialRegisterAvailable +using partial_uint64x4 = PartialRegister; + +#pragma endregion + +#pragma region Partial Register Type Aliases (Signed AVX2) + +/** @brief 256-bit signed-byte PartialRegister with a caller-selected logical prefix. */ +template + requires PartialRegisterAvailable +using partial_int8x32 = PartialRegister; + +/** @brief 256-bit signed-word PartialRegister with a caller-selected logical prefix. */ +template + requires PartialRegisterAvailable +using partial_int16x16 = PartialRegister; + +/** @brief 256-bit signed-doubleword PartialRegister with a caller-selected logical prefix. */ +template + requires PartialRegisterAvailable +using partial_int32x8 = PartialRegister; + +/** @brief 256-bit signed-quadword PartialRegister with a caller-selected logical prefix. */ +template + requires PartialRegisterAvailable +using partial_int64x4 = PartialRegister; + +#pragma endregion +#endif + #pragma region Vector Types // TODO: Remove these "Vector..." aliases in favor of the more descriptive "int8x16" style aliases below. diff --git a/include/SimdLib/Api.h b/include/SimdLib/Api.h index 18c0bab..316e314 100644 --- a/include/SimdLib/Api.h +++ b/include/SimdLib/Api.h @@ -148,6 +148,25 @@ struct Api : public Detail::SimdMappings { return impl::load_unaligned(data.data()); } + else if constexpr (active_count * sizeof(element_t) == byte_count_half) + { + if (std::is_constant_evaluated()) + { + return [&data](std::index_sequence) constexpr noexcept -> vector_t + { return setr_partial(static_cast(data[Indices])...); }(std::make_index_sequence{}); + } + else if constexpr (using_int) + { + return impl::load_half(data.data()); + } + else + { + using byte_api = Api; + const auto bytes = byte_api::template load_partial( + std::span{reinterpret_cast(data.data()), byte_count_half}); + return byte_api::template bit_cast(bytes); + } + } else { return [&data](std::index_sequence) constexpr noexcept -> vector_t @@ -155,6 +174,57 @@ struct Api : public Detail::SimdMappings } } + /** @brief Loads a logical prefix from register-aligned storage and zero-fills the remaining lanes. + * @tparam active_count Number of leading elements to load. + * @param data Register-aligned source containing at least `active_count` elements. + * @return Register containing the requested active values followed by zero-filled inactive lanes. + * @pre `data.data()` is aligned to `byte_count` bytes. + */ + template + constexpr static vector_t SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) load_partial_aligned(std::span data) noexcept + requires(active_count <= element_count) + { + if (!std::is_constant_evaluated()) + { + SIMDLIB_PRECONDITION(reinterpret_cast(data.data()) % byte_count == 0, + "Aligned partial SIMD load requires register-width alignment"); + } + if constexpr (active_count == element_count) + return impl::load(data.data()); + else if constexpr (active_count * sizeof(element_t) == byte_count_half) + { + if (std::is_constant_evaluated()) + return load_partial(data); + else if constexpr (using_int) + return impl::load_half_aligned(data.data()); + else + { + using byte_api = Api; + const auto bytes = byte_api::template load_partial_aligned( + std::span{reinterpret_cast(data.data()), byte_count_half}); + return byte_api::template bit_cast(bytes); + } + } + else + return load_partial(data); + } + + /** @brief Loads a logical prefix of one register's byte representation and zero-fills the remaining bytes. + * @tparam active_byte_count Number of leading bytes to load. + * @param data Source containing at least `active_byte_count` bytes. + * @return Register containing the requested byte prefix followed by zero-filled bytes. + */ + template + static vector_t SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) load_bytes_partial(std::span data) noexcept + requires(active_byte_count <= byte_count) + { + SIMDLIB_PRECONDITION(data.size() >= active_byte_count, "Data span must contain at least the requested active byte count"); + using byte_api = Api; + const auto bytes = + byte_api::template load_partial(std::span{reinterpret_cast(data.data()), data.size()}); + return byte_api::template bit_cast(bytes); + } + /** @brief Loads element data into a SIMD register without enforcing a fixed extent. * @param data Source span whose leading elements are read into the register. * @return Register populated from the provided span. @@ -197,6 +267,95 @@ struct Api : public Detail::SimdMappings impl::store_unaligned(vector, data.data()); } + /** @brief Stores a logical prefix of a SIMD register without writing the inactive suffix. + * @tparam active_count Number of leading elements to store. + * @param vector Register value to store. + * @param data Destination containing at least `active_count` elements. + */ + template + constexpr static void SIMD_FLAGS(In, ForceInline, Flatten) store_partial(vector_t vector, std::span data) noexcept + requires(active_count <= element_count) + { + if (!std::is_constant_evaluated()) + { + SIMDLIB_PRECONDITION(data.size() >= active_count, "Data span must contain at least the requested active element count"); + } + if constexpr (active_count == element_count) + { + impl::store_unaligned(vector, data.data()); + } + else if (std::is_constant_evaluated()) + { + const auto lanes = to_array_constexpr(vector); + for (std::size_t index = 0; index < active_count; ++index) + data[index] = lanes[index]; + } + else + { + if constexpr (active_count * sizeof(element_t) == byte_count_half && using_int) + impl::store_half(vector, data.data()); + else if constexpr (active_count * sizeof(element_t) == byte_count_half) + { + using byte_api = Api; + const auto bytes = Api::template bit_cast(vector); + byte_api::template store_partial(bytes, + std::span{reinterpret_cast(data.data()), byte_count_half}); + } + else + { + [vector, data](std::index_sequence) noexcept + { ((data[Indices] = static_cast(extract(Indices)>(vector))), ...); }(std::make_index_sequence{}); + } + } + } + + /** @brief Stores a logical prefix to register-aligned storage without writing the inactive suffix. + * @tparam active_count Number of leading elements to store. + * @param vector Register value to store. + * @param data Register-aligned destination containing at least `active_count` elements. + * @pre `data.data()` is aligned to `byte_count` bytes. + */ + template + constexpr static void SIMD_FLAGS(In, ForceInline, Flatten) store_partial_aligned(vector_t vector, std::span data) noexcept + requires(active_count <= element_count) + { + if (!std::is_constant_evaluated()) + { + SIMDLIB_PRECONDITION(reinterpret_cast(data.data()) % byte_count == 0, + "Aligned partial SIMD store requires register-width alignment"); + } + if constexpr (active_count == element_count) + impl::store(vector, data.data()); + else if (std::is_constant_evaluated()) + store_partial(vector, data); + else if constexpr (active_count * sizeof(element_t) == byte_count_half && using_int) + impl::store_half_aligned(vector, data.data()); + else if constexpr (active_count * sizeof(element_t) == byte_count_half) + { + using byte_api = Api; + const auto bytes = Api::template bit_cast(vector); + byte_api::template store_partial_aligned(bytes, + std::span{reinterpret_cast(data.data()), byte_count_half}); + } + else + store_partial(vector, data); + } + + /** @brief Stores a logical prefix of one register's byte representation without writing the inactive suffix. + * @tparam active_byte_count Number of leading bytes to store. + * @param vector Register value to store. + * @param data Destination containing at least `active_byte_count` bytes. + */ + template + static void SIMD_FLAGS(In, ForceInline, Flatten) store_bytes_partial(vector_t vector, std::span data) noexcept + requires(active_byte_count <= byte_count) + { + SIMDLIB_PRECONDITION(data.size() >= active_byte_count, "Data span must contain at least the requested active byte count"); + using byte_api = Api; + const auto bytes = Api::template bit_cast(vector); + byte_api::template store_partial(bytes, std::span{reinterpret_cast(data.data()), data.size()}); + } + /** @brief Stores a SIMD register into a raw byte span. * @param vector Register value to store. * @param data Destination byte span with capacity for the full register payload. @@ -230,6 +389,65 @@ struct Api : public Detail::SimdMappings return result; } + /** @brief Converts a logical register prefix into an exactly sized fixed array. + * @tparam active_count Number of leading lanes to return. + * @param vector Register value to observe. + * @return Array containing exactly the requested leading lanes. + */ + template + [[nodiscard]] constexpr static std::array SIMD_FLAGS(In, ForceInline, Flatten) to_array_partial(const vector_t vector) noexcept + requires(active_count <= element_count) + { + if (std::is_constant_evaluated()) + { + const auto lanes = to_array_constexpr(vector); + return [&lanes](std::index_sequence) constexpr noexcept + { return std::array{lanes[Indices]...}; }(std::make_index_sequence{}); + } + else + { + std::array result{}; + store_partial(vector, std::span{result}); + return result; + } + } + + /** @brief Finishes integer magnitude by summing the SIMD-produced pairwise squares per 128-bit lane and broadcasting the root. + * @tparam partial_element_t Integer lane type produced by the first pairwise square-and-sum step. + * @param pairSums Register containing `x*x + y*y` style partial sums for each 128-bit lane group. + * @return Register containing the lane-local magnitudes broadcast to every source lane. + */ + template + SIMDLIB_FORCE_INLINE static vector_t VECTORCALL + FinishIntegerMagnitudeFromPairSums(typename Api::vector_t pairSums) noexcept + { + using partial_simd = Api; + using accumulation_t = std::conditional_t, int64_t, uint64_t>; + constexpr std::size_t LaneGroupCount = register_width / 128; + constexpr std::size_t SourceLaneWidth = element_count / LaneGroupCount; + constexpr std::size_t PartialLaneWidth = partial_simd::element_count / LaneGroupCount; + + const auto partialValues = partial_simd::to_array(pairSums); + std::array output{}; + for (std::size_t groupIndex = 0; groupIndex < LaneGroupCount; ++groupIndex) + { + accumulation_t total{}; + const std::size_t partialStart = groupIndex * PartialLaneWidth; + for (std::size_t partialOffset = 0; partialOffset < PartialLaneWidth; ++partialOffset) + { + total += static_cast(partialValues[partialStart + partialOffset]); + } + + const element_t laneMagnitude = static_cast(std::round(std::sqrt(static_cast(total)))); + const std::size_t laneStart = groupIndex * SourceLaneWidth; + for (std::size_t laneOffset = 0; laneOffset < SourceLaneWidth; ++laneOffset) + { + output[laneStart + laneOffset] = laneMagnitude; + } + } + + return construct(output); + } #pragma endregion #pragma region Arithmetic Operations @@ -253,6 +471,34 @@ struct Api : public Detail::SimdMappings return impl::set1(value); } + /** + * @brief Broadcasts one scalar value into a logical lane prefix and zero-fills the remaining lanes. + * @tparam active_count Number of leading lanes initialized to `value`. + * @param value Scalar value to broadcast. + * @return Register containing `value` in the active prefix and all-bits-zero inactive lanes. + */ + template + constexpr static vector_t SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) broadcast_partial(const element_t value) noexcept + requires(active_count <= element_count) + { + return broadcast_partial_native(value, std::make_index_sequence{}); + } + + protected: + /** + * @brief Expands one scalar into the requested active-prefix argument count. + * @tparam indices Active lane positions used to repeat the scalar argument. + * @param value Scalar value repeated across the active prefix. + * @return Register containing the repeated active prefix and a zero-filled suffix. + */ + template + constexpr static vector_t SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) + broadcast_partial_native(const element_t value, std::index_sequence) noexcept + { + return setr_partial(((void)indices, value)...); + } + + public: /** @brief Constructs a register from lane values in native argument order. * @tparam Args Argument pack matching the register lane count. * @param args Lane values in native intrinsic order. diff --git a/include/SimdLib/Detail/Implementations.h b/include/SimdLib/Detail/Implementations.h index f6c256b..a675e61 100644 --- a/include/SimdLib/Detail/Implementations.h +++ b/include/SimdLib/Detail/Implementations.h @@ -3483,6 +3483,13 @@ template struct SimdMappings<128, element_t> : public SimdImpl return _mm_loadl_epi64(reinterpret_cast(ptr)); } + /** @brief Loads the aligned lower half of the register and clears its upper half. */ + static int_vector_t SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) load_half_aligned(const element_t *ptr) noexcept + requires std::is_integral_v + { + return _mm_loadl_epi64(reinterpret_cast(ptr)); + } + /// Loads a full register from memory. Pointer must be appropriately aligned for the register width. static vector_t SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) load(const element_t *ptr) noexcept requires std::is_floating_point_v @@ -3529,6 +3536,13 @@ template struct SimdMappings<128, element_t> : public SimdImpl _mm_storel_epi64(reinterpret_cast(ptr), lhs); } + /** @brief Stores the aligned lower half of the register without touching its upper-half destination. */ + static void SIMD_FLAGS(In, ForceInline, Flatten) store_half_aligned(int_vector_t lhs, void *ptr) noexcept + requires std::is_integral_v + { + _mm_storel_epi64(reinterpret_cast(ptr), lhs); + } + /// Stores a full register to memory. Pointer must be appropriately aligned for the register width. static void SIMD_FLAGS(In, ForceInline, Flatten) store(vector_t lhs, void *ptr) noexcept requires std::is_floating_point_v @@ -6975,6 +6989,14 @@ template struct SimdMappings<256, element_t> : public SimdImpl return _mm256_inserti128_si256(_mm256_setzero_si256(), lo, 0); } + /** @brief Loads the aligned lower half of the register and clears its upper half. */ + static int_vector_t SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) load_half_aligned(const element_t *ptr) noexcept + requires std::is_integral_v + { + const __m128i lo = _mm_load_si128(reinterpret_cast(ptr)); + return _mm256_inserti128_si256(_mm256_setzero_si256(), lo, 0); + } + /// Loads a full register from memory. Pointer must be appropriately aligned for the register width. static vector_t SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) load(const element_t *ptr) noexcept requires std::is_floating_point_v @@ -7023,6 +7045,14 @@ template struct SimdMappings<256, element_t> : public SimdImpl _mm_storeu_si128(reinterpret_cast<__m128i *>(ptr), lo); } + /** @brief Stores the aligned lower half of the register without touching its upper-half destination. */ + static void SIMD_FLAGS(In, ForceInline, Flatten) store_half_aligned(int_vector_t lhs, void *ptr) noexcept + requires std::is_integral_v + { + const __m128i lo = _mm256_castsi256_si128(lhs); + _mm_store_si128(reinterpret_cast<__m128i *>(ptr), lo); + } + /// Stores a full register to memory. Pointer must be appropriately aligned for the register width. static void SIMD_FLAGS(In, ForceInline, Flatten) store(vector_t lhs, void *ptr) noexcept requires std::is_floating_point_v diff --git a/include/SimdLib/IApi.h b/include/SimdLib/IApi.h index bc1606f..132ef0d 100644 --- a/include/SimdLib/IApi.h +++ b/include/SimdLib/IApi.h @@ -2,9 +2,11 @@ #include +#include #include #include #include +#include #include #include @@ -38,6 +40,66 @@ concept Type = requires { typename api_t::vector_t; }; +/** @brief Reports whether an API exposes scalar broadcast. */ +template +concept SetOne = Type && requires(typename api_t::element_type value) { api_t::set1(value); }; + +/** @brief Reports whether an API exposes zero-filling reverse lane construction. */ +template +concept SetReversePartial = Type && requires(lane_types &&...lanes) { api_t::setr_partial(std::forward(lanes)...); }; + +/** @brief Reports whether an API exposes scalar broadcast into an exact active-prefix lane count. */ +template +concept BroadcastPartial = Type && requires(typename api_t::element_type value) { api_t::template broadcast_partial(value); }; + +/** @brief Reports whether an API exposes exact-prefix element loading. */ +template +concept LoadPartial = + Type && requires(std::span source) { api_t::template load_partial(source); }; + +/** @brief Reports whether an API exposes aligned exact-prefix element loading. */ +template +concept LoadPartialAligned = + Type && requires(std::span source) { api_t::template load_partial_aligned(source); }; + +/** @brief Reports whether an API exposes exact-prefix byte loading. */ +template +concept LoadBytesPartial = + Type && requires(std::span source) { api_t::template load_bytes_partial(source); }; + +/** @brief Reports whether an API exposes exact-prefix element storage. */ +template +concept StorePartial = Type && requires(typename api_t::vector_t value, std::span destination) { + api_t::template store_partial(value, destination); +}; + +/** @brief Reports whether an API exposes aligned exact-prefix element storage. */ +template +concept StorePartialAligned = Type && requires(typename api_t::vector_t value, std::span destination) { + api_t::template store_partial_aligned(value, destination); +}; + +/** @brief Reports whether an API exposes exact-prefix byte storage. */ +template +concept StoreBytesPartial = Type && requires(typename api_t::vector_t value, std::span destination) { + api_t::template store_bytes_partial(value, destination); +}; + +/** @brief Reports whether an API exposes exactly sized prefix observation. */ +template +concept ToArrayPartial = Type && requires(typename api_t::vector_t value) { + { api_t::template to_array_partial(value) } -> std::same_as>; +}; + +/** @brief Reports whether an API exposes compile-time-selected lane extraction. */ +template +concept Extract = Type && requires(typename api_t::vector_t value) { api_t::template extract(index)>(value); }; + +/** @brief Reports whether an API exposes compile-time-selected lane insertion. */ +template +concept Insert = + Type && requires(typename api_t::vector_t value, typename api_t::element_type replacement) { api_t::template insert(value, replacement); }; + /** @brief Identifies a valid widening destination API shape. */ template concept WidenTarget = Type && requires { @@ -167,6 +229,60 @@ template concept DotProduct = immediate >= 0 && immediate <= 255 && Type && requires(typename api_t::vector_t lhs, typename api_t::vector_t rhs) { api_t::template dot_product(lhs, rhs); }; +/** @brief Reports whether an API exposes bitwise intersection. */ +template +concept BitwiseAnd = Type && requires(typename api_t::vector_t lhs, typename api_t::vector_t rhs) { api_t::bitwise_and(lhs, rhs); }; + +/** @brief Reports whether an API exposes bitwise union. */ +template +concept BitwiseOr = Type && requires(typename api_t::vector_t lhs, typename api_t::vector_t rhs) { api_t::bitwise_or(lhs, rhs); }; + +/** @brief Reports whether an API exposes bitwise exclusive union. */ +template +concept BitwiseXor = Type && requires(typename api_t::vector_t lhs, typename api_t::vector_t rhs) { api_t::bitwise_xor(lhs, rhs); }; + +/** @brief Reports whether an API exposes bitwise complement. */ +template +concept BitwiseNot = Type && requires(typename api_t::vector_t value) { api_t::bitwise_not(value); }; + +/** @brief Reports whether an API exposes complemented-left bitwise intersection. */ +template +concept BitwiseAndNot = Type && requires(typename api_t::vector_t lhs, typename api_t::vector_t rhs) { api_t::bitwise_andnot(lhs, rhs); }; + +/** @brief Reports whether an API exposes its native-granularity sign-bit mask. */ +template +concept Movemask = Type && requires(typename api_t::vector_t value) { api_t::movemask(value); }; + +/** @brief Reports whether an API exposes native predicate selection. */ +template +concept Select = Type && requires(typename api_t::vector_t condition, typename api_t::vector_t when_true, typename api_t::vector_t when_false) { + api_t::select(condition, when_true, when_false); +}; + +/** @brief Reports whether an API exposes one sign bit per logical lane. */ +template +concept MovemaskSlim = Type && requires(typename api_t::vector_t value) { api_t::movemask_slim(value); }; + +/** @brief Reports whether an API exposes ordered per-lane equality comparison. */ +template +concept CompareEqual = Type && requires(typename api_t::vector_t lhs, typename api_t::vector_t rhs) { api_t::compare_equal(lhs, rhs); }; + +/** @brief Reports whether an API exposes ordered per-lane greater-than comparison. */ +template +concept CompareGreater = Type && requires(typename api_t::vector_t lhs, typename api_t::vector_t rhs) { api_t::compare_greater(lhs, rhs); }; + +/** @brief Reports whether an API exposes ordered per-lane greater-than-or-equal comparison. */ +template +concept CompareGreaterEqual = Type && requires(typename api_t::vector_t lhs, typename api_t::vector_t rhs) { api_t::compare_greater_equal(lhs, rhs); }; + +/** @brief Reports whether an API exposes ordered per-lane less-than comparison. */ +template +concept CompareLess = Type && requires(typename api_t::vector_t lhs, typename api_t::vector_t rhs) { api_t::compare_less(lhs, rhs); }; + +/** @brief Reports whether an API exposes ordered per-lane less-than-or-equal comparison. */ +template +concept CompareLessEqual = Type && requires(typename api_t::vector_t lhs, typename api_t::vector_t rhs) { api_t::compare_less_equal(lhs, rhs); }; + /** @brief Reports whether an API exposes per-lane left shift. */ template concept ShiftLeft = Type && requires(typename api_t::vector_t value) { api_t::shift_left(value, 1); }; diff --git a/include/SimdLib/IPartialRegisterMask.h b/include/SimdLib/IPartialRegisterMask.h new file mode 100644 index 0000000..93231e6 --- /dev/null +++ b/include/SimdLib/IPartialRegisterMask.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include + +namespace SimdLib::IPartialRegisterMask +{ + +/** @brief Identifies an independent predicate type associated with a PartialRegister geometry. */ +template +concept Type = + std::is_final_v && std::is_aggregate_v && std::is_standard_layout_v && requires(mask_t value, typename mask_t::native_type native) { + typename mask_t::element_type; + typename mask_t::api_type; + typename mask_t::native_type; + typename mask_t::register_type; + typename mask_t::bits_type; + { mask_t::register_width } -> std::convertible_to; + { mask_t::byte_count } -> std::convertible_to; + { mask_t::native_lane_count } -> std::convertible_to; + { mask_t::lane_count } -> std::convertible_to; + { value.native } -> std::same_as; + { mask_t::from_native(native) } -> std::same_as; + { value.to_native() } -> std::same_as; + }; + +/** @brief Identifies an independent partial predicate that can reduce its logical lanes. */ +template +concept Reductions = Type && requires(mask_t value) { + { value.any() } -> std::same_as; + { value.all() } -> std::same_as; + { value.none() } -> std::same_as; + { value.bits() } -> std::same_as; +}; + +/** @brief Identifies an independent partial predicate that supports immutable Boolean composition. */ +template +concept Composition = Type && requires(mask_t lhs, mask_t rhs) { + { lhs & rhs } -> std::same_as; + { lhs | rhs } -> std::same_as; + { lhs ^ rhs } -> std::same_as; + { ~lhs } -> std::same_as; +}; + +/** @brief Identifies an independent partial predicate that selects between matching partial values. */ +template +concept Select = Type && requires(mask_t condition, typename mask_t::register_type when_true, typename mask_t::register_type when_false) { + { condition.select(when_true, when_false) } -> std::same_as; +}; + +} // namespace SimdLib::IPartialRegisterMask diff --git a/include/SimdLib/IRegister.h b/include/SimdLib/IRegister.h index 381622d..7c1bcd5 100644 --- a/include/SimdLib/IRegister.h +++ b/include/SimdLib/IRegister.h @@ -24,397 +24,404 @@ concept Type = std::is_aggregate_v && requires(register_t value, typ { register_t{native} } -> std::same_as; }; -/** @brief Reports whether a Register type exposes zero initialization. */ +/** @brief Reports whether a Register-shaped type exposes zero initialization. */ template concept Zero = Type && requires { { register_t::zero() } -> std::same_as; }; -/** @brief Reports whether a Register type exposes scalar broadcast construction. */ +/** @brief Reports whether a Register-shaped type exposes scalar broadcast construction. */ template concept Broadcast = Type && requires(typename register_t::element_type value) { { register_t::broadcast(value) } -> std::same_as; }; -/** @brief Reports whether a Register type accepts the supplied complete logical lane list. */ +/** @brief Reports whether a Register-shaped type accepts the supplied logical lane list. */ template concept FromLanes = Type && sizeof...(lane_types) == register_t::lane_count && requires(lane_types &&...lanes) { { register_t::from_lanes(std::forward(lanes)...) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes fixed-size array construction. */ +/** @brief Reports whether a Register-shaped type exposes fixed-size array construction. */ template concept FromArray = Type && requires(const std::array &source) { { register_t::from_array(source) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes exact-width unaligned loading. */ +/** @brief Reports whether a Register-shaped type exposes logical-extent unaligned loading. */ template concept Load = Type && requires(std::span source) { { register_t::load(source) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes exact-width aligned loading. */ +/** @brief Reports whether a Register-shaped type exposes logical-extent aligned loading. */ template concept LoadAligned = Type && requires(std::span source) { { register_t::load_aligned(source) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes exact-width raw-byte loading. */ +/** @brief Reports whether a Register-shaped type exposes logical-extent raw-byte loading. */ template -concept LoadBytes = Type && requires(std::span source) { +concept LoadBytes = Type && requires(std::span source) { { register_t::load_bytes(source) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes exact-width unaligned storage. */ +/** @brief Reports whether a Register-shaped type exposes logical-extent unaligned storage. */ template concept Store = Type && requires(register_t value, std::span destination) { { value.store(destination) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes exact-width aligned storage. */ +/** @brief Reports whether a Register-shaped type exposes logical-extent aligned storage. */ template concept StoreAligned = Type && requires(register_t value, std::span destination) { { value.store_aligned(destination) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes exact-width raw-byte storage. */ +/** @brief Reports whether a Register-shaped type exposes logical-extent raw-byte storage. */ template -concept StoreBytes = Type && requires(register_t value, std::span destination) { - { value.store_bytes(destination) } -> std::same_as; -}; +concept StoreBytes = + Type && requires(register_t value, std::span destination) { + { value.store_bytes(destination) } -> std::same_as; + }; -/** @brief Reports whether a Register type exposes fixed-size array conversion. */ +/** @brief Reports whether a Register-shaped type exposes fixed-size array conversion. */ template concept ToArray = Type && requires(register_t value) { { value.to_array() } -> std::same_as>; }; -/** @brief Reports whether a Register type exposes one compile-time-selected lane. */ +/** @brief Reports whether a Register-shaped type exposes one compile-time-selected lane. */ template concept Lane = Type && requires(register_t value) { { value.template lane() } -> std::same_as; }; -/** @brief Reports whether a Register type can replace one compile-time-selected lane. */ +/** @brief Reports whether a Register-shaped type can replace one compile-time-selected lane. */ template concept WithLane = Type && requires(register_t value, typename register_t::element_type replacement) { { value.template with_lane(replacement) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes register addition. */ +/** @brief Audits the invariant-bearing construction, transfer, and observation surface shared by Register-shaped types. */ +template +concept CoreSurface = Type && Zero && Broadcast && FromArray && Load && LoadAligned && + LoadBytes && Store && StoreAligned && StoreBytes && ToArray && + Lane && WithLane; + +/** @brief Reports whether a Register-shaped type exposes register addition. */ template concept Add = Type && requires(register_t lhs, register_t rhs) { { lhs + rhs } -> std::same_as; }; -/** @brief Reports whether a Register type exposes register subtraction. */ +/** @brief Reports whether a Register-shaped type exposes register subtraction. */ template concept Subtract = Type && requires(register_t lhs, register_t rhs) { { lhs - rhs } -> std::same_as; }; -/** @brief Reports whether a Register type exposes register multiplication. */ +/** @brief Reports whether a Register-shaped type exposes register multiplication. */ template concept Multiply = Type && requires(register_t lhs, register_t rhs) { { lhs * rhs } -> std::same_as; }; -/** @brief Reports whether a Register type exposes register division. */ +/** @brief Reports whether a Register-shaped type exposes register division. */ template concept Divide = Type && requires(register_t lhs, register_t rhs) { { lhs / rhs } -> std::same_as; }; -/** @brief Reports whether a Register type exposes register remainder. */ +/** @brief Reports whether a Register-shaped type exposes register remainder. */ template concept Modulus = Type && requires(register_t lhs, register_t rhs) { { lhs % rhs } -> std::same_as; }; -/** @brief Reports whether a Register type exposes arithmetic negation. */ +/** @brief Reports whether a Register-shaped type exposes arithmetic negation. */ template concept Negate = Type && requires(register_t value) { { -value } -> std::same_as; }; -/** @brief Reports whether a Register type exposes lane-wise minimum. */ +/** @brief Reports whether a Register-shaped type exposes lane-wise minimum. */ template concept Min = Type && requires(register_t value) { { value.min(value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes lane-wise maximum. */ +/** @brief Reports whether a Register-shaped type exposes lane-wise maximum. */ template concept Max = Type && requires(register_t value) { { value.max(value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes lane-wise absolute value. */ +/** @brief Reports whether a Register-shaped type exposes lane-wise absolute value. */ template concept Absolute = Type && requires(register_t value) { { value.absolute() } -> std::same_as; }; -/** @brief Reports whether a Register type exposes lane-wise square root. */ +/** @brief Reports whether a Register-shaped type exposes lane-wise square root. */ template concept Sqrt = Type && requires(register_t value) { { value.sqrt() } -> std::same_as; }; -/** @brief Reports whether a Register type exposes lane-wise average. */ +/** @brief Reports whether a Register-shaped type exposes lane-wise average. */ template concept Average = Type && requires(register_t value) { { value.average(value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes multiply-add. */ +/** @brief Reports whether a Register-shaped type exposes multiply-add. */ template concept MultiplyAdd = Type && requires(register_t value) { { value.multiply_add(value, value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes magnitude. */ +/** @brief Reports whether a Register-shaped type exposes magnitude. */ template concept Magnitude = Type && requires(register_t value) { { value.magnitude() } -> std::same_as; }; -/** @brief Reports whether a Register type exposes checked magnitude. */ +/** @brief Reports whether a Register-shaped type exposes checked magnitude. */ template concept MagnitudeChecked = Type && requires(register_t value) { { value.magnitude_checked() } -> std::same_as; }; -/** @brief Reports whether a Register type exposes normalization. */ +/** @brief Reports whether a Register-shaped type exposes normalization. */ template concept Normalize = Type && requires(register_t value) { { value.normalize() } -> std::same_as; }; -/** @brief Reports whether a Register type exposes horizontal addition. */ +/** @brief Reports whether a Register-shaped type exposes horizontal addition. */ template concept HorizontalAdd = Type && requires(register_t value) { { value.horizontal_add(value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes horizontal subtraction. */ +/** @brief Reports whether a Register-shaped type exposes horizontal subtraction. */ template concept HorizontalSubtract = Type && requires(register_t value) { { value.horizontal_subtract(value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes adjacent multiply-add for the requested source type. */ +/** @brief Reports whether a Register-shaped type exposes adjacent multiply-add for the requested source type. */ template concept MultiplyAddAdjacent = Type && requires(register_t value) { value.template multiply_add_adjacent(value); }; -/** @brief Reports whether a Register type exposes unsigned/signed byte multiply-add for the requested source type. */ +/** @brief Reports whether a Register-shaped type exposes unsigned/signed byte multiply-add for the requested source type. */ template concept MultiplyAddUnsignedSignedBytes = Type && requires(register_t value) { value.template multiply_add_unsigned_signed_bytes(value); }; -/** @brief Reports whether a Register type exposes byte sum-of-absolute-differences for the requested source type. */ +/** @brief Reports whether a Register-shaped type exposes byte sum-of-absolute-differences for the requested source type. */ template concept SumAbsoluteByteDifferences = Type && requires(register_t value) { value.template sum_absolute_byte_differences(value); }; -/** @brief Reports whether a Register type exposes immediate-controlled multi-SAD for the requested source type. */ +/** @brief Reports whether a Register-shaped type exposes immediate-controlled multi-SAD for the requested source type. */ template concept MultiSumAbsoluteByteDifferences = Type && requires(register_t value) { value.template multi_sum_absolute_byte_differences(value); }; -/** @brief Reports whether a Register type exposes minimum-position lookup. */ +/** @brief Reports whether a Register-shaped type exposes minimum-position lookup. */ template concept MinPosition = Type && requires(register_t value) { { value.min_position() } -> std::same_as; }; -/** @brief Reports whether a Register type exposes maximum-position lookup. */ +/** @brief Reports whether a Register-shaped type exposes maximum-position lookup. */ template concept MaxPosition = Type && requires(register_t value) { { value.max_position() } -> std::same_as; }; -/** @brief Reports whether a Register type exposes saturating addition. */ +/** @brief Reports whether a Register-shaped type exposes saturating addition. */ template concept AddSaturated = Type && requires(register_t value) { { value.add_saturated(value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes saturating subtraction. */ +/** @brief Reports whether a Register-shaped type exposes saturating subtraction. */ template concept SubtractSaturated = Type && requires(register_t value) { { value.subtract_saturated(value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes saturating horizontal addition. */ +/** @brief Reports whether a Register-shaped type exposes saturating horizontal addition. */ template concept HorizontalAddSaturated = Type && requires(register_t value) { { value.horizontal_add_saturated(value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes saturating horizontal subtraction. */ +/** @brief Reports whether a Register-shaped type exposes saturating horizontal subtraction. */ template concept HorizontalSubtractSaturated = Type && requires(register_t value) { { value.horizontal_subtract_saturated(value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes alternating add-subtract. */ +/** @brief Reports whether a Register-shaped type exposes alternating add-subtract. */ template concept AddSubtract = Type && requires(register_t value) { { value.add_subtract(value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes an immediate-controlled dot product. */ +/** @brief Reports whether a Register-shaped type exposes an immediate-controlled dot product. */ template concept DotProduct = Type && requires(register_t value) { { value.template dot_product(value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes bitwise AND. */ +/** @brief Reports whether a Register-shaped type exposes bitwise AND. */ template concept BitwiseAnd = Type && requires(register_t lhs, register_t rhs) { { lhs & rhs } -> std::same_as; }; -/** @brief Reports whether a Register type exposes bitwise OR. */ +/** @brief Reports whether a Register-shaped type exposes bitwise OR. */ template concept BitwiseOr = Type && requires(register_t lhs, register_t rhs) { { lhs | rhs } -> std::same_as; }; -/** @brief Reports whether a Register type exposes bitwise XOR. */ +/** @brief Reports whether a Register-shaped type exposes bitwise XOR. */ template concept BitwiseXor = Type && requires(register_t lhs, register_t rhs) { { lhs ^ rhs } -> std::same_as; }; -/** @brief Reports whether a Register type exposes bitwise complement. */ +/** @brief Reports whether a Register-shaped type exposes bitwise complement. */ template concept BitwiseNot = Type && requires(register_t value) { { ~value } -> std::same_as; }; -/** @brief Reports whether a Register type exposes bitwise AND-NOT. */ +/** @brief Reports whether a Register-shaped type exposes bitwise AND-NOT. */ template concept BitwiseAndNot = Type && requires(register_t lhs, register_t rhs) { { lhs.andnot(rhs) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes its native-granularity sign mask. */ +/** @brief Reports whether a Register-shaped type exposes its native-granularity sign mask. */ template concept Movemask = Type && requires(register_t value) { { value.movemask() } -> std::same_as; }; -/** @brief Reports whether a Register type exposes one sign bit per logical lane. */ +/** @brief Reports whether a Register-shaped type exposes one sign bit per logical lane. */ template concept LaneSignBits = Type && requires(register_t value) { { value.lane_sign_bits() } -> std::same_as; }; -/** @brief Reports whether a Register type exposes per-lane left shift. */ +/** @brief Reports whether a Register-shaped type exposes per-lane left shift. */ template concept ShiftLeft = Type && requires(register_t value) { { value << 1 } -> std::same_as; }; -/** @brief Reports whether a Register type exposes logical per-lane right shift. */ +/** @brief Reports whether a Register-shaped type exposes logical per-lane right shift. */ template concept LogicalShiftRight = Type && requires(register_t value) { { value.logical_shift_right(1) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes signedness-selected per-lane right shift. */ +/** @brief Reports whether a Register-shaped type exposes signedness-selected per-lane right shift. */ template concept ShiftRight = Type && requires(register_t value) { { value >> 1 } -> std::same_as; }; -/** @brief Reports whether a Register type exposes explicit slow-path complete-register dynamic byte left shift. */ +/** @brief Reports whether a Register-shaped type exposes explicit slow-path logical-payload dynamic byte left shift. */ template concept ShiftBytesLeftSlow = Type && requires(register_t value) { { value.shift_bytes_left_slow(1) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes explicit slow-path complete-register dynamic byte right shift. */ +/** @brief Reports whether a Register-shaped type exposes explicit slow-path logical-payload dynamic byte right shift. */ template concept ShiftBytesRightSlow = Type && requires(register_t value) { { value.shift_bytes_right_slow(1) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes immediate complete-register byte left shift. */ +/** @brief Reports whether a Register-shaped type exposes immediate logical-payload byte left shift. */ template concept ShiftBytesLeft = count >= 0 && Type && requires(register_t value) { { value.template shift_bytes_left() } -> std::same_as; }; -/** @brief Reports whether a Register type exposes immediate complete-register byte right shift. */ +/** @brief Reports whether a Register-shaped type exposes immediate logical-payload byte right shift. */ template concept ShiftBytesRight = count >= 0 && Type && requires(register_t value) { { value.template shift_bytes_right() } -> std::same_as; }; -/** @brief Reports whether a Register type exposes explicit slow-path complete-register dynamic bit left shift. */ +/** @brief Reports whether a Register-shaped type exposes explicit slow-path logical-payload dynamic bit left shift. */ template concept ShiftBitsLeftSlow = Type && requires(register_t value) { { value.shift_bits_left_slow(1) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes explicit slow-path complete-register dynamic bit right shift. */ +/** @brief Reports whether a Register-shaped type exposes explicit slow-path logical-payload dynamic bit right shift. */ template concept ShiftBitsRightSlow = Type && requires(register_t value) { { value.shift_bits_right_slow(1) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes complete-register compile-time bit left shift. */ +/** @brief Reports whether a Register-shaped type exposes logical-payload compile-time bit left shift. */ template concept ShiftBitsLeft = Type && requires(register_t value) { { value.template shift_bits_left() } -> std::same_as; }; -/** @brief Reports whether a Register type exposes complete-register compile-time bit right shift. */ +/** @brief Reports whether a Register-shaped type exposes logical-payload compile-time bit right shift. */ template concept ShiftBitsRight = Type && requires(register_t value) { { value.template shift_bits_right() } -> std::same_as; }; -/** @brief Reports whether a Register type exposes ordered equality comparison. */ +/** @brief Reports whether a Register-shaped type exposes ordered equality comparison. */ template concept CompareEqual = Type && requires(register_t value) { { value.compare_equal(value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes ordered greater-than comparison. */ +/** @brief Reports whether a Register-shaped type exposes ordered greater-than comparison. */ template concept CompareGreater = Type && requires(register_t value) { { value.compare_greater(value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes ordered greater-than-or-equal comparison. */ +/** @brief Reports whether a Register-shaped type exposes ordered greater-than-or-equal comparison. */ template concept CompareGreaterEqual = Type && requires(register_t value) { { value.compare_greater_equal(value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes ordered less-than comparison. */ +/** @brief Reports whether a Register-shaped type exposes ordered less-than comparison. */ template concept CompareLess = Type && requires(register_t value) { { value.compare_less(value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes ordered less-than-or-equal comparison. */ +/** @brief Reports whether a Register-shaped type exposes ordered less-than-or-equal comparison. */ template concept CompareLessEqual = Type && requires(register_t value) { { value.compare_less_equal(value) } -> std::same_as; }; -/** @brief Reports whether a Register type exposes whole-register equality. */ +/** @brief Reports whether a Register-shaped type exposes logical-register equality. */ template concept Equal = Type && requires(register_t lhs, register_t rhs) { { lhs == rhs } -> std::same_as; }; -/** @brief Reports whether a Register type exposes whole-register inequality. */ +/** @brief Reports whether a Register-shaped type exposes logical-register inequality. */ template concept NotEqual = Type && requires(register_t lhs, register_t rhs) { { lhs != rhs } -> std::same_as; @@ -424,19 +431,19 @@ concept NotEqual = Type && requires(register_t lhs, register_t rhs) template concept Shape = Type && std::same_as && register_t::register_width == bits; -/** @brief Reports whether a Register exposes its lower 128-bit half. */ +/** @brief Reports whether a Register-shaped type exposes its lower 128-bit half. */ template concept LowerHalf = Type && requires(register_t value) { { value.lower_half() } -> Shape; }; -/** @brief Reports whether a Register exposes low-lane unpacking. */ +/** @brief Reports whether a Register-shaped type exposes low-lane unpacking. */ template concept UnpackLow = Type && requires(register_t value) { { value.unpack_low(value) } -> std::same_as; }; -/** @brief Reports whether a Register exposes high-lane unpacking. */ +/** @brief Reports whether a Register-shaped type exposes high-lane unpacking. */ template concept UnpackHigh = Type && requires(register_t value) { { value.unpack_high(value) } -> std::same_as; @@ -454,37 +461,37 @@ concept ShuffleBytes = Type && requires(register_t value) { { value.template shuffle_bytes() } -> std::same_as; }; -/** @brief Reports whether a Register exposes an immediate-controlled low-half shuffle. */ +/** @brief Reports whether a Register-shaped type exposes an immediate-controlled low-half shuffle. */ template concept ShuffleLow = Type && requires(register_t value) { { value.template shuffle_low() } -> std::same_as; }; -/** @brief Reports whether a Register exposes an immediate-controlled high-half shuffle. */ +/** @brief Reports whether a Register-shaped type exposes an immediate-controlled high-half shuffle. */ template concept ShuffleHigh = Type && requires(register_t value) { { value.template shuffle_high() } -> std::same_as; }; -/** @brief Reports whether a Register exposes an immediate-controlled two-register blend. */ +/** @brief Reports whether a Register-shaped type exposes an immediate-controlled two-register blend. */ template concept Blend = Type && requires(register_t lhs, register_t rhs) { { lhs.template blend(rhs) } -> std::same_as; }; -/** @brief Reports whether a Register can reinterpret its complete bit pattern as the requested element type. */ +/** @brief Reports whether a Register-shaped type can reinterpret its logical bit pattern as the requested element type. */ template concept BitCast = Type && requires(register_t value) { { value.template bit_cast() } -> Shape; }; -/** @brief Reports whether a Register can numerically convert every lane to the requested element type. */ +/** @brief Reports whether a Register-shaped type can numerically convert every lane to the requested element type. */ template concept Convert = Type && requires(register_t value) { { value.template convert() } -> Shape; }; -/** @brief Reports whether a Register can widen its lowest lanes into the requested complete target register. */ +/** @brief Reports whether a Register-shaped type can widen its lowest lanes into the requested target register. */ template concept WidenLow = Type && requires(register_t value) { { value.template widen_low() } -> Shape; diff --git a/include/SimdLib/PartialRegister.h b/include/SimdLib/PartialRegister.h new file mode 100644 index 0000000..07c0f06 --- /dev/null +++ b/include/SimdLib/PartialRegister.h @@ -0,0 +1,1336 @@ +#pragma once + +#include + +#if !SIMDLIB_REGISTER_INTERFACE_AVAILABLE && !SIMDLIB_REQUIRE_REGISTER_INTERFACE +#error "SIMDLIB_PARTIAL_REGISTER_HEADER_REQUIRES_CXX23: requires C++23 explicit object parameter support" +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace SimdLib +{ + +/** + * @brief Owns one SIMD register with a fixed low prefix of logical lanes. + * @tparam element_t Scalar interpretation of every lane. + * @tparam bits Physical native-register width in bits. + * @tparam active_lane_count Number of active low lanes. + * @invariant The sole native value has all-bits-zero inactive high lanes. + * @remarks The type is available only for a non-empty, non-complete active prefix. + */ +template + requires PartialRegisterAvailable +class PartialRegister final +{ + public: + using element_type = element_t; + using api_type = Api; + using native_type = typename api_type::vector_t; + using mask_type = PartialRegisterMask; + + constexpr static inline std::size_t register_width = bits; + constexpr static inline std::size_t byte_count = api_type::byte_count; + constexpr static inline std::size_t native_lane_count = api_type::element_count; + constexpr static inline std::size_t lane_count = active_lane_count; + constexpr static inline std::size_t active_byte_count = lane_count * sizeof(element_type); + constexpr static inline std::size_t inactive_lane_count = native_lane_count - lane_count; + + private: + /** @brief Compile-time inactive-lane divisor identity used by division and modulus. */ + constexpr static inline std::array inactive_divisor_identity = []() constexpr + { + std::array result{}; + for (std::size_t lane = lane_count; lane < native_lane_count; ++lane) + result[lane] = element_type{1}; + return result; + }(); + + /** @brief Compile-time inactive-lane maximum used to exclude the suffix from minimum-position searches. */ + constexpr static inline std::array inactive_min_position_identity = []() constexpr + { + std::array result{}; + for (std::size_t lane = lane_count; lane < native_lane_count; ++lane) + result[lane] = std::numeric_limits::max(); + return result; + }(); + + /** @brief Compile-time inactive-lane minimum used to exclude the suffix from maximum-position searches. */ + constexpr static inline std::array inactive_max_position_identity = []() constexpr + { + std::array result{}; + for (std::size_t lane = lane_count; lane < native_lane_count; ++lane) + result[lane] = std::numeric_limits::lowest(); + return result; + }(); + + /** @brief Compile-time all-bits-one active prefix and all-bits-zero inactive suffix. */ + constexpr static inline std::array active_lane_filter = []() constexpr + { + std::array result{}; + std::array one_bytes{}; + for (auto &byte : one_bytes) + byte = std::byte{0xff}; + const auto one = std::bit_cast(one_bytes); + for (std::size_t lane = 0; lane < lane_count; ++lane) + result[lane] = one; + return result; + }(); + + /** + * @brief Wraps a lane-combining native result in its deliberate partial or complete result type. + * @tparam result_t Public result type selected by the corresponding result alias. + * @param native Native result produced by the source API operation. + * @return Complete result unchanged, or partial result with its inactive suffix cleared. + */ + template + [[nodiscard]] constexpr static result_t SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) make_specialized_result(result_native_t native) noexcept + { + if constexpr (result_t::lane_count == result_t::api_type::element_count) + return result_t{native}; + else + return result_t::from_native(native); + } + + /** + * @brief Reports whether an API can shuffle an active selector list padded with one known-zero source lane. + * @tparam shuffle_api_t API whose logical lane type is being shuffled. + * @tparam active_count Number of meaningful low lanes or bytes in that API interpretation. + * @tparam indices Active-prefix source selectors. + * @tparam padding_indices Positions in the inactive result suffix. + * @return True when the expanded native-width selector list is supported by the API. + */ + template + [[nodiscard]] consteval static bool partial_shuffle_available(std::index_sequence) noexcept + { + return IApi::Shuffle; + } + + /** + * @brief Applies an active-prefix shuffle while selecting the first inactive zero lane for every suffix output. + * @tparam shuffle_api_t API whose logical lane type is being shuffled. + * @tparam active_count Number of meaningful low lanes or bytes in that API interpretation. + * @tparam indices Active-prefix source selectors. + * @tparam padding_indices Positions in the inactive result suffix. + * @param value Native value with a zero suffix beginning at `active_count`. + * @return Native-width shuffled value whose inactive result suffix remains zero. + */ + template + [[nodiscard]] constexpr static typename shuffle_api_t::vector_t SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + partial_shuffle_native(typename shuffle_api_t::vector_t value, std::index_sequence) noexcept + requires IApi::Shuffle + { + return shuffle_api_t::template shuffle(value); + } + + /** + * @brief Reports whether a dot-product immediate writes only active result lanes. + * @tparam imm8 Intrinsic dot-product control byte. + * @return True when every selected destination position is active in every physical 128-bit group. + */ + template [[nodiscard]] consteval static bool dot_product_outputs_are_active() noexcept + { + constexpr std::size_t group_lane_count = 128 / (sizeof(element_type) * 8); + for (std::size_t lane = lane_count; lane < native_lane_count; ++lane) + if ((imm8 & (1 << (lane % group_lane_count))) != 0) + return false; + return true; + } + + /** + * @brief Validates division and modulus preconditions over active lanes only when checks are enabled. + * @param dividends Native dividend lanes with a zero inactive suffix. + * @param divisors Native divisor lanes with a zero inactive suffix. + */ + static void SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) validate_active_divisors(native_type dividends, native_type divisors) noexcept + { +#if SIMDLIB_ENABLE_CHECKS + using mask_t = typename api_type::mask_t; + constexpr mask_t active_bits = (mask_t{1} << lane_count) - mask_t{1}; + const auto zero_divisor_bits = api_type::cmp_eq_slim(divisors, api_type::setzero()) & active_bits; + SIMDLIB_PRECONDITION(zero_divisor_bits == 0, "PartialRegister active divisor lanes must be nonzero"); + if constexpr (std::is_integral_v && std::is_signed_v) + { + const auto minimum_bits = api_type::cmp_eq_slim(dividends, api_type::set1(std::numeric_limits::lowest())); + const auto negative_one_bits = api_type::cmp_eq_slim(divisors, api_type::set1(element_type{-1})); + SIMDLIB_PRECONDITION((minimum_bits & negative_one_bits & active_bits) == 0, "PartialRegister signed minimum cannot be divided by negative one"); + } +#else + (void)dividends; + (void)divisors; +#endif + } + + /** + * @brief Clears inactive high lanes from an arbitrary native value. + * @param native Native value whose logical low prefix is retained. + * @return Native value with an all-bits-zero inactive suffix. + */ + [[nodiscard]] constexpr static native_type SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) normalize_native(native_type native) noexcept + requires IApi::BitwiseAnd + { + if consteval + { + auto lanes = api_type::to_array(native); + for (std::size_t lane = lane_count; lane < native_lane_count; ++lane) + lanes[lane] = element_type{}; + return api_type::construct(lanes); + } + return api_type::bitwise_and(native, api_type::construct(active_lane_filter)); + } + + /** + * @brief Validates the bit representation of every inactive lane when checks are enabled. + * @param native Native value expected to have an all-bits-zero inactive suffix. + * @return The unchanged native value. + */ + [[nodiscard]] constexpr static native_type validate_native(native_type native) noexcept + { +#if SIMDLIB_ENABLE_CHECKS + using byte_api_type = Api; + const auto bytes = api_type::template bit_cast(native); + const auto zero_bytes = byte_api_type::compare_equal(bytes, byte_api_type::setzero()); + const auto zero_bits = byte_api_type::movemask_slim(zero_bytes); + for (std::size_t byte = active_byte_count; byte < byte_count; ++byte) + SIMDLIB_PRECONDITION((zero_bits & (typename byte_api_type::mask_t{1} << byte)) != 0, + "PartialRegister inactive lanes must have an all-bits-zero representation"); +#endif + return native; + } + + public: + /** + * @brief Owns the partial native register represented by this aggregate. + * @pre Direct aggregate initialization must supply a value with a bitwise-zero inactive suffix. + */ + native_type native = api_type::setzero(); + + /** + * @brief Imports a native value after clearing its inactive high-lane suffix. + * @param native Native register value whose logical low prefix is retained. + * @return A PartialRegister with a bitwise-zero inactive suffix. + */ + [[nodiscard]] constexpr static PartialRegister SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) from_native(native_type native) noexcept + requires IApi::BitwiseAnd + { + return PartialRegister{normalize_native(native)}; + } + + /** + * @brief Exports the sole native register value by value. + * @return A native value whose inactive high-lane suffix is all-bits zero. + */ + [[nodiscard]] constexpr native_type SIMD_FLAGS(In, ForceInline, Flatten) to_native(this PartialRegister value) noexcept + { + return validate_native(value.native); + } + + /** + * @brief Imports the logical low prefix of a matching complete Register. + * @param value Complete register whose low `lane_count` lanes are retained. + * @return PartialRegister with the discarded high lanes cleared to all-bits zero. + * @remarks Available exactly when inactive-suffix normalization is supported by the selected API. + */ + [[nodiscard]] constexpr static PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + from_register(Register value) noexcept + requires IApi::BitwiseAnd + { + return from_native(value.native); + } + + /** + * @brief Exports this value as a matching complete Register with zero high lanes. + * @return Complete register containing the active prefix followed by the invariant zero suffix. + */ + [[nodiscard]] constexpr Register SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + to_register(this PartialRegister value) noexcept + { + return Register{value.to_native()}; + } + + /** + * @brief Returns a value with every active and inactive lane set to all-bits zero. + * @return A fully initialized logical zero value. + */ + [[nodiscard]] constexpr static PartialRegister SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) zero() noexcept + { + return PartialRegister{api_type::setzero()}; + } + + /** + * @brief Broadcasts one scalar value to every active lane and clears every inactive lane. + * @param value Scalar value to broadcast. + * @return PartialRegister containing `value` in every active lane and positive zero elsewhere. + * @remarks Available exactly when `IApi::BroadcastPartial` is satisfied. + */ + [[nodiscard]] constexpr static PartialRegister SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) broadcast(element_type value) noexcept + requires IApi::BroadcastPartial + { + return PartialRegister{api_type::template broadcast_partial(value)}; + } + + /** + * @brief Constructs a partial register from exactly its active logical lane list. + * @tparam lane_types Scalar argument types convertible to `element_type`. + * @param lanes Values in low-to-high logical lane order. + * @return PartialRegister containing the supplied active values and a zero inactive suffix. + */ + template ... lane_types> + requires(sizeof...(lane_types) == lane_count) && IApi::SetReversePartial + [[nodiscard]] constexpr static PartialRegister SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) from_lanes(lane_types &&...lanes) noexcept + { + return PartialRegister{api_type::setr_partial(static_cast(std::forward(lanes))...)}; + } + + /** + * @brief Constructs a partial register from exactly its active fixed-size lane array. + * @param source Active values in low-to-high logical order. + * @return PartialRegister containing the supplied active values and a zero inactive suffix. + */ + [[nodiscard]] constexpr static PartialRegister SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) + from_array(const std::array &source) noexcept + requires IApi::LoadPartial + { + return PartialRegister{api_type::template load_partial(std::span{source})}; + } + + /** + * @brief Loads exactly the active logical elements from potentially unaligned storage. + * @param source Source containing exactly the active logical extent. + * @return PartialRegister containing the source values and a zero inactive suffix. + * @remarks The operation does not read beyond `source`. + */ + [[nodiscard]] static PartialRegister SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) load(std::span source) noexcept + requires IApi::LoadPartial + { + return PartialRegister{api_type::template load_partial(source)}; + } + + /** + * @brief Loads exactly the active logical elements from register-aligned storage. + * @param source Register-aligned source containing exactly the active logical extent. + * @return PartialRegister containing the source values and a zero inactive suffix. + * @pre `source.data()` is aligned to `byte_count` bytes. + * @remarks The operation does not read beyond `source`. + */ + [[nodiscard]] static PartialRegister SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) + load_aligned(std::span source) noexcept + requires IApi::LoadPartialAligned + { + return PartialRegister{api_type::template load_partial_aligned(source)}; + } + + /** + * @brief Loads exactly the active logical byte representation from potentially unaligned storage. + * @param source Source containing exactly `active_byte_count` bytes. + * @return PartialRegister containing the source bit pattern and a zero inactive suffix. + * @remarks The operation does not read beyond `source`. + */ + [[nodiscard]] static PartialRegister SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) + load_bytes(std::span source) noexcept + requires IApi::LoadBytesPartial + { + return PartialRegister{api_type::template load_bytes_partial(source)}; + } + + /** + * @brief Stores exactly the active logical elements to potentially unaligned storage. + * @param value Partial register to store. + * @param destination Destination containing exactly the active logical extent. + * @remarks The operation does not write beyond `destination`. + */ + void SIMD_FLAGS(In, ForceInline, Flatten) store(this PartialRegister value, std::span destination) noexcept + requires IApi::StorePartial + { + api_type::template store_partial(value.to_native(), destination); + } + + /** + * @brief Stores exactly the active logical elements to register-aligned storage. + * @param value Partial register to store. + * @param destination Register-aligned destination containing exactly the active logical extent. + * @pre `destination.data()` is aligned to `byte_count` bytes. + * @remarks The operation does not write beyond `destination`. + */ + void SIMD_FLAGS(In, ForceInline, Flatten) store_aligned(this PartialRegister value, std::span destination) noexcept + requires IApi::StorePartialAligned + { + api_type::template store_partial_aligned(value.to_native(), destination); + } + + /** + * @brief Stores exactly the active logical byte representation to potentially unaligned storage. + * @param value Partial register to store. + * @param destination Destination containing exactly `active_byte_count` bytes. + * @remarks The operation does not write beyond `destination`. + */ + void SIMD_FLAGS(In, ForceInline, Flatten) store_bytes(this PartialRegister value, std::span destination) noexcept + requires IApi::StoreBytesPartial + { + api_type::template store_bytes_partial(value.to_native(), destination); + } + + /** + * @brief Copies exactly the active logical lanes into a fixed-size array. + * @param value Partial register to observe. + * @return Active lanes in low-to-high logical order. + */ + [[nodiscard]] constexpr std::array SIMD_FLAGS(In, ForceInline, Flatten) to_array(this PartialRegister value) noexcept + requires IApi::ToArrayPartial + { + return api_type::template to_array_partial(value.to_native()); + } + + /** + * @brief Returns one compile-time-selected active lane. + * @tparam index Active logical lane index. + * @param value Partial register containing the selected lane. + * @return Copy of the selected active lane. + * @remarks Available exactly when `index < lane_count` and `IApi::Extract` are satisfied. + */ + template + requires(index < lane_count) && IApi::Extract + [[nodiscard]] constexpr element_type SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) lane(this PartialRegister value) noexcept + { + if consteval + { + return value.to_array()[index]; + } + return api_type::template extract(index)>(value.to_native()); + } + + /** + * @brief Returns a copy with one compile-time-selected active lane replaced. + * @tparam index Active logical lane index. + * @param value Partial register containing the lanes to copy. + * @param replacement Replacement value for the selected active lane. + * @return PartialRegister with lane `index` replaced and the inactive suffix unchanged. + * @remarks Available exactly when `index < lane_count` and `IApi::Insert` are satisfied. + */ + template + requires(index < lane_count) && IApi::Insert + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + with_lane(this PartialRegister value, element_type replacement) noexcept + { + value.native = api_type::template insert(value.to_native(), replacement); + return value; + } + +#pragma region Arithmetic Operations + + /** + * @brief Adds corresponding active lanes and preserves the zero inactive suffix. + * @param lhs Left active-lane addends. + * @param rhs Right active-lane addends. + * @return Same-shaped partial register containing the active sums and inactive zeros. + * @remarks Available exactly when `IApi::Add` is satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator+(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::Add + { + return PartialRegister{api_type::add(lhs.native, rhs.native)}; + } + + /** + * @brief Subtracts corresponding active lanes and clears any inactive negative-zero representations. + * @param lhs Active-lane minuends. + * @param rhs Active-lane subtrahends. + * @return Same-shaped partial register containing the active differences and inactive bitwise zeros. + * @remarks Floating results require suffix normalization. Available when the listed `IApi` contracts are satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator-(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::Subtract && (!std::is_floating_point_v || IApi::BitwiseAnd) + { + const auto result = api_type::subtract(lhs.native, rhs.native); + if constexpr (std::is_floating_point_v) + return PartialRegister{normalize_native(result)}; + else + return PartialRegister{result}; + } + + /** + * @brief Multiplies corresponding active lanes and preserves the zero inactive suffix. + * @param lhs Left active-lane factors. + * @param rhs Right active-lane factors. + * @return Same-shaped partial register containing the active products and inactive zeros. + * @remarks Available exactly when `IApi::Multiply` is satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator*(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::Multiply + { + return PartialRegister{api_type::multiply(lhs.native, rhs.native)}; + } + + /** + * @brief Divides corresponding active lanes after replacing inactive divisors with one. + * @param lhs Active-lane dividends. + * @param rhs Active-lane divisors. + * @return Same-shaped partial register containing active quotients and inactive bitwise zeros. + * @pre Every active divisor is nonzero and signed minimum is not divided by negative one. + * @remarks Available when the listed `IApi` contracts are satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator/(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::Divide && IApi::BitwiseOr && IApi::BitwiseAnd + { + validate_active_divisors(lhs.native, rhs.native); + const auto divisors = api_type::bitwise_or(rhs.native, api_type::construct(PartialRegister::inactive_divisor_identity)); + return PartialRegister{normalize_native(api_type::divide(lhs.native, divisors))}; + } + + /** + * @brief Computes corresponding active-lane remainders after replacing inactive divisors with one. + * @param lhs Active-lane dividends. + * @param rhs Active-lane divisors. + * @return Same-shaped partial register containing active remainders and inactive bitwise zeros. + * @pre Every active divisor is nonzero and signed minimum is not divided by negative one. + * @remarks Available when the listed `IApi` contracts are satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator%(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::Modulus && IApi::BitwiseOr && IApi::BitwiseAnd + { + validate_active_divisors(lhs.native, rhs.native); + const auto divisors = api_type::bitwise_or(rhs.native, api_type::construct(PartialRegister::inactive_divisor_identity)); + return PartialRegister{normalize_native(api_type::modulus(lhs.native, divisors))}; + } + + /** + * @brief Negates every active lane and clears negative-zero bit patterns from the inactive suffix. + * @param value Active lanes to negate. + * @return Same-shaped partial register containing active negations and inactive bitwise zeros. + * @remarks Available when `IApi::Negate` and `IApi::BitwiseAnd` are satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator-(this PartialRegister value) noexcept + requires IApi::Negate && IApi::BitwiseAnd + { + return PartialRegister{normalize_native(api_type::negate(value.native))}; + } + +#pragma endregion +#pragma region Specialized Arithmetic and Reductions + + /** + * @brief Selects the intrinsic-defined minimum for each active lane and clears the suffix. + * @param lhs First active-lane candidates. + * @param rhs Second active-lane candidates. + * @return Same-shaped partial register containing active minima and inactive bitwise zeros. + * @remarks Available when `IApi::Min` and `IApi::BitwiseAnd` are satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) min(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::Min + { + return PartialRegister{api_type::min(lhs.native, rhs.native)}; + } + + /** + * @brief Selects the intrinsic-defined maximum for each active lane and clears the suffix. + * @param lhs First active-lane candidates. + * @param rhs Second active-lane candidates. + * @return Same-shaped partial register containing active maxima and inactive bitwise zeros. + * @remarks Available when `IApi::Max` and `IApi::BitwiseAnd` are satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) max(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::Max + { + return PartialRegister{api_type::max(lhs.native, rhs.native)}; + } + + /** + * @brief Computes the absolute value of each active lane and preserves inactive zeros. + * @param value Active lanes whose absolute values are requested. + * @return Same-shaped partial register containing active absolute values and inactive zeros. + * @remarks Available exactly when `IApi::Absolute` is satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) absolute(this PartialRegister value) noexcept + requires IApi::Absolute + { + return PartialRegister{api_type::absolute(value.native)}; + } + + /** + * @brief Computes the square root of each active lane and preserves inactive positive zeros. + * @param value Active lanes whose square roots are requested. + * @return Same-shaped partial register containing active square roots and inactive positive zeros. + * @remarks Exceptional active inputs follow the API contract. Available exactly when `IApi::Sqrt` is satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) sqrt(this PartialRegister value) noexcept + requires IApi::Sqrt + { + return PartialRegister{api_type::sqrt(value.native)}; + } + + /** + * @brief Computes the intrinsic-defined average of corresponding active lanes. + * @param lhs Left active-lane inputs. + * @param rhs Right active-lane inputs. + * @return Same-shaped partial register containing active averages and inactive zeros. + * @remarks Rounding follows the API contract. Available exactly when `IApi::Average` is satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) average(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::Average + { + return PartialRegister{api_type::avg(lhs.native, rhs.native)}; + } + + /** + * @brief Multiplies corresponding active lanes and adds the corresponding active addend. + * @param lhs Left active-lane multiplicands. + * @param rhs Right active-lane multiplicands. + * @param addend Active-lane addends. + * @return Same-shaped partial register containing active multiply-add results and inactive zeros. + * @remarks Fusion follows the API configuration. Available exactly when `IApi::MultiplyAdd` is satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + multiply_add(this PartialRegister lhs, PartialRegister rhs, PartialRegister addend) noexcept + requires IApi::MultiplyAdd + { + return PartialRegister{api_type::multiply_add(lhs.native, rhs.native, addend.native)}; + } + + /** + * @brief Computes the Register-defined magnitude for each 128-bit group containing active lanes. + * @param value Active lanes contributing to each grouped magnitude; inactive lanes contribute zero. + * @return Same-shaped partial result with the API-defined active layout and inactive bitwise zeros. + * @pre Every active integer-group magnitude is representable in `element_type`. + * @remarks Available when `IApi::Magnitude` and `IApi::BitwiseAnd` are satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) magnitude(this PartialRegister value) noexcept + requires IApi::Magnitude && IApi::BitwiseAnd + { + return PartialRegister{normalize_native(api_type::magnitude(value.native))}; + } + + /** + * @brief Computes checked integer magnitudes and retains the overflow lane of every occupied 128-bit group. + * @tparam source_element_t Deferred source type used to constrain result availability. + * @param value Active lanes contributing to each grouped checked magnitude. + * @return A partial or complete result ending after the final occupied group's overflow lane. + * @remarks Available when `IApi::MagnitudeChecked` and `IApi::BitwiseAnd` are satisfied. + */ + template + requires std::same_as && IApi::MagnitudeChecked && IApi::BitwiseAnd + [[nodiscard]] partial_magnitude_checked_result_t SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + magnitude_checked(this PartialRegister value) noexcept + { + using result_t = partial_magnitude_checked_result_t; + return PartialRegister::template make_specialized_result(api_type::magnitude_checked(value.native)); + } + + /** + * @brief Normalizes active floating lanes by their active 128-bit-group magnitude and clears the suffix. + * @param value Floating-point source whose active group prefixes determine each magnitude. + * @return Active lanes divided by their group magnitude with an all-bits-zero inactive suffix. + * @remarks Inactive divisors are neutralized at the abstract operation boundary. Floating-environment status follows the + * selected compiler model, matching Register. Available when the listed `IApi` contracts are satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) normalize(this PartialRegister value) noexcept + requires IApi::Normalize && IApi::BitwiseAnd + { + return PartialRegister{normalize_native(api_type::normalize(value.native))}; + } + + /** + * @brief Returns the low logical prefix of intrinsic-ordered adjacent-pair sums. + * @param lhs Supplies the first intrinsic-ordered active results. + * @param rhs Supplies the remaining intrinsic-ordered active results. + * @return Same-shaped partial result with its inactive suffix cleared. + * @remarks Available when `IApi::HorizontalAdd` and `IApi::BitwiseAnd` are satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) horizontal_add(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::HorizontalAdd && IApi::BitwiseAnd + { + return PartialRegister{normalize_native(api_type::add_horizontal(lhs.native, rhs.native))}; + } + + /** + * @brief Returns the low logical prefix of intrinsic-ordered adjacent-pair differences. + * @param lhs Supplies the first intrinsic-ordered active results. + * @param rhs Supplies the remaining intrinsic-ordered active results. + * @return Same-shaped partial result with its inactive suffix cleared. + * @remarks Available when `IApi::HorizontalSubtract` and `IApi::BitwiseAnd` are satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + horizontal_subtract(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::HorizontalSubtract && IApi::BitwiseAnd + { + return PartialRegister{normalize_native(api_type::subtract_horizontal(lhs.native, rhs.native))}; + } + + /** + * @brief Multiplies adjacent active integral lane pairs, pairing an unmatched final lane with zero. + * @tparam source_element_t Deferred source type used to constrain result availability. + * @param lhs Left active-lane factors. + * @param rhs Right active-lane factors. + * @return Contiguous promoted partial or complete result with `ceil(lane_count / 2)` logical lanes. + * @remarks Available exactly when the listed source and `IApi::MultiplyAddAdjacent` constraints are satisfied. + */ + template + requires std::same_as && std::is_integral_v && IApi::MultiplyAddAdjacent + [[nodiscard]] partial_multiply_add_adjacent_result_t SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) + multiply_add_adjacent(this PartialRegister lhs, PartialRegister rhs) noexcept + { + using result_t = partial_multiply_add_adjacent_result_t; + return PartialRegister::template make_specialized_result(api_type::multiply_add_adjacent(lhs.native, rhs.native)); + } + + /** + * @brief Multiplies unsigned and signed active byte pairs, pairing an unmatched final byte with zero. + * @tparam source_element_t Deferred source type used to constrain result availability. + * @param lhs Unsigned active-byte multiplicands. + * @param rhs Signed active-byte multiplicands. + * @return Contiguous signed 16-bit partial or complete result with `ceil(active_byte_count / 2)` logical lanes. + * @remarks Available exactly when the listed source and `IApi::ByteMultiplyAdd` constraints are satisfied. + */ + template + requires std::same_as && std::is_integral_v && IApi::ByteMultiplyAdd + [[nodiscard]] partial_byte_multiply_add_result_t SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) + multiply_add_unsigned_signed_bytes(this PartialRegister lhs, PartialRegister rhs) noexcept + { + using result_t = partial_byte_multiply_add_result_t; + return PartialRegister::template make_specialized_result(api_type::multiply_add_unsigned_signed_bytes(lhs.native, rhs.native)); + } + + /** + * @brief Sums active byte-wise absolute differences into contiguous unsigned 64-bit groups. + * @tparam source_element_t Deferred source type used to constrain result availability. + * @param lhs Left active source bytes. + * @param rhs Right active source bytes. + * @return Contiguous unsigned 64-bit partial or complete result with `ceil(active_byte_count / 8)` logical lanes. + * @remarks Inactive source bytes contribute zero. Available exactly when the listed source and `IApi::Sad` constraints are satisfied. + */ + template + requires std::same_as && std::is_integral_v && IApi::Sad + [[nodiscard]] partial_sad_result_t SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) + sum_absolute_byte_differences(this PartialRegister lhs, PartialRegister rhs) noexcept + { + using result_t = partial_sad_result_t; + return PartialRegister::template make_specialized_result(api_type::sum_absolute_byte_differences(lhs.native, rhs.native)); + } + + /** + * @brief Computes immediate-controlled multi-SAD with inactive source bytes fixed at zero. + * @tparam imm8 Immediate selector in the API-defined range. + * @tparam source_element_t Deferred source type used to constrain result availability. + * @param lhs Left active source bytes. + * @param rhs Right active source bytes. + * @return Complete Register preserving the API's noncontiguous output layout. + * @remarks Available exactly when the listed source and `IApi::MultiSad` constraints are satisfied. + */ + template + requires(imm8 >= 0 && imm8 <= 255 && std::same_as && std::is_integral_v && + IApi::MultiSad) + [[nodiscard]] multi_sad_result_t SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) + multi_sum_absolute_byte_differences(this PartialRegister lhs, PartialRegister rhs) noexcept + { + return multi_sad_result_t{api_type::template multi_sum_absolute_byte_differences(lhs.native, rhs.native)}; + } + + /** + * @brief Returns the first active logical lane containing the minimum integral value. + * @param value Active integral lanes to search; the inactive suffix is excluded. + * @return First minimum index in `[0, lane_count)`. + * @remarks Available when `IApi::MinPosition` and `IApi::BitwiseOr` are satisfied. + */ + [[nodiscard]] constexpr std::size_t SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) min_position(this PartialRegister value) noexcept + requires IApi::MinPosition && IApi::BitwiseOr + { + return api_type::min_position(api_type::bitwise_or(value.native, api_type::construct(PartialRegister::inactive_min_position_identity))); + } + + /** + * @brief Returns the first active logical lane containing the maximum integral value. + * @param value Active integral lanes to search; the inactive suffix is excluded. + * @return First maximum index in `[0, lane_count)`. + * @remarks Available when `IApi::MaxPosition` and `IApi::BitwiseOr` are satisfied. + */ + [[nodiscard]] constexpr std::size_t SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) max_position(this PartialRegister value) noexcept + requires IApi::MaxPosition && IApi::BitwiseOr + { + return api_type::max_position(api_type::bitwise_or(value.native, api_type::construct(PartialRegister::inactive_max_position_identity))); + } + + /** + * @brief Adds corresponding active lanes with intrinsic saturation. + * @param lhs Left active-lane addends. + * @param rhs Right active-lane addends. + * @return Same-shaped saturated result with an inactive zero suffix. + * @remarks Available exactly when `IApi::AddSaturated` is satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) add_saturated(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::AddSaturated + { + return PartialRegister{api_type::add_saturated(lhs.native, rhs.native)}; + } + + /** + * @brief Subtracts corresponding active lanes with intrinsic saturation. + * @param lhs Active-lane minuends. + * @param rhs Active-lane subtrahends. + * @return Same-shaped saturated result with an inactive zero suffix. + * @remarks Available exactly when `IApi::SubtractSaturated` is satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + subtract_saturated(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::SubtractSaturated + { + return PartialRegister{api_type::subtract_saturated(lhs.native, rhs.native)}; + } + + /** + * @brief Returns the low logical prefix of intrinsic-ordered saturated adjacent-pair sums. + * @param lhs Supplies the first intrinsic-ordered active results. + * @param rhs Supplies the remaining intrinsic-ordered active results. + * @return Same-shaped saturated partial result with its inactive suffix cleared. + * @remarks Available when `IApi::HorizontalAddSaturated` and `IApi::BitwiseAnd` are satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + horizontal_add_saturated(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::HorizontalAddSaturated && IApi::BitwiseAnd + { + return PartialRegister{normalize_native(api_type::hadd_saturated(lhs.native, rhs.native))}; + } + + /** + * @brief Returns the low logical prefix of intrinsic-ordered saturated adjacent-pair differences. + * @param lhs Supplies the first intrinsic-ordered active results. + * @param rhs Supplies the remaining intrinsic-ordered active results. + * @return Same-shaped saturated partial result with its inactive suffix cleared. + * @remarks Available when `IApi::HorizontalSubtractSaturated` and `IApi::BitwiseAnd` are satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + horizontal_subtract_saturated(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::HorizontalSubtractSaturated && IApi::BitwiseAnd + { + return PartialRegister{normalize_native(api_type::hsubtract_saturated(lhs.native, rhs.native))}; + } + + /** + * @brief Alternates subtraction and addition across active floating lanes and clears the suffix. + * @param lhs Left active-lane inputs. + * @param rhs Right active-lane inputs. + * @return Same-shaped intrinsic-ordered result with inactive bitwise-zero lanes. + * @remarks Lane polarity repeats per 128-bit group. Available when the listed `IApi` contracts are satisfied. + */ + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) add_subtract(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::AddSubtract && IApi::BitwiseAnd + { + return PartialRegister{normalize_native(api_type::add_subtract(lhs.native, rhs.native))}; + } + + /** + * @brief Computes an immediate-controlled dot product and clears every inactive output lane. + * @tparam imm8 API-defined source and destination selection control. + * @param lhs Left active-lane factors. + * @param rhs Right active-lane factors. + * @return Same-shaped partial result when every selected destination lane is active. + * @remarks Inactive input lanes contribute zero. Available only when the listed API and active-output constraints are satisfied. + */ + template + requires IApi::DotProduct && (PartialRegister::template dot_product_outputs_are_active()) + [[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) dot_product(this PartialRegister lhs, PartialRegister rhs) noexcept + { + return PartialRegister{api_type::template dot_product(lhs.native, rhs.native)}; + } + +#pragma endregion + +#pragma region Bitwise Operations + + /** + * @brief Computes the bitwise intersection of corresponding active lanes. + * @param lhs Left active bit pattern. + * @param rhs Right active bit pattern. + * @return Same-shaped partial register containing `lhs & rhs` and an inactive zero suffix. + * @remarks Available exactly when `IApi::BitwiseAnd` is satisfied. + */ + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator&(this PartialRegister lhs, + PartialRegister rhs) noexcept + requires IApi::BitwiseAnd + { + return PartialRegister{api_type::bitwise_and(lhs.native, rhs.native)}; + } + + /** + * @brief Computes the bitwise union of corresponding active lanes. + * @param lhs Left active bit pattern. + * @param rhs Right active bit pattern. + * @return Same-shaped partial register containing `lhs | rhs` and an inactive zero suffix. + * @remarks Available exactly when `IApi::BitwiseOr` is satisfied. + */ + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator|(this PartialRegister lhs, + PartialRegister rhs) noexcept + requires IApi::BitwiseOr + { + return PartialRegister{api_type::bitwise_or(lhs.native, rhs.native)}; + } + + /** + * @brief Computes the bitwise exclusive union of corresponding active lanes. + * @param lhs Left active bit pattern. + * @param rhs Right active bit pattern. + * @return Same-shaped partial register containing `lhs ^ rhs` and an inactive zero suffix. + * @remarks Available exactly when `IApi::BitwiseXor` is satisfied. + */ + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator^(this PartialRegister lhs, + PartialRegister rhs) noexcept + requires IApi::BitwiseXor + { + return PartialRegister{api_type::bitwise_xor(lhs.native, rhs.native)}; + } + + /** + * @brief Complements every active bit and clears every inactive bit. + * @param value Active bit pattern to complement. + * @return Same-shaped complemented partial register with an inactive zero suffix. + * @remarks Available when `IApi::BitwiseNot` and `IApi::BitwiseAnd` are satisfied. + */ + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator~(this PartialRegister value) noexcept + requires IApi::BitwiseNot && IApi::BitwiseAnd + { + return PartialRegister{normalize_native(api_type::bitwise_not(value.native))}; + } + + /** + * @brief Computes `(~lhs) & rhs` for corresponding active bits. + * @param lhs Active bit pattern complemented before intersection. + * @param rhs Active bit pattern intersected with the complemented left operand. + * @return Same-shaped partial register containing `(~lhs) & rhs` and inactive zeros. + * @remarks Available exactly when `IApi::BitwiseAndNot` is satisfied. + */ + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) andnot(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::BitwiseAndNot + { + return PartialRegister{api_type::bitwise_andnot(lhs.native, rhs.native)}; + } + + /** + * @brief Returns the API's native-granularity sign-bit mask for the active payload. + * @param value Partial register whose active sign bits are observed. + * @return Native-granularity scalar mask with no bits sourced from inactive bytes. + * @remarks Available exactly when `IApi::Movemask` is satisfied. + */ + [[nodiscard]] constexpr typename api_type::mask_t SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) movemask(this PartialRegister value) noexcept + requires IApi::Movemask + { + return api_type::movemask(value.native); + } + + /** + * @brief Returns one sign bit for every active logical lane. + * @param value Partial register whose active lane sign bits are observed. + * @return Compact scalar mask whose unused high bits are zero. + * @remarks Available exactly when `IApi::MovemaskSlim` is satisfied. + */ + [[nodiscard]] constexpr typename api_type::mask_t SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) lane_sign_bits(this PartialRegister value) noexcept + requires IApi::MovemaskSlim + { + return api_type::movemask_slim(value.native); + } + +#pragma endregion + +#pragma region Shifting Operations + + /** + * @brief Left-shifts every active integral lane with zero fill. + * @param value Active integral lanes to shift. + * @param count Runtime shift count applied to each active lane. + * @return Same-shaped shifted result with an inactive zero suffix. + * @pre `count >= 0`; counts at least the lane width produce zero lanes. + * @remarks Available exactly when `IApi::ShiftLeft` is satisfied. + */ + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator<<(this PartialRegister value, int count) noexcept + requires IApi::ShiftLeft + { + return PartialRegister{api_type::shift_left(value.native, count)}; + } + + /** + * @brief Right-shifts every active integral lane with zero fill. + * @param value Active integral lanes to shift. + * @param count Runtime shift count applied to each active lane. + * @return Same-shaped shifted result with an inactive zero suffix. + * @pre `count >= 0`; counts at least the lane width produce zero lanes. + * @remarks Available exactly when `IApi::ShiftRight` is satisfied. + */ + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + logical_shift_right(this PartialRegister value, int count) noexcept + requires IApi::ShiftRight + { + return PartialRegister{api_type::shift_right(value.native, count)}; + } + + /** + * @brief Right-shifts unsigned active lanes logically and signed active lanes arithmetically. + * @param value Active integral lanes to shift. + * @param count Runtime shift count applied to each active lane. + * @return Same-shaped signedness-selected result with an inactive zero suffix. + * @pre `count >= 0`; oversized signed counts clamp and unsigned counts produce zero lanes. + */ + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator>>(this PartialRegister value, int count) noexcept + requires((std::is_signed_v && IApi::ArithmeticShiftRight) || (std::is_unsigned_v && IApi::ShiftRight)) + { + if constexpr (std::is_signed_v) + return PartialRegister{api_type::shift_right_arithmetic(value.native, count)}; + else + return PartialRegister{api_type::shift_right(value.native, count)}; + } + + /** + * @brief Runtime-shifts the logical active byte payload toward higher indices. + * @param value Active byte payload to shift. + * @param count Runtime byte count; nonpositive values are identity and counts at least the active extent produce zero. + * @return Logical active-byte shift with discarded overflow and inactive zeros. + */ + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + shift_bytes_left_slow(this PartialRegister value, int count) noexcept + requires(register_width == 128 && IApi::ShiftBytesSlow && IApi::BitwiseAnd) + { + return PartialRegister{normalize_native(api_type::shift_bytes_left_slow(value.native, count))}; + } + + /** + * @brief Compile-time shifts the logical active byte payload toward higher indices. + * @tparam count Nonnegative byte count. + * @param value Active byte payload to shift. + * @return Logical active-byte shift with discarded overflow and inactive zeros. + */ + template + requires(count >= 0 && IApi::ShiftBytesLeft && IApi::BitwiseAnd) + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) shift_bytes_left(this PartialRegister value) noexcept + { + return PartialRegister{normalize_native(api_type::template shift_bytes_left(value.native))}; + } + + /** + * @brief Runtime-shifts the logical active byte payload toward lower indices. + * @param value Active byte payload to shift. + * @param count Runtime byte count; nonpositive values are identity and counts at least the active extent produce zero. + * @return Logical active-byte shift with zero-filled high logical bytes. + */ + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + shift_bytes_right_slow(this PartialRegister value, int count) noexcept + requires(register_width == 128 && IApi::ShiftBytesSlow) + { + return PartialRegister{api_type::shift_bytes_right_slow(value.native, count)}; + } + + /** + * @brief Compile-time shifts the logical active byte payload toward lower indices. + * @tparam count Nonnegative byte count. + * @param value Active byte payload to shift. + * @return Logical active-byte shift with zero-filled high logical bytes. + */ + template + requires(count >= 0 && IApi::ShiftBytesRight) + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) shift_bytes_right(this PartialRegister value) noexcept + { + return PartialRegister{api_type::template shift_bytes_right(value.native)}; + } + + /** + * @brief Runtime-shifts the logical active bit payload toward higher indices. + * @param value Active bit payload to shift. + * @param count Runtime bit count; nonpositive values are identity and counts at least the active extent produce zero. + * @return Logical active-bit shift with discarded overflow and inactive zeros. + */ + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + shift_bits_left_slow(this PartialRegister value, int count) noexcept + requires(register_width == 128 && IApi::ShiftBitsSlow && IApi::BitwiseAnd) + { + return PartialRegister{normalize_native(api_type::shift_bits_left_slow(value.native, count))}; + } + + /** + * @brief Runtime-shifts the logical active bit payload toward lower indices. + * @param value Active bit payload to shift. + * @param count Runtime bit count; nonpositive values are identity and counts at least the active extent produce zero. + * @return Logical active-bit shift with zero-filled high logical bits. + */ + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + shift_bits_right_slow(this PartialRegister value, int count) noexcept + requires(register_width == 128 && IApi::ShiftBitsSlow) + { + return PartialRegister{api_type::shift_bits_right_slow(value.native, count)}; + } + + /** + * @brief Compile-time shifts the logical active bit payload toward higher indices. + * @tparam count Nonnegative bit count. + * @param value Active bit payload to shift. + * @return Logical active-bit shift with discarded overflow and inactive zeros. + */ + template + requires(register_width == 128 && count >= 0 && IApi::ShiftBits && IApi::BitwiseAnd) + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) shift_bits_left(this PartialRegister value) noexcept + { + return PartialRegister{normalize_native(api_type::template shift_bits_left(value.native))}; + } + + /** + * @brief Compile-time shifts the logical active bit payload toward lower indices. + * @tparam count Nonnegative bit count. + * @param value Active bit payload to shift. + * @return Logical active-bit shift with zero-filled high logical bits. + */ + template + requires(register_width == 128 && count >= 0 && IApi::ShiftBits) + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) shift_bits_right(this PartialRegister value) noexcept + { + return PartialRegister{api_type::template shift_bits_right(value.native)}; + } + +#pragma endregion + +#pragma region Rearrangement and Conversion Operations + + /** + * @brief Extracts the meaningful low 128-bit half of a 256-bit partial register. + * @param value Source value in logical low-to-high lane order. + * @return Complete 128-bit Register containing the meaningful low half. + * @remarks Valid 256-bit PartialRegister geometries always reach the upper half, so the retained low half is complete. + */ + [[nodiscard]] constexpr auto SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) lower_half(this PartialRegister value) noexcept + requires(register_width == 256 && IApi::LowerHalf) + { + using result_t = partial_lower_half_result_t; + return result_t{api_type::lower_half(value.native)}; + } + + /** + * @brief Interleaves each native 128-bit group's low source lanes and retains the low logical result prefix. + * @param lhs Supplies even-numbered lanes in each intrinsic result group. + * @param rhs Supplies odd-numbered lanes in each intrinsic result group. + * @return Same-shaped partial register containing the first `lane_count` intrinsic result lanes and an inactive zero suffix. + * @remarks Missing source lanes are the operands' invariant-preserving zeros. + */ + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + unpack_low(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::UnpackLow && IApi::BitwiseAnd + { + return PartialRegister{normalize_native(api_type::unpack_lo(lhs.native, rhs.native))}; + } + + /** + * @brief Interleaves each native 128-bit group's high source lanes and retains the low logical result prefix. + * @param lhs Supplies even-numbered lanes in each intrinsic result group. + * @param rhs Supplies odd-numbered lanes in each intrinsic result group. + * @return Same-shaped partial register containing the first `lane_count` intrinsic result lanes and an inactive zero suffix. + * @remarks Missing source lanes are the operands' invariant-preserving zeros. + */ + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + unpack_high(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::UnpackHigh + { + return PartialRegister{api_type::unpack_hi(lhs.native, rhs.native)}; + } + + /** + * @brief Rearranges the logical active prefix with one compile-time source selector per result lane. + * @tparam indices Exactly `lane_count` source indices, each strictly less than `lane_count`. + * @param value Source partial register. + * @return Same-shaped partial register containing the selected active lanes and a zero inactive suffix. + * @remarks Padding cannot be requested explicitly; every logical result lane selects an active source lane. + */ + template + requires(sizeof...(indices) == lane_count && ((indices < lane_count) && ...) && + partial_shuffle_available(std::make_index_sequence{})) + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) shuffle(this PartialRegister value) noexcept + { + return PartialRegister{ + partial_shuffle_native(value.native, std::make_index_sequence{})}; + } + + /** + * @brief Rearranges the logical active byte prefix with one compile-time source selector per result byte. + * @tparam indices Exactly `active_byte_count` source-byte indices, each strictly less than `active_byte_count`. + * @param value Source partial register. + * @return Same-shaped partial register containing the selected active bytes and a zero inactive suffix. + * @remarks Padding cannot be requested explicitly; every logical result byte selects an active source byte. + */ + template + requires(sizeof...(indices) == active_byte_count && ((indices < active_byte_count) && ...) && IApi::BitCast && + IApi::BitCast, element_type> && + partial_shuffle_available, active_byte_count, indices...>( + std::make_index_sequence{})) + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) shuffle_bytes(this PartialRegister value) noexcept + { + using byte_api_type = Api; + const auto bytes = api_type::template bit_cast(value.native); + const auto shuffled = + partial_shuffle_native(bytes, std::make_index_sequence{}); + return PartialRegister{byte_api_type::template bit_cast(shuffled)}; + } + + /** + * @brief Applies Register's immediate-controlled low four-lane shuffle in each native 128-bit group. + * @tparam imm8 Immediate selector in the inclusive range `0..255`. + * @param value Source 16-bit partial register. + * @return Same-shaped projected intrinsic result with an inactive zero suffix. + * @remarks An active output may select an inactive source lane, in which case that logical output is zero. + */ + template + requires IApi::ShuffleLow && IApi::BitwiseAnd + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) shuffle_low(this PartialRegister value) noexcept + { + return PartialRegister{normalize_native(api_type::template shuffle_lo(value.native))}; + } + + /** + * @brief Applies Register's immediate-controlled high four-lane shuffle in each native 128-bit group. + * @tparam imm8 Immediate selector in the inclusive range `0..255`. + * @param value Source 16-bit partial register. + * @return Same-shaped projected intrinsic result with an inactive zero suffix. + * @remarks An active output may select an inactive source lane, in which case that logical output is zero. + */ + template + requires IApi::ShuffleHigh && IApi::BitwiseAnd + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) shuffle_high(this PartialRegister value) noexcept + { + return PartialRegister{normalize_native(api_type::template shuffle_hi(value.native))}; + } + + /** + * @brief Selects corresponding active lanes from two partial registers with Register's immediate-mask semantics. + * @tparam imm8 Immediate control in the inclusive range `0..255`; set applicable bits select `rhs`. + * @param lhs Source selected by cleared applicable control bits. + * @param rhs Source selected by set applicable control bits. + * @return Same-shaped blended result whose inactive suffix remains zero. + * @remarks A 256-bit 16-bit blend repeats the eight control bits in each 128-bit group. + */ + template + requires IApi::Blend + [[nodiscard]] constexpr PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) blend(this PartialRegister lhs, PartialRegister rhs) noexcept + { + return PartialRegister{api_type::template blend(lhs.native, rhs.native)}; + } + + /** + * @brief Reinterprets the active source bits as complete target lanes at the same native width. + * @tparam target_t Destination lane type whose size exactly divides `active_byte_count`. + * @param value Source partial register. + * @return Partial or complete result exposing exactly `active_byte_count / sizeof(target_t)` meaningful target lanes. + * @remarks Target types requiring a fractional logical lane are unavailable; no padding rule is applied. + */ + template + requires RegisterAvailable && (active_byte_count % sizeof(target_t) == 0) && IApi::BitCast + [[nodiscard]] constexpr auto SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) bit_cast(this PartialRegister value) noexcept + { + using result_t = partial_bit_cast_result_t; + return result_t{api_type::template bit_cast(value.native)}; + } + + /** + * @brief Numerically converts each active source lane into one corresponding target lane. + * @tparam target_t Explicit numeric destination lane type supported by the source API. + * @param value Source partial register. + * @return Partial or complete result exposing exactly `lane_count` converted target lanes. + * @remarks Inactive positive-zero inputs convert to positive-zero target lanes and remain unobservable. + */ + template + requires RegisterAvailable && (lane_count <= Api::element_count) && + (lane_count == Api::element_count || PartialRegisterAvailable) && + IApi::Convert + [[nodiscard]] constexpr auto SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) convert(this PartialRegister value) noexcept + { + using result_t = partial_convert_result_t; + return result_t{api_type::template convert(value.native)}; + } + + /** + * @brief Widens the lowest active source lanes that fit in one target register. + * @tparam target_t Wider integral destination lane type supported by the source API. + * @tparam target_bits Destination native width, either 128 or 256 bits. + * @param value Source 128-bit partial register. + * @return Partial or complete target exposing exactly the number of consumed active source lanes. + * @remarks A target cell is unavailable when its meaningful prefix would leave an entirely inactive upper 128-bit half. + */ + template + requires RegisterAvailable && IApi::Widen> && + (partial_widen_low_lane_count == Api::element_count || + PartialRegisterAvailable>) + [[nodiscard]] constexpr auto SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) widen_low(this PartialRegister value) noexcept + { + using result_t = partial_widen_low_result_t; + return result_t{api_type::template widen>(value.native)}; + } + +#pragma endregion + +#pragma region Comparison Operations + + /** @brief Compares active lanes for ordered equality and clears inactive predicate lanes. */ + [[nodiscard]] constexpr mask_type SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) compare_equal(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::CompareEqual && IApi::BitwiseAnd + { + return mask_type::from_native(api_type::compare_equal(lhs.native, rhs.native)); + } + + /** @brief Compares active lanes for ordered greater-than and clears inactive predicate lanes. */ + [[nodiscard]] constexpr mask_type SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) compare_greater(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::CompareGreater && IApi::BitwiseAnd + { + return mask_type::from_native(api_type::compare_greater(lhs.native, rhs.native)); + } + + /** @brief Compares active lanes for ordered greater-than-or-equal and clears inactive predicate lanes. */ + [[nodiscard]] constexpr mask_type SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) + compare_greater_equal(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::CompareGreaterEqual && IApi::BitwiseAnd + { + return mask_type::from_native(api_type::compare_greater_equal(lhs.native, rhs.native)); + } + + /** @brief Compares active lanes for ordered less-than and clears inactive predicate lanes. */ + [[nodiscard]] constexpr mask_type SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) compare_less(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::CompareLess && IApi::BitwiseAnd + { + return mask_type::from_native(api_type::compare_less(lhs.native, rhs.native)); + } + + /** @brief Compares active lanes for ordered less-than-or-equal and clears inactive predicate lanes. */ + [[nodiscard]] constexpr mask_type SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) + compare_less_equal(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::CompareLessEqual && IApi::BitwiseAnd + { + return mask_type::from_native(api_type::compare_less_equal(lhs.native, rhs.native)); + } + + /** + * @brief Tests whether every corresponding active lane compares equal. + * @return True only when all active lanes compare equal; inactive lanes are ignored. + * @remarks Floating NaNs compare unequal and signed zeros compare equal. + */ + [[nodiscard]] constexpr bool SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) operator==(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::CompareEqual && IApi::BitwiseAnd && IApi::MovemaskSlim + { + return lhs.compare_equal(rhs).all(); + } + + /** + * @brief Tests whether at least one corresponding active lane fails ordered equality. + * @return Logical negation of active-prefix equality. + */ + [[nodiscard]] constexpr bool SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) operator!=(this PartialRegister lhs, PartialRegister rhs) noexcept + requires IApi::CompareEqual && IApi::BitwiseAnd && IApi::MovemaskSlim + { + return !lhs.compare_equal(rhs).all(); + } + +#pragma endregion +}; + +} // namespace SimdLib + +#include diff --git a/include/SimdLib/PartialRegisterFwd.h b/include/SimdLib/PartialRegisterFwd.h new file mode 100644 index 0000000..c18644e --- /dev/null +++ b/include/SimdLib/PartialRegisterFwd.h @@ -0,0 +1,182 @@ +#pragma once + +#include + +#include +#include +#include + +namespace SimdLib +{ + +/** + * @brief Reports whether an element type, width, and active-lane count form a useful partial-register geometry. + * @tparam element_t Scalar interpretation of every lane. + * @tparam bits Physical native-register width in bits. + * @tparam active_lane_count Number of logical low lanes retained by the value. + * @remarks A 256-bit partial register must occupy at least one lane in its upper 128-bit group. + */ +template +concept PartialRegisterAvailable = RegisterAvailable && (active_lane_count > 0) && (active_lane_count < Api::element_count) && + ((bits != 256) || (active_lane_count * sizeof(element_t) * 8 > 128)); + +/** @brief Boolean form of PartialRegisterAvailable for metaprogramming contexts. */ +template +constexpr inline bool is_partial_register_available_v = PartialRegisterAvailable; + +/** + * @brief Owns one SIMD register with a compile-time low prefix of active lanes. + * @tparam element_t Scalar interpretation of every lane. + * @tparam bits Physical native-register width in bits. + * @tparam active_lane_count Number of logical low lanes retained by the value. + * @remarks Inactive high lanes always contain the all-bits-zero representation. + */ +template + requires PartialRegisterAvailable +class PartialRegister; + +/** + * @brief Declares the predicate companion for a PartialRegister geometry. + * @tparam element_t Scalar geometry represented by every predicate lane. + * @tparam bits Physical native predicate-register width in bits. + * @tparam active_lane_count Number of logical low predicate lanes. + */ +template + requires PartialRegisterAvailable +class PartialRegisterMask; + +/** + * @brief Selects the widest available PartialRegister width for an element type. + * @tparam element_t Scalar interpretation of every lane. + * @tparam active_lane_count Number of logical low lanes retained by the value. + * @remarks Selects 256 bits only when that width is available and the active payload reaches its upper 128-bit group. + */ +template + requires PartialRegisterAvailable || PartialRegisterAvailable +using NativePartialRegister = PartialRegister ? 256 : 128), active_lane_count>; + +namespace Detail +{ +/** @brief Selects a partial or complete result without forming a constraint-invalid unselected specialization. */ +template struct PartialOrCompleteRegisterSelector; + +/** @brief Selects a complete Register when every target lane is meaningful. */ +template struct PartialOrCompleteRegisterSelector +{ + using type = Register; +}; + +/** @brief Selects a PartialRegister when the target retains an inactive suffix. */ +template struct PartialOrCompleteRegisterSelector +{ + using type = PartialRegister; +}; +} // namespace Detail + +/** + * @brief Selects a partial result unless it fills the target or cannot form a useful partial geometry. + * @tparam element_t Scalar interpretation of the result lanes. + * @tparam bits Physical result-register width in bits. + * @tparam result_lane_count Number of meaningful low result lanes. + */ +template + requires RegisterAvailable && (result_lane_count > 0) && (result_lane_count <= Api::element_count) +using partial_or_complete_register_t = + typename Detail::PartialOrCompleteRegisterSelector::element_count || + !PartialRegisterAvailable>::type; + +/** @brief Scalar lane type produced by adjacent integer multiply-add. */ +template + requires std::is_integral_v +using partial_multiply_add_adjacent_element_t = std::conditional_t< + (sizeof(element_t) >= sizeof(std::int64_t)), element_t, + std::conditional_t, + std::conditional_t>, + std::conditional_t>>>; + +/** @brief Result type produced by adjacent integer multiply-add over a partial logical prefix. */ +template + requires PartialRegisterAvailable && std::is_integral_v && IApi::MultiplyAddAdjacent> +using partial_multiply_add_adjacent_result_t = + partial_or_complete_register_t, bits, (active_lane_count + 1) / 2>; + +/** @brief Result type produced by unsigned/signed byte multiply-add over active source bytes. */ +template + requires PartialRegisterAvailable && std::is_integral_v && IApi::ByteMultiplyAdd> +using partial_byte_multiply_add_result_t = partial_or_complete_register_t; + +/** @brief Result type produced by byte absolute-difference sums over active source bytes. */ +template + requires PartialRegisterAvailable && std::is_integral_v && IApi::Sad> +using partial_sad_result_t = partial_or_complete_register_t; + +/** + * @brief Result type produced by checked magnitude while retaining every occupied group's overflow lane. + * @tparam element_t Scalar interpretation of the source and result lanes. + * @tparam bits Physical source and result register width in bits. + * @tparam active_lane_count Number of meaningful low source lanes. + */ +template + requires PartialRegisterAvailable && std::is_integral_v && IApi::MagnitudeChecked> +constexpr inline std::size_t partial_magnitude_checked_lane_count = []() constexpr +{ + constexpr std::size_t group_lane_count = 128 / (sizeof(element_t) * 8); + return ((active_lane_count - 1) / group_lane_count) * group_lane_count + 2; +}(); + +/** @brief Checked-magnitude result retaining each occupied group's magnitude and overflow lanes. */ +template + requires PartialRegisterAvailable && std::is_integral_v && IApi::MagnitudeChecked> +using partial_magnitude_checked_result_t = + partial_or_complete_register_t>; + +/** @brief Number of meaningful lanes retained when extracting the low 128-bit half of a partial register. */ +template + requires PartialRegisterAvailable && IApi::LowerHalf> +constexpr inline std::size_t partial_lower_half_lane_count = + active_lane_count < Api<128, element_t>::element_count ? active_lane_count : Api<128, element_t>::element_count; + +/** @brief Partial or complete low-half result selected from the meaningful source-lane count. */ +template + requires PartialRegisterAvailable && IApi::LowerHalf> +using partial_lower_half_result_t = partial_or_complete_register_t>; + +/** @brief Number of complete target lanes represented by the active source-byte extent. */ +template + requires PartialRegisterAvailable && RegisterAvailable && + ((active_lane_count * sizeof(source_t)) % sizeof(target_t) == 0) && IApi::BitCast, target_t> +constexpr inline std::size_t partial_bit_cast_lane_count = active_lane_count * sizeof(source_t) / sizeof(target_t); + +/** @brief Bit-cast result whose logical lane count exactly covers the active source bits. */ +template + requires PartialRegisterAvailable && RegisterAvailable && + ((active_lane_count * sizeof(source_t)) % sizeof(target_t) == 0) && IApi::BitCast, target_t> +using partial_bit_cast_result_t = partial_or_complete_register_t>; + +/** @brief Numeric-conversion result retaining one meaningful target lane per active source lane. */ +template + requires PartialRegisterAvailable && RegisterAvailable && + (active_lane_count <= Api::element_count) && + (active_lane_count == Api::element_count || PartialRegisterAvailable) && + IApi::Convert, target_t> +using partial_convert_result_t = partial_or_complete_register_t; + +/** @brief Number of low active source lanes consumed by one widening operation. */ +template + requires PartialRegisterAvailable && RegisterAvailable && + IApi::Widen, Api> +constexpr inline std::size_t partial_widen_low_lane_count = + active_lane_count < Api::element_count ? active_lane_count : Api::element_count; + +/** @brief Widening result whose type exposes exactly the consumed low source lanes. */ +template + requires PartialRegisterAvailable && RegisterAvailable && + IApi::Widen, Api> && + (partial_widen_low_lane_count == Api::element_count || + PartialRegisterAvailable>) +using partial_widen_low_result_t = + partial_or_complete_register_t>; + +} // namespace SimdLib diff --git a/include/SimdLib/PartialRegisterMask.h b/include/SimdLib/PartialRegisterMask.h new file mode 100644 index 0000000..205eb3c --- /dev/null +++ b/include/SimdLib/PartialRegisterMask.h @@ -0,0 +1,220 @@ +#pragma once + +#include + +#if !SIMDLIB_REGISTER_INTERFACE_AVAILABLE && !SIMDLIB_REQUIRE_REGISTER_INTERFACE +#error "SIMDLIB_PARTIAL_REGISTER_MASK_HEADER_REQUIRES_CXX23: requires C++23 explicit object parameter support" +#endif + +#include +#include + +#include +#include +#include +#include +#include + +namespace SimdLib +{ + +/** + * @brief Owns one canonical predicate register for a PartialRegister geometry. + * @tparam element_t Scalar geometry associated with each predicate lane. + * @tparam register_bits Width of the associated native register in bits. + * @tparam active_lane_count Number of active low predicate lanes. + * @invariant Active lanes are canonical all-zero or all-one predicates; inactive lanes are all-bits zero. + */ +template + requires PartialRegisterAvailable +class PartialRegisterMask final +{ + public: + using element_type = element_t; + using api_type = Api; + using native_type = typename api_type::vector_t; + using register_type = PartialRegister; + using bits_type = std::conditional_t<(api_type::element_count <= 32), std::uint32_t, std::uint64_t>; + + constexpr static inline std::size_t register_width = register_bits; + constexpr static inline std::size_t byte_count = api_type::byte_count; + constexpr static inline std::size_t native_lane_count = api_type::element_count; + constexpr static inline std::size_t lane_count = active_lane_count; + constexpr static inline std::size_t inactive_lane_count = native_lane_count - lane_count; + + /** + * @brief Owns the partial native predicate represented by this aggregate. + * @pre Direct aggregate initialization must provide canonical active predicates and a bitwise-zero inactive suffix. + */ + native_type native = api_type::setzero(); + + /** + * @brief Imports canonical active predicate lanes and clears every inactive lane. + * @param native Native predicate whose active lanes are all-zero or all-one. + * @return A predicate with a bitwise-zero inactive suffix. + * @pre Active lanes are canonical Boolean predicate representations. + */ + [[nodiscard]] constexpr static PartialRegisterMask SIMD_FLAGS(Out, RegisterOnly, ForceInline, Flatten) from_native(native_type native) noexcept + requires IApi::BitwiseAnd + { + return PartialRegisterMask{normalize_native(validate_import_native(native))}; + } + + /** @brief Exports the native predicate by value with a bitwise-zero inactive suffix. */ + [[nodiscard]] constexpr native_type SIMD_FLAGS(In, ForceInline, Flatten) to_native(this PartialRegisterMask value) noexcept + { + return validate_native(value.native); + } + + /** @brief Tests whether any active predicate lane is true. */ + [[nodiscard]] constexpr bool SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) any(this PartialRegisterMask value) noexcept + requires IApi::MovemaskSlim + { + return value.bits() != 0; + } + + /** @brief Tests whether every active predicate lane is true, ignoring false inactive lanes. */ + [[nodiscard]] constexpr bool SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) all(this PartialRegisterMask value) noexcept + requires IApi::MovemaskSlim + { + return value.bits() == active_bits; + } + + /** @brief Tests whether every active predicate lane is false. */ + [[nodiscard]] constexpr bool SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) none(this PartialRegisterMask value) noexcept + requires IApi::MovemaskSlim + { + return value.bits() == 0; + } + + /** @brief Returns one compact Boolean bit for each active predicate lane. */ + [[nodiscard]] constexpr bits_type SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) bits(this PartialRegisterMask value) noexcept + requires IApi::MovemaskSlim + { + return static_cast(api_type::movemask_slim(value.to_native())) & active_bits; + } + + /** + * @brief Selects between matching PartialRegister operands for every active predicate lane. + * @param condition Canonical predicate selecting the source of each active lane. + * @param when_true Value supplying true-selected active lanes. + * @param when_false Value supplying false-selected active lanes. + * @return A PartialRegister with selected active lanes and a bitwise-zero inactive suffix. + */ + [[nodiscard]] constexpr register_type SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + select(this PartialRegisterMask condition, register_type when_true, register_type when_false) noexcept + requires IApi::Select + { + return register_type{api_type::select(condition.to_native(), when_true.to_native(), when_false.to_native())}; + } + + /** @brief Computes the intersection of two partial predicates. */ + [[nodiscard]] constexpr PartialRegisterMask SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator&(this PartialRegisterMask lhs, + PartialRegisterMask rhs) noexcept + requires IApi::BitwiseAnd + { + return PartialRegisterMask{api_type::bitwise_and(lhs.to_native(), rhs.to_native())}; + } + + /** @brief Computes the union of two partial predicates. */ + [[nodiscard]] constexpr PartialRegisterMask SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator|(this PartialRegisterMask lhs, + PartialRegisterMask rhs) noexcept + requires IApi::BitwiseOr + { + return PartialRegisterMask{api_type::bitwise_or(lhs.to_native(), rhs.to_native())}; + } + + /** @brief Computes the exclusive union of two partial predicates. */ + [[nodiscard]] constexpr PartialRegisterMask SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator^(this PartialRegisterMask lhs, + PartialRegisterMask rhs) noexcept + requires IApi::BitwiseXor + { + return PartialRegisterMask{api_type::bitwise_xor(lhs.to_native(), rhs.to_native())}; + } + + /** @brief Inverts active predicate lanes and explicitly clears the inactive predicate suffix. */ + [[nodiscard]] constexpr PartialRegisterMask SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator~(this PartialRegisterMask value) noexcept + requires IApi::BitwiseNot && IApi::BitwiseAnd + { + return PartialRegisterMask{normalize_native(api_type::bitwise_not(value.to_native()))}; + } + + private: + /** @brief Compact mask with one set bit for each active predicate lane. */ + constexpr static inline bits_type active_bits = (bits_type{1} << lane_count) - 1; + + /** @brief Compile-time all-bits-one active prefix and all-bits-zero inactive suffix. */ + constexpr static inline std::array active_lane_filter = []() constexpr + { + std::array result{}; + std::array one_bytes{}; + for (auto &byte : one_bytes) + byte = std::byte{0xff}; + const auto one = std::bit_cast(one_bytes); + for (std::size_t lane = 0; lane < lane_count; ++lane) + result[lane] = one; + return result; + }(); + + /** @brief Clears inactive predicate lanes from a native value while retaining the active prefix. */ + [[nodiscard]] constexpr static native_type SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) normalize_native(native_type native) noexcept + requires IApi::BitwiseAnd + { + if consteval + { + auto lanes = api_type::to_array(native); + for (std::size_t lane = lane_count; lane < native_lane_count; ++lane) + lanes[lane] = element_type{}; + return api_type::construct(lanes); + } + return api_type::bitwise_and(native, api_type::construct(active_lane_filter)); + } + + /** @brief Validates canonical active predicates at the native import boundary when checks are enabled. */ + [[nodiscard]] constexpr static native_type validate_import_native(native_type native) noexcept + { +#if SIMDLIB_ENABLE_CHECKS + validate_predicate_bytes(native, false); +#endif + return native; + } + + /** @brief Validates canonical active predicates and a bitwise-zero inactive suffix when checks are enabled. */ + [[nodiscard]] constexpr static native_type SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) validate_native(native_type native) noexcept + { +#if SIMDLIB_ENABLE_CHECKS + validate_predicate_bytes(native, true); +#endif + return native; + } + + /** + * @brief Validates predicate representations through SIMD byte comparisons. + * @param native Native predicate to inspect. + * @param validate_inactive Whether the inactive suffix must also be bitwise zero. + */ + constexpr static void validate_predicate_bytes(native_type native, bool validate_inactive) noexcept + { + using byte_api_type = Api; + const auto bytes = api_type::template bit_cast(native); + const auto zero_bits = byte_api_type::movemask_slim(byte_api_type::compare_equal(bytes, byte_api_type::setzero())); + const auto one_bits = byte_api_type::movemask_slim(byte_api_type::compare_equal(bytes, byte_api_type::set1(0xffU))); + for (std::size_t lane = 0; lane < lane_count; ++lane) + { + bool is_zero = true; + bool is_one = true; + for (std::size_t byte = lane * sizeof(element_type); byte < (lane + 1) * sizeof(element_type); ++byte) + { + is_zero = is_zero && (zero_bits & (typename byte_api_type::mask_t{1} << byte)) != 0; + is_one = is_one && (one_bits & (typename byte_api_type::mask_t{1} << byte)) != 0; + } + SIMDLIB_PRECONDITION(is_zero || is_one, "PartialRegisterMask active lanes must be canonical predicates"); + } + if (validate_inactive) + for (std::size_t byte = lane_count * sizeof(element_type); byte < byte_count; ++byte) + SIMDLIB_PRECONDITION((zero_bits & (typename byte_api_type::mask_t{1} << byte)) != 0, + "PartialRegisterMask inactive lanes must have an all-bits-zero representation"); + } +}; + +} // namespace SimdLib diff --git a/include/SimdLib/SimdLib.h b/include/SimdLib/SimdLib.h index 7bf732b..198a490 100644 --- a/include/SimdLib/SimdLib.h +++ b/include/SimdLib/SimdLib.h @@ -3,10 +3,12 @@ #include #include #include +#include #include #include #if SIMDLIB_REGISTER_INTERFACE_AVAILABLE #include +#include #include #endif #include diff --git a/tests/ApiPartialTransfer.tests.cpp b/tests/ApiPartialTransfer.tests.cpp new file mode 100644 index 0000000..fe25146 --- /dev/null +++ b/tests/ApiPartialTransfer.tests.cpp @@ -0,0 +1,162 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef SIMDLIB_API_PARTIAL_TRANSFER_TEST_WIDTH +#error "SIMDLIB_API_PARTIAL_TRANSFER_TEST_WIDTH must select the tested register width" +#endif + +namespace +{ + +/** @brief Produces one deterministic exactly representable lane value. */ +template [[nodiscard]] constexpr element_t lane_value(std::size_t index) noexcept +{ + return static_cast(index + 11); +} + +/** @brief Reports whether one scalar has an all-bits-zero object representation. */ +template [[nodiscard]] constexpr bool has_zero_bits(element_t value) noexcept +{ + const auto bytes = std::bit_cast>(value); + return std::all_of(bytes.begin(), bytes.end(), [](std::byte byte) constexpr noexcept { return byte == std::byte{}; }); +} + +/** @brief Verifies every element-prefix load, store, and observation operation for one extent. */ +template [[nodiscard]] bool has_element_prefix_contract() +{ + using element_t = typename api_t::element_type; + std::array source{}; + for (std::size_t index = 0; index < source.size(); ++index) + source[index] = lane_value(index); + + std::array unaligned_source{}; + std::copy(source.begin(), source.end(), unaligned_source.begin() + 1); + const auto loaded = api_t::template load_partial(std::span{unaligned_source.data() + 1, active_count}); + const auto loaded_lanes = api_t::to_array(loaded); + for (std::size_t index = 0; index < active_count; ++index) + if (loaded_lanes[index] != source[index]) + return false; + for (std::size_t index = active_count; index < api_t::element_count; ++index) + if (!has_zero_bits(loaded_lanes[index])) + return false; + + alignas(api_t::byte_count) std::array aligned_source{}; + std::copy(source.begin(), source.end(), aligned_source.begin() + api_t::element_count); + const auto aligned_loaded = + api_t::template load_partial_aligned(std::span{aligned_source.data() + api_t::element_count, active_count}); + if (api_t::to_array(aligned_loaded) != loaded_lanes) + return false; + + const auto observed = api_t::template to_array_partial(loaded); + if (!std::equal(observed.begin(), observed.end(), source.begin())) + return false; + + const auto canary = lane_value(api_t::element_count + 19); + std::array destination{}; + destination.fill(canary); + api_t::template store_partial(loaded, std::span{destination.data() + 1, active_count}); + if (destination.front() != canary || destination.back() != canary || !std::equal(source.begin(), source.begin() + active_count, destination.begin() + 1)) + return false; + + alignas(api_t::byte_count) std::array aligned_destination{}; + aligned_destination.fill(canary); + api_t::template store_partial_aligned(loaded, std::span{aligned_destination.data() + api_t::element_count, active_count}); + return std::all_of(aligned_destination.begin(), aligned_destination.begin() + api_t::element_count, + [](element_t value) noexcept { return value == lane_value(api_t::element_count + 19); }) && + aligned_destination.back() == canary && + std::equal(source.begin(), source.begin() + active_count, aligned_destination.begin() + api_t::element_count); +} + +/** @brief Verifies partial scalar broadcast for one active-lane extent. */ +template [[nodiscard]] bool has_partial_broadcast_contract() +{ + using element_t = typename api_t::element_type; + const auto expected = lane_value(7); + const auto lanes = api_t::to_array(api_t::template broadcast_partial(expected)); + for (std::size_t index = 0; index < active_count; ++index) + if (lanes[index] != expected) + return false; + for (std::size_t index = active_count; index < api_t::element_count; ++index) + if (!has_zero_bits(lanes[index])) + return false; + return true; +} + +/** @brief Verifies every byte-prefix load and store operation for one extent. */ +template [[nodiscard]] bool has_byte_prefix_contract() +{ + std::array source{}; + for (std::size_t index = 0; index < source.size(); ++index) + source[index] = static_cast(index + 1); + std::array unaligned_source{}; + std::copy(source.begin(), source.end(), unaligned_source.begin() + 1); + const auto loaded = api_t::template load_bytes_partial(std::span{unaligned_source.data() + 1, active_byte_count}); + std::array loaded_bytes{}; + api_t::store(loaded, std::span{loaded_bytes}); + for (std::size_t index = 0; index < active_byte_count; ++index) + if (loaded_bytes[index] != source[index]) + return false; + for (std::size_t index = active_byte_count; index < api_t::byte_count; ++index) + if (loaded_bytes[index] != std::byte{}) + return false; + + constexpr std::byte canary{0xa5}; + std::array destination{}; + destination.fill(canary); + api_t::template store_bytes_partial(loaded, std::span{destination.data() + 1, active_byte_count}); + return destination.front() == canary && destination.back() == canary && + std::equal(source.begin(), source.begin() + active_byte_count, destination.begin() + 1); +} + +/** @brief Verifies every supported element prefix for one API specialization. */ +template [[nodiscard]] bool has_all_element_prefixes(std::index_sequence) +{ + return (has_element_prefix_contract() && ...); +} + +/** @brief Verifies partial scalar broadcast for every supported active-lane extent. */ +template [[nodiscard]] bool has_all_partial_broadcasts(std::index_sequence) +{ + return (has_partial_broadcast_contract() && ...); +} + +/** @brief Verifies every supported byte prefix for one API specialization. */ +template [[nodiscard]] bool has_all_byte_prefixes(std::index_sequence) +{ + return (has_byte_prefix_contract() && ...); +} + +/** @brief Verifies the complete partial-transfer API for one scalar interpretation. */ +template [[nodiscard]] bool has_partial_transfer_contract() +{ + using api_t = SimdLib::Api; + return has_all_element_prefixes(std::make_index_sequence{}) && + has_all_partial_broadcasts(std::make_index_sequence{}) && + has_all_byte_prefixes(std::make_index_sequence{}); +} + +} // namespace + +/** @brief Verifies every partial API method across all scalar interpretations and extents. */ +TEST_CASE("Api partial operations preserve exact prefixes, zero suffixes, and surrounding canaries", "[API][PARTIAL_TRANSFER]") +{ + REQUIRE(has_partial_transfer_contract()); + REQUIRE(has_partial_transfer_contract()); + REQUIRE(has_partial_transfer_contract()); + REQUIRE(has_partial_transfer_contract()); + REQUIRE(has_partial_transfer_contract()); + REQUIRE(has_partial_transfer_contract()); + REQUIRE(has_partial_transfer_contract()); + REQUIRE(has_partial_transfer_contract()); + REQUIRE(has_partial_transfer_contract()); + REQUIRE(has_partial_transfer_contract()); +} diff --git a/tests/ImplementationHalfTransfer.tests.cpp b/tests/ImplementationHalfTransfer.tests.cpp new file mode 100644 index 0000000..ca0406b --- /dev/null +++ b/tests/ImplementationHalfTransfer.tests.cpp @@ -0,0 +1,79 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include + +#ifndef SIMDLIB_IMPLEMENTATION_HALF_TRANSFER_TEST_WIDTH +#error "SIMDLIB_IMPLEMENTATION_HALF_TRANSFER_TEST_WIDTH must select the tested register width" +#endif + +namespace +{ + +/** @brief Reports whether one mapping exposes aligned half-register transfer operations. */ +template +concept HasAlignedHalfTransfer = requires(const element_t *source, typename mapping_t::int_vector_t value, void *destination) { + { mapping_t::load_half_aligned(source) } -> std::same_as; + mapping_t::store_half_aligned(value, destination); +}; + +/** @brief Reports whether one scalar value has an all-bits-zero object representation. */ +template [[nodiscard]] constexpr bool has_zero_bits(element_t value) noexcept +{ + const auto bytes = std::bit_cast>(value); + return std::all_of(bytes.begin(), bytes.end(), [](std::byte byte) constexpr noexcept { return byte == std::byte{}; }); +} + +/** @brief Verifies aligned half-register load and store behavior for one integral mapping. */ +template [[nodiscard]] bool has_aligned_half_transfer_contract() +{ + using mapping_t = SimdLib::Detail::SimdMappings; + static_assert(HasAlignedHalfTransfer); + constexpr std::size_t half_count = mapping_t::element_count / 2; + alignas(SIMDLIB_IMPLEMENTATION_HALF_TRANSFER_TEST_WIDTH / 8) std::array source{}; + for (std::size_t index = 0; index < source.size(); ++index) + source[index] = static_cast(index + 17); + + const auto loaded = mapping_t::load_half_aligned(source.data()); + std::array loaded_lanes{}; + mapping_t::store_unaligned(loaded, loaded_lanes.data()); + for (std::size_t index = 0; index < half_count; ++index) + if (loaded_lanes[index] != source[index]) + return false; + for (std::size_t index = half_count; index < loaded_lanes.size(); ++index) + if (!has_zero_bits(loaded_lanes[index])) + return false; + + const auto canary = static_cast(91); + alignas(SIMDLIB_IMPLEMENTATION_HALF_TRANSFER_TEST_WIDTH / 8) std::array destination{}; + destination.fill(canary); + mapping_t::store_half_aligned(loaded, destination.data()); + return std::equal(source.begin(), source.begin() + half_count, destination.begin()) && + std::all_of(destination.begin() + half_count, destination.end(), [](element_t value) noexcept { return value == static_cast(91); }); +} + +using signed_byte_mapping = SimdLib::Detail::SimdMappings; +using float_mapping = SimdLib::Detail::SimdMappings; +static_assert(HasAlignedHalfTransfer); +static_assert(!HasAlignedHalfTransfer); + +} // namespace + +/** @brief Verifies direct aligned half-register transfers for every supported integral mapping. */ +TEST_CASE("Implementation aligned half transfers preserve exact bounds and clear loaded high lanes", "[IMPLEMENTATION][PARTIAL_TRANSFER]") +{ + REQUIRE(has_aligned_half_transfer_contract()); + REQUIRE(has_aligned_half_transfer_contract()); + REQUIRE(has_aligned_half_transfer_contract()); + REQUIRE(has_aligned_half_transfer_contract()); + REQUIRE(has_aligned_half_transfer_contract()); + REQUIRE(has_aligned_half_transfer_contract()); + REQUIRE(has_aligned_half_transfer_contract()); + REQUIRE(has_aligned_half_transfer_contract()); +} diff --git a/tests/PartialRegisterArithmetic.tests.cpp b/tests/PartialRegisterArithmetic.tests.cpp new file mode 100644 index 0000000..7d56a69 --- /dev/null +++ b/tests/PartialRegisterArithmetic.tests.cpp @@ -0,0 +1,498 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + +/** @brief Verifies arithmetic availability parity with the selected API for one partial geometry. */ +template consteval bool has_arithmetic_surface_parity() noexcept +{ + using value_t = SimdLib::PartialRegister; + using api_t = typename value_t::api_type; + static_assert(SimdLib::IRegister::Add == SimdLib::IApi::Add); + static_assert(SimdLib::IRegister::Subtract == SimdLib::IApi::Subtract); + static_assert(SimdLib::IRegister::Multiply == SimdLib::IApi::Multiply); + static_assert(SimdLib::IRegister::Divide == SimdLib::IApi::Divide); + static_assert(SimdLib::IRegister::Modulus == SimdLib::IApi::Modulus); + static_assert(SimdLib::IRegister::Negate == SimdLib::IApi::Negate); + static_assert(SimdLib::IRegister::Min == SimdLib::IApi::Min); + static_assert(SimdLib::IRegister::Max == SimdLib::IApi::Max); + static_assert(SimdLib::IRegister::Absolute == SimdLib::IApi::Absolute); + static_assert(SimdLib::IRegister::Sqrt == SimdLib::IApi::Sqrt); + static_assert(SimdLib::IRegister::Average == SimdLib::IApi::Average); + static_assert(SimdLib::IRegister::MultiplyAdd == SimdLib::IApi::MultiplyAdd); + static_assert(SimdLib::IRegister::Magnitude == SimdLib::IApi::Magnitude); + constexpr bool has_magnitude_checked = requires(value_t value) { value.magnitude_checked(); }; + static_assert(has_magnitude_checked == SimdLib::IApi::MagnitudeChecked); + if constexpr (SimdLib::IApi::MagnitudeChecked) + static_assert( + std::same_as().magnitude_checked()), SimdLib::partial_magnitude_checked_result_t>); + static_assert(SimdLib::IRegister::Normalize == SimdLib::IApi::Normalize); + static_assert(SimdLib::IRegister::HorizontalAdd == SimdLib::IApi::HorizontalAdd); + static_assert(SimdLib::IRegister::HorizontalSubtract == SimdLib::IApi::HorizontalSubtract); + static_assert(SimdLib::IRegister::MinPosition == SimdLib::IApi::MinPosition); + static_assert(SimdLib::IRegister::MaxPosition == SimdLib::IApi::MaxPosition); + static_assert(SimdLib::IRegister::AddSaturated == SimdLib::IApi::AddSaturated); + static_assert(SimdLib::IRegister::SubtractSaturated == SimdLib::IApi::SubtractSaturated); + static_assert(SimdLib::IRegister::HorizontalAddSaturated == SimdLib::IApi::HorizontalAddSaturated); + static_assert(SimdLib::IRegister::HorizontalSubtractSaturated == SimdLib::IApi::HorizontalSubtractSaturated); + static_assert(SimdLib::IRegister::AddSubtract == SimdLib::IApi::AddSubtract); + static_assert(SimdLib::IRegister::DotProduct == SimdLib::IApi::DotProduct); + static_assert(SimdLib::IRegister::MultiplyAddAdjacent == SimdLib::IApi::MultiplyAddAdjacent); + static_assert(SimdLib::IRegister::MultiplyAddUnsignedSignedBytes == SimdLib::IApi::ByteMultiplyAdd); + static_assert(SimdLib::IRegister::SumAbsoluteByteDifferences == SimdLib::IApi::Sad); + static_assert(SimdLib::IRegister::MultiSumAbsoluteByteDifferences == SimdLib::IApi::MultiSad); + return true; +} + +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +static_assert(has_arithmetic_surface_parity()); +#endif + +/** @brief Reports whether one scalar has the all-bits-zero representation. */ +template [[nodiscard]] bool has_zero_bits(element_t value) noexcept +{ + const auto bytes = std::bit_cast>(value); + return std::ranges::all_of(bytes, [](std::byte byte) noexcept { return byte == std::byte{}; }); +} + +/** @brief Verifies that every physical lane beyond the logical prefix is bitwise zero. */ +template void require_zero_suffix(value_t value) +{ + const auto native = [&]() + { + if constexpr (requires { value.to_native(); }) + return value.to_native(); + else + return value.native; + }(); + const auto lanes = value_t::api_type::to_array(native); + for (std::size_t lane = value_t::lane_count; lane < value_t::api_type::element_count; ++lane) + REQUIRE(has_zero_bits(lanes[lane])); +} + +/** @brief Verifies the core lane-wise arithmetic surface against scalar arithmetic. */ +template void require_lane_arithmetic() +{ + using value_t = SimdLib::PartialRegister; + std::array lhs_values{}; + std::array rhs_values{}; + for (std::size_t lane = 0; lane < active_count; ++lane) + { + lhs_values[lane] = static_cast(lane + 6); + rhs_values[lane] = static_cast(2); + } + const auto lhs = value_t::from_array(lhs_values); + const auto rhs = value_t::from_array(rhs_values); + + if constexpr (requires { lhs + rhs; }) + { + const auto actual = (lhs + rhs).to_array(); + for (std::size_t lane = 0; lane < active_count; ++lane) + REQUIRE(actual[lane] == static_cast(lhs_values[lane] + rhs_values[lane])); + require_zero_suffix(lhs + rhs); + } + if constexpr (requires { lhs - rhs; }) + { + const auto actual = (lhs - rhs).to_array(); + for (std::size_t lane = 0; lane < active_count; ++lane) + REQUIRE(actual[lane] == static_cast(lhs_values[lane] - rhs_values[lane])); + require_zero_suffix(lhs - rhs); + } + if constexpr (requires { lhs * rhs; }) + { + const auto actual = (lhs * rhs).to_array(); + for (std::size_t lane = 0; lane < active_count; ++lane) + REQUIRE(actual[lane] == static_cast(lhs_values[lane] * rhs_values[lane])); + require_zero_suffix(lhs * rhs); + } + if constexpr (requires { lhs / rhs; }) + { + const auto result = lhs / rhs; + const auto actual = result.to_array(); + for (std::size_t lane = 0; lane < active_count; ++lane) + REQUIRE(actual[lane] == static_cast(lhs_values[lane] / rhs_values[lane])); + require_zero_suffix(result); + } + if constexpr (requires { lhs % rhs; }) + { + const auto result = lhs % rhs; + const auto actual = result.to_array(); + for (std::size_t lane = 0; lane < active_count; ++lane) + REQUIRE(actual[lane] == static_cast(lhs_values[lane] % rhs_values[lane])); + require_zero_suffix(result); + } + if constexpr (requires { -lhs; }) + { + const auto negated = -lhs; + for (std::size_t lane = 0; lane < active_count; ++lane) + REQUIRE(negated.to_array()[lane] == static_cast(element_t{} - lhs_values[lane])); + require_zero_suffix(negated); + } + if constexpr (requires { lhs.min(rhs); }) + { + const auto minimum = lhs.min(rhs); + const auto maximum = lhs.max(rhs); + const auto absolute_source = [&]() + { + if constexpr (std::is_signed_v || std::is_floating_point_v) + return -lhs; + else + return lhs; + }(); + const auto absolute = absolute_source.absolute(); + for (std::size_t lane = 0; lane < active_count; ++lane) + { + REQUIRE(minimum.to_array()[lane] == std::min(lhs_values[lane], rhs_values[lane])); + REQUIRE(maximum.to_array()[lane] == std::max(lhs_values[lane], rhs_values[lane])); + REQUIRE(absolute.to_array()[lane] == lhs_values[lane]); + } + require_zero_suffix(minimum); + require_zero_suffix(maximum); + require_zero_suffix(absolute); + } + if constexpr (requires { lhs.sqrt(); }) + { + std::array squares{}; + for (std::size_t lane = 0; lane < active_count; ++lane) + { + const auto root = static_cast(lane % 3 + 1); + squares[lane] = static_cast(root * root); + } + const auto result = value_t::from_array(squares).sqrt(); + for (std::size_t lane = 0; lane < active_count; ++lane) + REQUIRE(result.to_array()[lane] == static_cast(lane % 3 + 1)); + require_zero_suffix(result); + } + if constexpr (requires { lhs.average(rhs); }) + { + const auto result = lhs.average(rhs); + for (std::size_t lane = 0; lane < active_count; ++lane) + REQUIRE(result.to_array()[lane] == static_cast((lhs_values[lane] + rhs_values[lane] + 1) / 2)); + require_zero_suffix(result); + } + if constexpr (requires { lhs.multiply_add(rhs, rhs); }) + { + const auto result = lhs.multiply_add(rhs, rhs); + for (std::size_t lane = 0; lane < active_count; ++lane) + REQUIRE(result.to_array()[lane] == static_cast(lhs_values[lane] * rhs_values[lane] + rhs_values[lane])); + require_zero_suffix(result); + } + if constexpr (requires { lhs.add_saturated(rhs); }) + { + const auto added = lhs.add_saturated(rhs); + const auto subtracted = lhs.subtract_saturated(rhs); + for (std::size_t lane = 0; lane < active_count; ++lane) + { + REQUIRE(added.to_array()[lane] == static_cast(lhs_values[lane] + rhs_values[lane])); + REQUIRE(subtracted.to_array()[lane] == static_cast(lhs_values[lane] - rhs_values[lane])); + } + require_zero_suffix(added); + require_zero_suffix(subtracted); + } +} + +/** @brief Verifies logical extrema positions cannot select zero-valued inactive lanes. */ +template void require_positions() +{ + constexpr std::size_t active_count = bits == 256 ? 128 / (sizeof(element_t) * 8) + 1 : 3; + using value_t = SimdLib::PartialRegister; + std::array lanes{}; + lanes.fill(element_t{5}); + lanes[1] = element_t{2}; + lanes[2] = element_t{9}; + const auto value = value_t::from_array(lanes); + REQUIRE(value.min_position() == 1); + REQUIRE(value.max_position() == 2); + lanes.fill(element_t{2}); + const auto ties = value_t::from_array(lanes); + REQUIRE(ties.min_position() == 0); + REQUIRE(ties.max_position() == 0); + const auto lower_sentinel_tie = value_t::broadcast(std::numeric_limits::lowest()); + const auto upper_sentinel_tie = value_t::broadcast(std::numeric_limits::max()); + REQUIRE(lower_sentinel_tie.max_position() == 0); + REQUIRE(upper_sentinel_tie.min_position() == 0); +} + +/** @brief Verifies grouped floating operations use only active inputs and clear generated suffix values. */ +template void require_floating_group_operations() +{ + using value_t = SimdLib::PartialRegister; + std::array source{}; + source[0] = 3.0F; + source[1] = 4.0F; + for (std::size_t lane = 2; lane < active_count; ++lane) + source[lane] = 0.0F; + const auto value = value_t::from_array(source); + const auto magnitude = value.magnitude(); + const auto normalized = value.normalize(); + REQUIRE(magnitude.to_array()[0] == Catch::Approx(5.0F)); + REQUIRE(magnitude.to_array()[1] == Catch::Approx(5.0F)); + REQUIRE(normalized.to_array()[0] == Catch::Approx(0.6F)); + REQUIRE(normalized.to_array()[1] == Catch::Approx(0.8F)); + require_zero_suffix(magnitude); + require_zero_suffix(normalized); + const auto dot = value.template dot_product<0x71>(value); + REQUIRE(dot.to_array()[0] == Catch::Approx(25.0F)); + for (std::size_t lane = 1; lane < active_count; ++lane) + REQUIRE(dot.to_array()[lane] == 0.0F); + require_zero_suffix(dot); +} + +/** @brief Verifies zero active magnitudes retain Register's exceptional normalization semantics without exposing the suffix. */ +void require_zero_normalization_semantics() +{ + using value_t = SimdLib::PartialRegister; + const auto normalized = value_t::zero().normalize(); + for (const auto lane : normalized.to_array()) + REQUIRE(std::isnan(lane)); + require_zero_suffix(normalized); +} + +/** @brief Verifies lane-wise and horizontal saturation at both numeric limits. */ +void require_saturation_boundaries() +{ + using unsigned_t = SimdLib::PartialRegister; + const auto unsigned_lhs = unsigned_t::from_lanes(250, 5, 0); + const auto unsigned_rhs = unsigned_t::from_lanes(10, 10, 1); + const auto unsigned_added = unsigned_lhs.add_saturated(unsigned_rhs); + const auto unsigned_subtracted = unsigned_lhs.subtract_saturated(unsigned_rhs); + REQUIRE(unsigned_added.to_array() == std::array{255, 15, 1}); + REQUIRE(unsigned_subtracted.to_array() == std::array{240, 0, 0}); + require_zero_suffix(unsigned_added); + require_zero_suffix(unsigned_subtracted); + + using signed_t = SimdLib::PartialRegister; + const auto signed_lhs = signed_t::from_lanes(120, -120, 5); + const auto signed_rhs = signed_t::from_lanes(20, 20, -10); + const auto signed_added = signed_lhs.add_saturated(signed_rhs); + const auto signed_subtracted = signed_lhs.subtract_saturated(signed_rhs); + REQUIRE(signed_added.to_array() == std::array{127, -100, -5}); + REQUIRE(signed_subtracted.to_array() == std::array{100, -128, 15}); + require_zero_suffix(signed_added); + require_zero_suffix(signed_subtracted); + + using horizontal_t = SimdLib::PartialRegister; + constexpr auto maximum = std::numeric_limits::max(); + constexpr auto minimum = std::numeric_limits::lowest(); + const auto add_input = horizontal_t::from_lanes(maximum, 1, minimum, -1, 5); + const auto subtract_input = horizontal_t::from_lanes(maximum, -1, minimum, 1, 5); + const auto horizontal_added = add_input.horizontal_add_saturated(add_input); + const auto horizontal_subtracted = subtract_input.horizontal_subtract_saturated(subtract_input); + REQUIRE(horizontal_added.to_array() == std::array{maximum, minimum, 5, 0, maximum}); + REQUIRE(horizontal_subtracted.to_array() == std::array{maximum, minimum, 5, 0, maximum}); + require_zero_suffix(horizontal_added); + require_zero_suffix(horizontal_subtracted); +} + +/** @brief Verifies result aliases preserve final unmatched and grouped output lanes when they fill a register. */ +template void require_complete_specialized_results() +{ + using source_t = SimdLib::PartialRegister::element_count - 1>; + std::array source_lanes{}; + for (std::size_t lane = 0; lane < source_lanes.size(); ++lane) + source_lanes[lane] = 1; + const auto source = source_t::from_array(source_lanes); + const auto adjacent = source.multiply_add_adjacent(source); + const auto byte_products = source.multiply_add_unsigned_signed_bytes(source); + const auto sad = source.sum_absolute_byte_differences(source_t::zero()); + static_assert(std::same_as>); + static_assert(std::same_as>); + static_assert(std::same_as>); + const auto adjacent_lanes = adjacent.to_array(); + const auto byte_product_lanes = byte_products.to_array(); + for (std::size_t lane = 0; lane + 1 < adjacent_lanes.size(); ++lane) + { + REQUIRE(adjacent_lanes[lane] == 2); + REQUIRE(byte_product_lanes[lane] == 2); + } + REQUIRE(adjacent_lanes.back() == 1); + REQUIRE(byte_product_lanes.back() == 1); + const auto sad_lanes = sad.to_array(); + for (std::size_t lane = 0; lane + 1 < sad_lanes.size(); ++lane) + REQUIRE(sad_lanes[lane] == 8); + REQUIRE(sad_lanes.back() == 7); +} + +/** @brief Verifies checked magnitude retains overflow metadata beyond a one-lane source prefix. */ +template void require_checked_magnitude_result_extent() +{ + using source_t = SimdLib::PartialRegister; + const auto source = source_t::broadcast(std::numeric_limits::lowest()); + const auto checked = source.magnitude_checked(); + static_assert(std::same_as>); + const auto lanes = checked.to_array(); + REQUIRE(lanes[0] == std::numeric_limits::max()); + REQUIRE(lanes[1] == -1); + if constexpr (bits == 256) + { + REQUIRE(lanes[2] == std::numeric_limits::max()); + REQUIRE(lanes[3] == -1); + } +} + +/** @brief Verifies checked magnitude stops after the final occupied group's status lane. */ +template void require_partial_checked_magnitude_result_extent() +{ + using source_t = SimdLib::PartialRegister; + using expected_t = SimdLib::PartialRegister; + const auto source = source_t::from_array(std::array{}); + const auto checked = source.magnitude_checked(); + static_assert(std::same_as); + const auto lanes = checked.to_array(); + REQUIRE(lanes[0] == 0); + REQUIRE(lanes[1] == 0); + if constexpr (bits == 256) + { + REQUIRE(lanes[8] == 0); + REQUIRE(lanes[9] == 0); + } + require_zero_suffix(checked); +} + +/** @brief Verifies horizontal, saturated, alternating, and lane-combining adapters preserve their result contracts. */ +void require_specialized_adapters() +{ + using horizontal_t = SimdLib::PartialRegister; + const auto lhs = horizontal_t::from_lanes(1, 2, 3, 4, 5); + const auto rhs = horizontal_t::from_lanes(10, 20, 30, 40, 50); + REQUIRE(lhs.horizontal_add(rhs).to_array() == std::array{3, 7, 5, 0, 30}); + REQUIRE(lhs.horizontal_subtract(rhs).to_array() == std::array{-1, -1, 5, 0, -10}); + REQUIRE(lhs.horizontal_add_saturated(rhs).to_array() == lhs.horizontal_add(rhs).to_array()); + REQUIRE(lhs.horizontal_subtract_saturated(rhs).to_array() == lhs.horizontal_subtract(rhs).to_array()); + require_zero_suffix(lhs.horizontal_add_saturated(rhs)); + require_zero_suffix(lhs.horizontal_subtract_saturated(rhs)); + require_zero_suffix(lhs.add_saturated(rhs)); + require_zero_suffix(lhs.subtract_saturated(rhs)); + + using byte_t = SimdLib::PartialRegister; + const auto bytes = byte_t::from_lanes(1, 2, 3, 4, 5, 6, 7); + const auto adjacent = bytes.multiply_add_adjacent(bytes); + REQUIRE(adjacent.to_array()[0] == 5); + REQUIRE(adjacent.to_array()[1] == 25); + REQUIRE(adjacent.to_array()[2] == 61); + REQUIRE(adjacent.to_array()[3] == 49); + require_zero_suffix(adjacent); + const auto byte_products = bytes.multiply_add_unsigned_signed_bytes(bytes); + REQUIRE(byte_products.to_array()[0] == 5); + REQUIRE(byte_products.to_array()[1] == 25); + REQUIRE(byte_products.to_array()[2] == 61); + REQUIRE(byte_products.to_array()[3] == 49); + require_zero_suffix(byte_products); + const auto sad = bytes.sum_absolute_byte_differences(byte_t::zero()); + REQUIRE(sad.to_array()[0] == 28); + require_zero_suffix(sad); + const auto multi_sad = bytes.template multi_sum_absolute_byte_differences<0>(byte_t::zero()); + REQUIRE(multi_sad.to_array() == std::array{10, 14, 18, 22, 18, 13, 7, 0}); + + using alternating_t = SimdLib::PartialRegister; + const auto floats = alternating_t::from_lanes(10.0F, 20.0F, 30.0F); + const auto alternating = floats.add_subtract(alternating_t::broadcast(2.0F)); + REQUIRE(alternating.to_array() == std::array{8.0F, 22.0F, 28.0F}); + require_zero_suffix(alternating); + + using magnitude_t = SimdLib::PartialRegister; + const auto magnitude_input = magnitude_t::from_lanes(3, 4, 0, 0, 0); + const auto magnitude = magnitude_input.magnitude(); + const auto checked = magnitude_input.magnitude_checked(); + REQUIRE(magnitude.to_array()[0] == 5); + REQUIRE(checked.to_array()[0] == 5); + REQUIRE(checked.to_array()[1] == 0); + require_zero_suffix(magnitude); + require_zero_suffix(checked); +} + +/** @brief Reports whether a value type exposes addition assignment. */ +template +concept has_add_assign = requires(value_t value) { value += value; }; + +/** @brief Reports whether a value type exposes subtraction assignment. */ +template +concept has_subtract_assign = requires(value_t value) { value -= value; }; + +/** @brief Reports whether a value type exposes a dot-product control that writes inactive lanes. */ +template +concept has_inactive_dot_output = requires(value_t value) { value.template dot_product<0x7f>(value); }; + +static_assert(!has_add_assign>); +static_assert(!has_subtract_assign>); +static_assert(!has_inactive_dot_output>); + +TEST_CASE("PartialRegister lane arithmetic follows scalar active-lane semantics", "[simdlib][partial-register][arithmetic]") +{ + require_lane_arithmetic(); + require_lane_arithmetic(); + require_lane_arithmetic(); + require_lane_arithmetic(); + require_lane_arithmetic(); + require_lane_arithmetic(); + require_lane_arithmetic(); + require_lane_arithmetic(); + require_lane_arithmetic(); + require_lane_arithmetic(); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 + require_lane_arithmetic(); + require_lane_arithmetic(); + require_lane_arithmetic(); + require_lane_arithmetic(); + require_lane_arithmetic(); + require_lane_arithmetic(); + require_lane_arithmetic(); + require_lane_arithmetic(); + require_lane_arithmetic(); + require_lane_arithmetic(); +#endif +} + +TEST_CASE("PartialRegister reductions and specialized results honor the logical prefix", "[simdlib][partial-register][specialized]") +{ + require_positions(); + require_positions(); + require_floating_group_operations<128, 3>(); + require_zero_normalization_semantics(); + require_specialized_adapters(); + require_saturation_boundaries(); + require_complete_specialized_results<128>(); + require_checked_magnitude_result_extent<128>(); + require_partial_checked_magnitude_result_extent<128>(); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 + require_positions(); + require_floating_group_operations<256, 5>(); + require_complete_specialized_results<256>(); + require_checked_magnitude_result_extent<256>(); + require_partial_checked_magnitude_result_extent<256>(); +#endif +} + +} // namespace diff --git a/tests/PartialRegisterBitwiseShiftComparison.tests.cpp b/tests/PartialRegisterBitwiseShiftComparison.tests.cpp new file mode 100644 index 0000000..3323a9d --- /dev/null +++ b/tests/PartialRegisterBitwiseShiftComparison.tests.cpp @@ -0,0 +1,299 @@ +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 +#define SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 SIMDLIB_HAS_AVX2 +#endif + +namespace +{ + +/** @brief Verifies bitwise, shift, and comparison surface parity for one partial geometry. */ +template [[nodiscard]] consteval bool has_operation_surface_parity() noexcept +{ + using value_t = SimdLib::PartialRegister; + using api_t = typename value_t::api_type; + static_assert(SimdLib::IRegister::BitwiseAnd == SimdLib::IApi::BitwiseAnd); + static_assert(SimdLib::IRegister::BitwiseOr == SimdLib::IApi::BitwiseOr); + static_assert(SimdLib::IRegister::BitwiseXor == SimdLib::IApi::BitwiseXor); + static_assert(SimdLib::IRegister::BitwiseNot == (SimdLib::IApi::BitwiseNot && SimdLib::IApi::BitwiseAnd)); + static_assert(SimdLib::IRegister::BitwiseAndNot == SimdLib::IApi::BitwiseAndNot); + static_assert(SimdLib::IRegister::Movemask == SimdLib::IApi::Movemask); + static_assert(SimdLib::IRegister::LaneSignBits == SimdLib::IApi::MovemaskSlim); + static_assert(SimdLib::IRegister::ShiftLeft == SimdLib::IApi::ShiftLeft); + static_assert(SimdLib::IRegister::LogicalShiftRight == SimdLib::IApi::ShiftRight); + static_assert(SimdLib::IRegister::ShiftRight == ((std::is_signed_v && SimdLib::IApi::ArithmeticShiftRight) || + (std::is_unsigned_v && SimdLib::IApi::ShiftRight))); + static_assert(SimdLib::IRegister::ShiftBytesLeftSlow == (bits == 128 && SimdLib::IApi::ShiftBytesSlow && SimdLib::IApi::BitwiseAnd)); + static_assert(SimdLib::IRegister::ShiftBytesRightSlow == (bits == 128 && SimdLib::IApi::ShiftBytesSlow)); + static_assert(SimdLib::IRegister::ShiftBytesLeft == (SimdLib::IApi::ShiftBytesLeft && SimdLib::IApi::BitwiseAnd)); + static_assert(SimdLib::IRegister::ShiftBytesRight == SimdLib::IApi::ShiftBytesRight); + static_assert(SimdLib::IRegister::ShiftBitsLeftSlow == (bits == 128 && SimdLib::IApi::ShiftBitsSlow && SimdLib::IApi::BitwiseAnd)); + static_assert(SimdLib::IRegister::ShiftBitsRightSlow == (bits == 128 && SimdLib::IApi::ShiftBitsSlow)); + static_assert(SimdLib::IRegister::ShiftBitsLeft == (bits == 128 && SimdLib::IApi::ShiftBits && SimdLib::IApi::BitwiseAnd)); + static_assert(SimdLib::IRegister::ShiftBitsRight == (bits == 128 && SimdLib::IApi::ShiftBits)); + static_assert(SimdLib::IRegister::CompareEqual == (SimdLib::IApi::CompareEqual && SimdLib::IApi::BitwiseAnd)); + static_assert(SimdLib::IRegister::CompareGreater == (SimdLib::IApi::CompareGreater && SimdLib::IApi::BitwiseAnd)); + static_assert(SimdLib::IRegister::CompareGreaterEqual == (SimdLib::IApi::CompareGreaterEqual && SimdLib::IApi::BitwiseAnd)); + static_assert(SimdLib::IRegister::CompareLess == (SimdLib::IApi::CompareLess && SimdLib::IApi::BitwiseAnd)); + static_assert(SimdLib::IRegister::CompareLessEqual == (SimdLib::IApi::CompareLessEqual && SimdLib::IApi::BitwiseAnd)); + static_assert(SimdLib::IRegister::Equal == + (SimdLib::IApi::CompareEqual && SimdLib::IApi::BitwiseAnd && SimdLib::IApi::MovemaskSlim)); + static_assert(SimdLib::IRegister::NotEqual == SimdLib::IRegister::Equal); + static_assert(!SimdLib::IRegister::ShiftBytesLeft); + static_assert(!SimdLib::IRegister::ShiftBytesRight); + static_assert(!SimdLib::IRegister::ShiftBitsLeft); + static_assert(!SimdLib::IRegister::ShiftBitsRight); + return true; +} + +#define SIMDLIB_PARTIAL_OPERATION_SURFACE(element_type, width, count) static_assert(has_operation_surface_parity()) +SIMDLIB_PARTIAL_OPERATION_SURFACE(std::int8_t, 128, 13); +SIMDLIB_PARTIAL_OPERATION_SURFACE(std::uint8_t, 128, 13); +SIMDLIB_PARTIAL_OPERATION_SURFACE(std::int16_t, 128, 5); +SIMDLIB_PARTIAL_OPERATION_SURFACE(std::uint16_t, 128, 5); +SIMDLIB_PARTIAL_OPERATION_SURFACE(std::int32_t, 128, 3); +SIMDLIB_PARTIAL_OPERATION_SURFACE(std::uint32_t, 128, 3); +SIMDLIB_PARTIAL_OPERATION_SURFACE(std::int64_t, 128, 1); +SIMDLIB_PARTIAL_OPERATION_SURFACE(std::uint64_t, 128, 1); +SIMDLIB_PARTIAL_OPERATION_SURFACE(float, 128, 3); +SIMDLIB_PARTIAL_OPERATION_SURFACE(double, 128, 1); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 +SIMDLIB_PARTIAL_OPERATION_SURFACE(std::int8_t, 256, 19); +SIMDLIB_PARTIAL_OPERATION_SURFACE(std::uint8_t, 256, 19); +SIMDLIB_PARTIAL_OPERATION_SURFACE(std::int16_t, 256, 11); +SIMDLIB_PARTIAL_OPERATION_SURFACE(std::uint16_t, 256, 11); +SIMDLIB_PARTIAL_OPERATION_SURFACE(std::int32_t, 256, 5); +SIMDLIB_PARTIAL_OPERATION_SURFACE(std::uint32_t, 256, 5); +SIMDLIB_PARTIAL_OPERATION_SURFACE(std::int64_t, 256, 3); +SIMDLIB_PARTIAL_OPERATION_SURFACE(std::uint64_t, 256, 3); +SIMDLIB_PARTIAL_OPERATION_SURFACE(float, 256, 5); +SIMDLIB_PARTIAL_OPERATION_SURFACE(double, 256, 3); +#endif +#undef SIMDLIB_PARTIAL_OPERATION_SURFACE + +/** @brief Reports whether one scalar has the all-bits-zero representation. */ +template [[nodiscard]] bool has_zero_bits(element_t value) noexcept +{ + const auto bytes = std::bit_cast>(value); + for (const auto byte : bytes) + if (byte != std::byte{}) + return false; + return true; +} + +/** @brief Requires that every physical lane beyond the logical prefix remains bitwise zero. */ +template void require_zero_suffix(value_t value) +{ + const auto lanes = value_t::api_type::to_array(value.to_native()); + for (std::size_t lane = value_t::lane_count; lane < value_t::native_lane_count; ++lane) + REQUIRE(has_zero_bits(lanes[lane])); +} + +/** @brief Verifies bitwise values, active-only masks, and suffix closure for one integral geometry. */ +template void require_bitwise_contract() +{ + using value_t = SimdLib::PartialRegister; + std::array lhs_values{}; + std::array rhs_values{}; + for (std::size_t lane = 0; lane < active_count; ++lane) + { + lhs_values[lane] = static_cast((lane & 1U) == 0 ? 0x55 : 0xaa); + rhs_values[lane] = static_cast((lane & 1U) == 0 ? 0x0f : 0xf0); + } + const auto lhs = value_t::from_array(lhs_values); + const auto rhs = value_t::from_array(rhs_values); + const auto intersection = lhs & rhs; + const auto union_value = lhs | rhs; + const auto exclusive = lhs ^ rhs; + const auto complement = ~lhs; + const auto difference = lhs.andnot(rhs); + for (std::size_t lane = 0; lane < active_count; ++lane) + { + REQUIRE(intersection.to_array()[lane] == static_cast(lhs_values[lane] & rhs_values[lane])); + REQUIRE(union_value.to_array()[lane] == static_cast(lhs_values[lane] | rhs_values[lane])); + REQUIRE(exclusive.to_array()[lane] == static_cast(lhs_values[lane] ^ rhs_values[lane])); + REQUIRE(complement.to_array()[lane] == static_cast(~lhs_values[lane])); + REQUIRE(difference.to_array()[lane] == static_cast((~lhs_values[lane]) & rhs_values[lane])); + } + require_zero_suffix(intersection); + require_zero_suffix(union_value); + require_zero_suffix(exclusive); + require_zero_suffix(complement); + require_zero_suffix(difference); +} + +/** @brief Verifies per-lane shift semantics and suffix closure for one integral geometry. */ +template void require_lane_shift_contract() +{ + using value_t = SimdLib::PartialRegister; + std::array values{}; + for (std::size_t lane = 0; lane < active_count; ++lane) + values[lane] = static_cast(lane + 2); + if constexpr (std::is_signed_v) + values[0] = static_cast(-8); + const auto value = value_t::from_array(values); + const auto left = value << 2; + const auto logical_right = value.logical_shift_right(1); + const auto signedness_right = value >> 1; + using unsigned_t = std::make_unsigned_t; + for (std::size_t lane = 0; lane < active_count; ++lane) + { + REQUIRE(left.to_array()[lane] == static_cast(static_cast(values[lane]) << 2)); + REQUIRE(logical_right.to_array()[lane] == static_cast(static_cast(values[lane]) >> 1)); + REQUIRE(signedness_right.to_array()[lane] == static_cast(values[lane] >> 1)); + } + require_zero_suffix(left); + require_zero_suffix(logical_right); + require_zero_suffix(signedness_right); +} + +/** @brief Verifies logical whole-payload byte and bit shifts at a non-group-aligned active extent. */ +template void require_payload_shift_contract() +{ + using value_t = SimdLib::PartialRegister; + std::array values{}; + for (std::size_t lane = 0; lane < active_count; ++lane) + values[lane] = static_cast(lane + 1); + const auto value = value_t::from_array(values); + const auto bytes_left = value.template shift_bytes_left<3>(); + const auto bytes_right = value.template shift_bytes_right<3>(); + for (std::size_t lane = 0; lane < active_count; ++lane) + { + REQUIRE(bytes_left.to_array()[lane] == (lane < 3 ? 0 : values[lane - 3])); + REQUIRE(bytes_right.to_array()[lane] == (lane + 3 < active_count ? values[lane + 3] : 0)); + } + require_zero_suffix(bytes_left); + require_zero_suffix(bytes_right); + + if constexpr (bits == 128) + { + const auto slow_bytes_left = value.shift_bytes_left_slow(3); + const auto slow_bytes_right = value.shift_bytes_right_slow(3); + const auto bits_left = value.template shift_bits_left<4>(); + const auto bits_right = value.template shift_bits_right<4>(); + const auto slow_bits_left = value.shift_bits_left_slow(4); + const auto slow_bits_right = value.shift_bits_right_slow(4); + REQUIRE(slow_bytes_left.to_array() == bytes_left.to_array()); + REQUIRE(slow_bytes_right.to_array() == bytes_right.to_array()); + REQUIRE(slow_bits_left.to_array() == bits_left.to_array()); + REQUIRE(slow_bits_right.to_array() == bits_right.to_array()); + REQUIRE(value.template shift_bytes_left().to_array() == std::array{}); + REQUIRE(value.template shift_bytes_right().to_array() == std::array{}); + REQUIRE(value.template shift_bits_left(active_count * 8)>().to_array() == std::array{}); + REQUIRE(value.template shift_bits_right(active_count * 8)>().to_array() == std::array{}); + require_zero_suffix(bits_left); + require_zero_suffix(bits_right); + require_zero_suffix(slow_bytes_left); + require_zero_suffix(slow_bytes_right); + require_zero_suffix(slow_bits_left); + require_zero_suffix(slow_bits_right); + } +} + +/** @brief Verifies ordered comparisons, scalar equality, and false inactive predicate lanes. */ +template void require_comparison_contract() +{ + using value_t = SimdLib::PartialRegister; + std::array lhs_values{}; + std::array rhs_values{}; + for (std::size_t lane = 0; lane < active_count; ++lane) + { + lhs_values[lane] = static_cast(lane + 1); + rhs_values[lane] = static_cast(lane + 1); + } + rhs_values[active_count - 1] = static_cast(rhs_values[active_count - 1] + 1); + const auto lhs = value_t::from_array(lhs_values); + const auto rhs = value_t::from_array(rhs_values); + const auto equal = lhs.compare_equal(rhs); + const auto greater = lhs.compare_greater(rhs); + const auto greater_equal = lhs.compare_greater_equal(rhs); + const auto less = lhs.compare_less(rhs); + const auto less_equal = lhs.compare_less_equal(rhs); + const auto expected_equal_bits = (typename value_t::mask_type::bits_type{1} << (active_count - 1)) - 1; + REQUIRE(equal.bits() == expected_equal_bits); + REQUIRE(greater.none()); + REQUIRE(greater_equal.bits() == expected_equal_bits); + REQUIRE(less.bits() == (typename value_t::mask_type::bits_type{1} << (active_count - 1))); + REQUIRE(less_equal.all()); + REQUIRE_FALSE(lhs == rhs); + REQUIRE(lhs != rhs); + REQUIRE(lhs == lhs); + const auto require_false_suffix = [](mask_t mask) + { + const auto native = mask_t::api_type::to_array(mask.to_native()); + for (std::size_t lane = mask_t::lane_count; lane < mask_t::native_lane_count; ++lane) + REQUIRE(has_zero_bits(native[lane])); + }; + require_false_suffix(equal); + require_false_suffix(greater); + require_false_suffix(greater_equal); + require_false_suffix(less); + require_false_suffix(less_equal); +} + +TEST_CASE("PartialRegister bitwise operations preserve active values and inactive zeros", "[PartialRegister][Bitwise]") +{ + require_bitwise_contract(); + require_bitwise_contract(); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 + require_bitwise_contract(); +#endif +} + +TEST_CASE("PartialRegister sign masks expose active lanes only", "[PartialRegister][Bitwise]") +{ + using value_t = SimdLib::PartialRegister; + const auto value = value_t::from_lanes(-1, 2, -3); + REQUIRE(value.lane_sign_bits() == 0b101); + REQUIRE(value.movemask() == 0x0f0f); +} + +TEST_CASE("PartialRegister per-lane shifts preserve logical geometry", "[PartialRegister][Shift]") +{ + require_lane_shift_contract(); + require_lane_shift_contract(); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 + require_lane_shift_contract(); +#endif +} + +TEST_CASE("PartialRegister whole-payload shifts use the active extent", "[PartialRegister][Shift]") +{ + require_payload_shift_contract<128, 13>(); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 + require_payload_shift_contract<256, 19>(); +#endif +} + +TEST_CASE("PartialRegister comparisons ignore inactive zero equality", "[PartialRegister][Comparison]") +{ + require_comparison_contract(); + require_comparison_contract(); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 + require_comparison_contract(); +#endif +} + +TEST_CASE("PartialRegister floating equality preserves NaN and signed-zero behavior", "[PartialRegister][Comparison]") +{ + using value_t = SimdLib::PartialRegister; + const auto positive_zero = value_t::from_lanes(0.0F, 2.0F, 3.0F); + const auto negative_zero = value_t::from_lanes(-0.0F, 2.0F, 3.0F); + const auto nan_value = value_t::from_lanes(std::numeric_limits::quiet_NaN(), 2.0F, 3.0F); + REQUIRE(positive_zero == negative_zero); + REQUIRE_FALSE(nan_value == nan_value); + REQUIRE(nan_value != nan_value); +} + +} // namespace diff --git a/tests/PartialRegisterConstructionTransfer.tests.cpp b/tests/PartialRegisterConstructionTransfer.tests.cpp new file mode 100644 index 0000000..89f2148 --- /dev/null +++ b/tests/PartialRegisterConstructionTransfer.tests.cpp @@ -0,0 +1,199 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 +#define SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 SIMDLIB_HAS_AVX2 +#endif + +namespace +{ + +/** @brief Produces one exactly representable active-lane test value. */ +template [[nodiscard]] constexpr element_t test_lane_value(std::size_t lane) noexcept +{ + return static_cast(lane + 3); +} + +/** @brief Reports whether one scalar value has an all-bits-zero representation. */ +template [[nodiscard]] constexpr bool has_zero_bits(element_t value) noexcept +{ + const auto bytes = std::bit_cast>(value); + return std::all_of(bytes.begin(), bytes.end(), [](std::byte byte) constexpr noexcept { return byte == std::byte{}; }); +} + +/** @brief Constructs one partial value through its exact lane-list factory. */ +template [[nodiscard]] constexpr value_t from_test_lanes(std::index_sequence) noexcept +{ + return value_t::from_lanes(test_lane_value(indices)...); +} + +/** @brief Reports whether every active lane matches and the inactive suffix is bitwise zero. */ +template [[nodiscard]] bool has_value_contract(value_t value, const std::array &expected) +{ + using api_t = typename value_t::api_type; + const auto logical = value.to_array(); + const auto native = api_t::to_array(value.to_native()); + if (logical != expected) + return false; + for (std::size_t lane = 0; lane < value_t::lane_count; ++lane) + if (native[lane] != expected[lane]) + return false; + for (std::size_t lane = value_t::lane_count; lane < value_t::native_lane_count; ++lane) + if (!has_zero_bits(native[lane])) + return false; + return true; +} + +/** @brief Reports whether compile-time-selected active lane observation and replacement preserve the contract. */ +template [[nodiscard]] bool has_lane_access(value_t value, std::index_sequence) +{ + using element_t = typename value_t::element_type; + if (!((value.template lane() == test_lane_value(indices)) && ...)) + return false; + ((value = value.template with_lane(static_cast(indices + 37))), ...); + const auto replaced = value.to_array(); + if (!((replaced[indices] == static_cast(indices + 37)) && ...)) + return false; + const auto native = value_t::api_type::to_array(value.to_native()); + for (std::size_t lane = value_t::lane_count; lane < value_t::native_lane_count; ++lane) + if (!has_zero_bits(native[lane])) + return false; + return true; +} + +/** @brief Reports whether construction, exact-extent transfer, observation, and lane access satisfy one partial geometry. */ +template [[nodiscard]] bool has_construction_transfer_contract() +{ + using value_t = SimdLib::PartialRegister; + using complete_t = SimdLib::Register; + static_assert(SimdLib::IRegister::CoreSurface); + static_assert([](std::index_sequence) + { return SimdLib::IRegister::FromLanes(indices), element_t{}))...>; }( + std::make_index_sequence{})); + std::array source{}; + for (std::size_t lane = 0; lane < source.size(); ++lane) + source[lane] = test_lane_value(lane); + + std::array zeros{}; + if (!has_value_contract(value_t::zero(), zeros)) + return false; + std::array broadcasts{}; + broadcasts.fill(test_lane_value(0)); + if (!has_value_contract(value_t::broadcast(test_lane_value(0)), broadcasts) || !has_value_contract(value_t::from_array(source), source)) + return false; + const auto listed = from_test_lanes(std::make_index_sequence{}); + if (!has_value_contract(listed, source) || !has_lane_access(listed, std::make_index_sequence{})) + return false; + + std::array complete_source{}; + for (std::size_t lane = 0; lane < complete_source.size(); ++lane) + complete_source[lane] = test_lane_value(lane); + const auto imported_complete = value_t::from_register(complete_t{value_t::api_type::construct(complete_source)}); + if (!has_value_contract(imported_complete, source)) + return false; + const auto exported_complete = value_t::api_type::to_array(imported_complete.to_register().native); + for (std::size_t lane = 0; lane < value_t::lane_count; ++lane) + if (exported_complete[lane] != source[lane]) + return false; + for (std::size_t lane = value_t::lane_count; lane < value_t::native_lane_count; ++lane) + if (!has_zero_bits(exported_complete[lane])) + return false; + + std::array unaligned_source{}; + std::copy(source.begin(), source.end(), unaligned_source.begin() + 1); + if (!has_value_contract(value_t::load(std::span{unaligned_source.data() + 1, value_t::lane_count}), source)) + return false; + + alignas(value_t::byte_count) std::array aligned_source{}; + std::copy(source.begin(), source.end(), aligned_source.begin() + value_t::native_lane_count); + aligned_source[value_t::native_lane_count - 1] = static_cast(89); + aligned_source.back() = static_cast(91); + if (!has_value_contract( + value_t::load_aligned(std::span{aligned_source.data() + value_t::native_lane_count, value_t::lane_count}), + source)) + return false; + + const auto source_bytes = std::bit_cast>(source); + std::array byte_source{}; + std::copy(source_bytes.begin(), source_bytes.end(), byte_source.begin() + 1); + if (!has_value_contract(value_t::load_bytes(std::span{byte_source.data() + 1, value_t::active_byte_count}), + source)) + return false; + + const auto element_canary = static_cast(97); + std::array unaligned_destination{}; + unaligned_destination.fill(element_canary); + listed.store(std::span{unaligned_destination.data() + 1, value_t::lane_count}); + if (unaligned_destination.front() != element_canary || unaligned_destination.back() != element_canary || + !std::equal(source.begin(), source.end(), unaligned_destination.begin() + 1)) + return false; + + alignas(value_t::byte_count) std::array aligned_destination{}; + aligned_destination.fill(element_canary); + listed.store_aligned(std::span{aligned_destination.data() + value_t::native_lane_count, value_t::lane_count}); + if (!std::all_of(aligned_destination.begin(), aligned_destination.begin() + value_t::native_lane_count, + [](element_t lane) noexcept { return lane == static_cast(97); }) || + aligned_destination.back() != element_canary || !std::equal(source.begin(), source.end(), aligned_destination.begin() + value_t::native_lane_count)) + return false; + + constexpr std::byte byte_canary{0xa5}; + std::array byte_destination{}; + byte_destination.fill(byte_canary); + listed.store_bytes(std::span{byte_destination.data() + 1, value_t::active_byte_count}); + return std::to_integer(byte_destination.front()) == std::to_integer(byte_canary) && + std::to_integer(byte_destination.back()) == std::to_integer(byte_canary) && + std::equal(source_bytes.begin(), source_bytes.end(), byte_destination.begin() + 1); +} + +/** @brief Reports whether every non-complete active extent passes for one element type and native width. */ +template +[[nodiscard]] bool has_all_active_counts(std::index_sequence) +{ + return ( + []() + { + if constexpr (SimdLib::PartialRegisterAvailable) + return has_construction_transfer_contract(); + else + return true; + }.template operator()() && + ...); +} + +/** @brief Reports whether the full supported element-type matrix passes for one native width. */ +template [[nodiscard]] bool has_construction_transfer_matrix() +{ +#define SIMDLIB_HAS_PARTIAL_ELEMENT(element_type) \ + has_all_active_counts(std::make_index_sequence::element_count - 1>{}) + return SIMDLIB_HAS_PARTIAL_ELEMENT(std::int8_t) && SIMDLIB_HAS_PARTIAL_ELEMENT(std::uint8_t) && SIMDLIB_HAS_PARTIAL_ELEMENT(std::int16_t) && + SIMDLIB_HAS_PARTIAL_ELEMENT(std::uint16_t) && SIMDLIB_HAS_PARTIAL_ELEMENT(std::int32_t) && SIMDLIB_HAS_PARTIAL_ELEMENT(std::uint32_t) && + SIMDLIB_HAS_PARTIAL_ELEMENT(std::int64_t) && SIMDLIB_HAS_PARTIAL_ELEMENT(std::uint64_t) && SIMDLIB_HAS_PARTIAL_ELEMENT(float) && + SIMDLIB_HAS_PARTIAL_ELEMENT(double); +#undef SIMDLIB_HAS_PARTIAL_ELEMENT +} + +} // namespace + +/** @brief Verifies exact-extent construction, transfer, and observation for every 128-bit partial geometry. */ +TEST_CASE("PartialRegister construction and transfer preserve every 128-bit partial extent", "[PARTIAL_REGISTER][SSE42]") +{ + REQUIRE(has_construction_transfer_matrix<128>()); +} + +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 +/** @brief Verifies exact-extent construction, transfer, and observation for every permitted 256-bit partial geometry. */ +TEST_CASE("PartialRegister construction and transfer preserve every permitted 256-bit partial extent", "[PARTIAL_REGISTER][AVX2]") +{ + REQUIRE(has_construction_transfer_matrix<256>()); +} +#endif diff --git a/tests/PartialRegisterObjectModel.tests.cpp b/tests/PartialRegisterObjectModel.tests.cpp new file mode 100644 index 0000000..4471955 --- /dev/null +++ b/tests/PartialRegisterObjectModel.tests.cpp @@ -0,0 +1,200 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include + +#ifndef SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 +#define SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 SIMDLIB_HAS_AVX2 +#endif + +namespace +{ + +/** @brief Produces one nonzero value suitable for a PartialRegister test lane. */ +template [[nodiscard]] constexpr element_t nonzero_lane_value() noexcept +{ + if constexpr (std::is_floating_point_v) + return static_cast(7.25); + else + return static_cast(7); +} + +/** @brief Reports whether every byte in one scalar representation is zero. */ +template [[nodiscard]] constexpr bool has_all_zero_bits(const element_t value) noexcept +{ + const auto bytes = std::bit_cast>(value); + return std::all_of(bytes.begin(), bytes.end(), [](const std::byte byte) constexpr noexcept { return byte == std::byte{}; }); +} + +/** @brief Produces the canonical all-bits-one predicate representation for one lane geometry. */ +template [[nodiscard]] constexpr element_t all_one_lane_value() noexcept +{ + std::array bytes{}; + for (auto &byte : bytes) + byte = std::byte{0xff}; + return std::bit_cast(bytes); +} + +/** @brief Verifies native import sanitization and zero construction for one partial extent. */ +template void require_native_boundary_contract() +{ + using value_t = SimdLib::PartialRegister; + using api_t = typename value_t::api_type; + + std::array source{}; + for (std::size_t lane = 0; lane < source.size(); ++lane) + source[lane] = static_cast(nonzero_lane_value() + static_cast(lane)); + + const auto imported = value_t::from_native(api_t::construct(source)); + const auto imported_lanes = api_t::to_array(imported.to_native()); + for (std::size_t lane = 0; lane < value_t::lane_count; ++lane) + REQUIRE(imported_lanes[lane] == source[lane]); + for (std::size_t lane = value_t::lane_count; lane < value_t::native_lane_count; ++lane) + REQUIRE(has_all_zero_bits(imported_lanes[lane])); + + const auto default_lanes = api_t::to_array(value_t{}.to_native()); + const auto zero_lanes = api_t::to_array(value_t::zero().to_native()); + for (std::size_t lane = 0; lane < value_t::native_lane_count; ++lane) + { + REQUIRE(has_all_zero_bits(default_lanes[lane])); + REQUIRE(has_all_zero_bits(zero_lanes[lane])); + } +} + +/** @brief Verifies the native boundary for the smallest and largest partial extents of one geometry. */ +template void require_native_boundary_extremes() +{ + using api_t = SimdLib::Api; + constexpr std::size_t first_active_lane_count = bits == 256 ? 128 / (sizeof(element_t) * 8) + 1 : 1; + require_native_boundary_contract(); + require_native_boundary_contract(); +} + +/** @brief Verifies active-only predicate reductions, composition, selection, and suffix sanitization. */ +template void require_mask_contract() +{ + using mask_t = SimdLib::PartialRegisterMask; + using value_t = typename mask_t::register_type; + using api_t = typename mask_t::api_type; + std::array true_lanes{}; + std::array false_lanes{}; + std::array mixed_lanes{}; + std::array true_values{}; + std::array false_values{}; + for (std::size_t lane = 0; lane < mask_t::native_lane_count; ++lane) + { + true_lanes[lane] = all_one_lane_value(); + false_lanes[lane] = element_t{}; + mixed_lanes[lane] = lane % 2 == 0 ? all_one_lane_value() : element_t{}; + true_values[lane] = static_cast(10 + lane); + false_values[lane] = static_cast(20 + lane); + } + const auto all_true = mask_t::from_native(api_t::construct(true_lanes)); + const auto all_false = mask_t::from_native(api_t::construct(false_lanes)); + const auto mixed = mask_t::from_native(api_t::construct(mixed_lanes)); + REQUIRE(all_true.any()); + REQUIRE(all_true.all()); + REQUIRE_FALSE(all_true.none()); + REQUIRE(all_true.bits() == (typename mask_t::bits_type{1} << mask_t::lane_count) - 1); + REQUIRE_FALSE(all_false.any()); + REQUIRE_FALSE(all_false.all()); + REQUIRE(all_false.none()); + REQUIRE((all_true & all_false).none()); + REQUIRE((all_true | all_false).all()); + REQUIRE((all_true ^ all_true).none()); + REQUIRE((~all_true).none()); + REQUIRE((~all_false).all()); + const auto imported_lanes = api_t::to_array(all_true.to_native()); + for (std::size_t lane = mask_t::lane_count; lane < mask_t::native_lane_count; ++lane) + REQUIRE(has_all_zero_bits(imported_lanes[lane])); + + const auto selected = all_true.select(value_t::from_native(api_t::construct(true_values)), value_t::from_native(api_t::construct(false_values))); + const auto mixed_selected = mixed.select(value_t::from_native(api_t::construct(true_values)), value_t::from_native(api_t::construct(false_values))); + const auto selected_lanes = api_t::to_array(selected.to_native()); + const auto mixed_selected_lanes = api_t::to_array(mixed_selected.to_native()); + const auto predicate_lanes = api_t::to_array((~all_true).to_native()); + for (std::size_t lane = 0; lane < mask_t::lane_count; ++lane) + { + REQUIRE(selected_lanes[lane] == true_values[lane]); + REQUIRE(mixed_selected_lanes[lane] == (lane % 2 == 0 ? true_values[lane] : false_values[lane])); + } + for (std::size_t lane = mask_t::lane_count; lane < mask_t::native_lane_count; ++lane) + { + REQUIRE(has_all_zero_bits(selected_lanes[lane])); + REQUIRE(has_all_zero_bits(predicate_lanes[lane])); + } +} + +/** @brief Verifies mask behavior at the smallest and largest partial extents of one geometry. */ +template void require_mask_contract_extremes() +{ + using api_t = SimdLib::Api; + constexpr std::size_t first_active_lane_count = bits == 256 ? 128 / (sizeof(element_t) * 8) + 1 : 1; + require_mask_contract(); + require_mask_contract(); +} + +/** @brief Verifies every non-complete predicate extent for one element and native width. */ +template void require_mask_contracts(std::index_sequence) +{ + ( + []() + { + if constexpr (SimdLib::PartialRegisterAvailable) + require_mask_contract(); + }.template operator()(), + ...); +} + +/** @brief Verifies all supported predicate extents for one element and native width. */ +template void require_all_mask_contracts() +{ + require_mask_contracts(std::make_index_sequence::element_count - 1>{}); +} + +/** @brief Runs the native-boundary matrix for all supported scalar interpretations at one width. */ +template void require_native_boundary_matrix() +{ + require_native_boundary_extremes(); + require_native_boundary_extremes(); + require_native_boundary_extremes(); + require_native_boundary_extremes(); + require_native_boundary_extremes(); + require_native_boundary_extremes(); + require_native_boundary_extremes(); + require_native_boundary_extremes(); + require_native_boundary_extremes(); + require_native_boundary_extremes(); + require_all_mask_contracts(); + require_all_mask_contracts(); + require_all_mask_contracts(); + require_all_mask_contracts(); + require_all_mask_contracts(); + require_all_mask_contracts(); + require_all_mask_contracts(); + require_all_mask_contracts(); + require_all_mask_contracts(); + require_all_mask_contracts(); +} + +} // namespace + +/** @brief Verifies PartialRegister native boundaries for the 128-bit SIMD profile. */ +TEST_CASE("PartialRegister native boundaries preserve the inactive suffix", "[PARTIAL_REGISTER][SSE42]") +{ + require_native_boundary_matrix<128>(); +} + +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 +/** @brief Verifies PartialRegister native boundaries for the 256-bit SIMD profile. */ +TEST_CASE("PartialRegister native boundaries preserve the inactive suffix at 256 bits", "[PARTIAL_REGISTER][AVX2]") +{ + require_native_boundary_matrix<256>(); +} +#endif diff --git a/tests/PartialRegisterOperationMatrix.tests.cpp b/tests/PartialRegisterOperationMatrix.tests.cpp new file mode 100644 index 0000000..d154c46 --- /dev/null +++ b/tests/PartialRegisterOperationMatrix.tests.cpp @@ -0,0 +1,218 @@ +#include +#include + +#include +#include +#include +#include +#include + +#ifndef SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 +#define SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 SIMDLIB_HAS_AVX2 +#endif + +namespace +{ + +/** @brief Reports whether the adjacent-result alias accepts one source geometry. */ +template +concept has_partial_adjacent_alias = requires { typename SimdLib::partial_multiply_add_adjacent_result_t; }; + +/** @brief Reports whether the byte-multiply-add result alias accepts one source geometry. */ +template +concept has_partial_byte_multiply_add_alias = requires { typename SimdLib::partial_byte_multiply_add_result_t; }; + +/** @brief Reports whether the SAD result alias accepts one source geometry. */ +template +concept has_partial_sad_alias = requires { typename SimdLib::partial_sad_result_t; }; + +/** @brief Reports whether the checked-magnitude result alias accepts one source geometry. */ +template +concept has_partial_checked_magnitude_alias = requires { typename SimdLib::partial_magnitude_checked_result_t; }; + +/** @brief Selects the independently expected complete result when a useful partial target cannot be formed. */ +template ::element_count || (bits == 256 && result_lane_count * sizeof(element_t) * 8 <= 128)> +struct expected_partial_result; + +/** @brief Provides the expected complete-register result spelling. */ +template struct expected_partial_result +{ + using type = SimdLib::Register; +}; + +/** @brief Provides the expected useful partial-register result spelling. */ +template struct expected_partial_result +{ + using type = SimdLib::PartialRegister; +}; + +/** @brief Expected public result type for one meaningful contiguous result extent. */ +template +using expected_partial_result_t = typename expected_partial_result::type; + +/** @brief Audits arithmetic and specialized-operation availability for one valid geometry. */ +template [[nodiscard]] consteval bool has_complete_arithmetic_surface() noexcept +{ + using value_t = SimdLib::PartialRegister; + using api_t = typename value_t::api_type; + using other_t = std::conditional_t, std::uint8_t, std::int8_t>; + + static_assert(SimdLib::IRegister::Add == SimdLib::IApi::Add); + static_assert(SimdLib::IRegister::Subtract == SimdLib::IApi::Subtract); + static_assert(SimdLib::IRegister::Multiply == SimdLib::IApi::Multiply); + static_assert(SimdLib::IRegister::Divide == SimdLib::IApi::Divide); + static_assert(SimdLib::IRegister::Modulus == SimdLib::IApi::Modulus); + static_assert(SimdLib::IRegister::Negate == SimdLib::IApi::Negate); + static_assert(SimdLib::IRegister::Min == SimdLib::IApi::Min); + static_assert(SimdLib::IRegister::Max == SimdLib::IApi::Max); + static_assert(SimdLib::IRegister::Absolute == SimdLib::IApi::Absolute); + static_assert(SimdLib::IRegister::Sqrt == SimdLib::IApi::Sqrt); + static_assert(SimdLib::IRegister::Average == SimdLib::IApi::Average); + static_assert(SimdLib::IRegister::MultiplyAdd == SimdLib::IApi::MultiplyAdd); + static_assert(SimdLib::IRegister::Magnitude == SimdLib::IApi::Magnitude); + constexpr bool has_magnitude_checked = requires(value_t value) { value.magnitude_checked(); }; + static_assert(has_magnitude_checked == SimdLib::IApi::MagnitudeChecked); + static_assert(SimdLib::IRegister::Normalize == SimdLib::IApi::Normalize); + static_assert(SimdLib::IRegister::HorizontalAdd == SimdLib::IApi::HorizontalAdd); + static_assert(SimdLib::IRegister::HorizontalSubtract == SimdLib::IApi::HorizontalSubtract); + static_assert(SimdLib::IRegister::MultiplyAddAdjacent == SimdLib::IApi::MultiplyAddAdjacent); + static_assert(SimdLib::IRegister::MultiplyAddUnsignedSignedBytes == SimdLib::IApi::ByteMultiplyAdd); + static_assert(SimdLib::IRegister::SumAbsoluteByteDifferences == SimdLib::IApi::Sad); + static_assert(SimdLib::IRegister::MultiSumAbsoluteByteDifferences == SimdLib::IApi::MultiSad); + static_assert(SimdLib::IRegister::MultiSumAbsoluteByteDifferences == SimdLib::IApi::MultiSad); + static_assert(SimdLib::IRegister::MinPosition == SimdLib::IApi::MinPosition); + static_assert(SimdLib::IRegister::MaxPosition == SimdLib::IApi::MaxPosition); + static_assert(SimdLib::IRegister::AddSaturated == SimdLib::IApi::AddSaturated); + static_assert(SimdLib::IRegister::SubtractSaturated == SimdLib::IApi::SubtractSaturated); + static_assert(SimdLib::IRegister::HorizontalAddSaturated == SimdLib::IApi::HorizontalAddSaturated); + static_assert(SimdLib::IRegister::HorizontalSubtractSaturated == SimdLib::IApi::HorizontalSubtractSaturated); + static_assert(SimdLib::IRegister::AddSubtract == SimdLib::IApi::AddSubtract); + static_assert(SimdLib::IRegister::DotProduct == SimdLib::IApi::DotProduct); + static_assert(!SimdLib::IRegister::DotProduct); + static_assert(!SimdLib::IRegister::DotProduct); + static_assert(!SimdLib::IRegister::MultiSumAbsoluteByteDifferences); + static_assert(!SimdLib::IRegister::MultiSumAbsoluteByteDifferences); + static_assert(!SimdLib::IRegister::MultiplyAddAdjacent); + static_assert(!SimdLib::IRegister::MultiplyAddUnsignedSignedBytes); + static_assert(!SimdLib::IRegister::SumAbsoluteByteDifferences); + static_assert(!SimdLib::IRegister::MultiSumAbsoluteByteDifferences); + return true; +} + +/** @brief Audits the last invalid and first valid 256-bit active extents for one element type. */ +template [[nodiscard]] consteval bool has_upper_group_geometry_boundary() noexcept +{ + constexpr std::size_t low_group_lane_count = 128 / (sizeof(element_t) * 8); + static_assert(!SimdLib::PartialRegisterAvailable); + static_assert(SimdLib::PartialRegisterAvailable); + return true; +} + +/** @brief Audits exact specialized-result aliases for one valid integral source geometry. */ +template [[nodiscard]] consteval bool has_exact_specialized_results() noexcept +{ + using value_t = SimdLib::PartialRegister; + using api_t = typename value_t::api_type; + if constexpr (SimdLib::IApi::MultiplyAddAdjacent) + { + using result_element_t = SimdLib::partial_multiply_add_adjacent_element_t; + using expected_t = expected_partial_result_t; + static_assert(std::same_as, expected_t>); + static_assert(std::same_as().multiply_add_adjacent(std::declval())), expected_t>); + } + if constexpr (SimdLib::IApi::ByteMultiplyAdd) + { + constexpr std::size_t result_lane_count = (active_lane_count * sizeof(element_t) + 1) / 2; + using expected_t = expected_partial_result_t; + static_assert(std::same_as, expected_t>); + static_assert(std::same_as().multiply_add_unsigned_signed_bytes(std::declval())), expected_t>); + } + if constexpr (SimdLib::IApi::Sad) + { + constexpr std::size_t result_lane_count = (active_lane_count * sizeof(element_t) + 7) / 8; + using expected_t = expected_partial_result_t; + static_assert(std::same_as, expected_t>); + static_assert(std::same_as().sum_absolute_byte_differences(std::declval())), expected_t>); + } + if constexpr (SimdLib::IApi::MagnitudeChecked) + { + constexpr std::size_t group_lane_count = 128 / (sizeof(element_t) * 8); + constexpr std::size_t result_lane_count = ((active_lane_count - 1) / group_lane_count) * group_lane_count + 2; + using expected_t = expected_partial_result_t; + static_assert(std::same_as, expected_t>); + static_assert(std::same_as().magnitude_checked()), expected_t>); + } + return true; +} + +/** @brief Audits every valid active extent for one integral element/width cell. */ +template +[[nodiscard]] consteval bool has_all_exact_specialized_results(std::index_sequence) noexcept +{ + return ( + []() consteval + { + if constexpr (SimdLib::PartialRegisterAvailable) + return has_exact_specialized_results(); + else + return true; + }.template operator()() && + ...); +} + +/** @brief Audits every valid specialized-result geometry for all integral element types at one width. */ +template [[nodiscard]] consteval bool has_exact_specialized_result_matrix() noexcept +{ +#define SIMDLIB_PARTIAL_EXACT_RESULTS(element_type) \ + has_all_exact_specialized_results(std::make_index_sequence::element_count - 1>{}) + return SIMDLIB_PARTIAL_EXACT_RESULTS(std::int8_t) && SIMDLIB_PARTIAL_EXACT_RESULTS(std::uint8_t) && SIMDLIB_PARTIAL_EXACT_RESULTS(std::int16_t) && + SIMDLIB_PARTIAL_EXACT_RESULTS(std::uint16_t) && SIMDLIB_PARTIAL_EXACT_RESULTS(std::int32_t) && SIMDLIB_PARTIAL_EXACT_RESULTS(std::uint32_t) && + SIMDLIB_PARTIAL_EXACT_RESULTS(std::int64_t) && SIMDLIB_PARTIAL_EXACT_RESULTS(std::uint64_t); +#undef SIMDLIB_PARTIAL_EXACT_RESULTS +} + +#define SIMDLIB_PARTIAL_VALIDATE_SURFACE(element_type, width, count) static_assert(has_complete_arithmetic_surface()) +SIMDLIB_PARTIAL_VALIDATE_SURFACE(std::int8_t, 128, 15); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(std::uint8_t, 128, 15); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(std::int16_t, 128, 7); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(std::uint16_t, 128, 7); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(std::int32_t, 128, 3); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(std::uint32_t, 128, 3); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(std::int64_t, 128, 1); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(std::uint64_t, 128, 1); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(float, 128, 3); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(double, 128, 1); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 +SIMDLIB_PARTIAL_VALIDATE_SURFACE(std::int8_t, 256, 17); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(std::uint8_t, 256, 17); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(std::int16_t, 256, 9); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(std::uint16_t, 256, 9); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(std::int32_t, 256, 5); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(std::uint32_t, 256, 5); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(std::int64_t, 256, 3); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(std::uint64_t, 256, 3); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(float, 256, 5); +SIMDLIB_PARTIAL_VALIDATE_SURFACE(double, 256, 3); +#endif +#undef SIMDLIB_PARTIAL_VALIDATE_SURFACE + +static_assert(has_exact_specialized_result_matrix<128>()); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 +static_assert(has_exact_specialized_result_matrix<256>()); +static_assert(has_upper_group_geometry_boundary() && has_upper_group_geometry_boundary()); +static_assert(has_upper_group_geometry_boundary() && has_upper_group_geometry_boundary()); +static_assert(has_upper_group_geometry_boundary() && has_upper_group_geometry_boundary()); +static_assert(has_upper_group_geometry_boundary() && has_upper_group_geometry_boundary()); +static_assert(has_upper_group_geometry_boundary() && has_upper_group_geometry_boundary()); +#endif + +static_assert(!has_partial_checked_magnitude_alias); +static_assert(!has_partial_checked_magnitude_alias); +static_assert(!has_partial_adjacent_alias); +static_assert(!has_partial_byte_multiply_add_alias); +static_assert(!has_partial_sad_alias); +static_assert(!has_partial_checked_magnitude_alias); + +} // namespace diff --git a/tests/PartialRegisterRearrangementConversion.tests.cpp b/tests/PartialRegisterRearrangementConversion.tests.cpp new file mode 100644 index 0000000..b47a697 --- /dev/null +++ b/tests/PartialRegisterRearrangementConversion.tests.cpp @@ -0,0 +1,432 @@ +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 +#define SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 SIMDLIB_HAS_AVX2 +#endif + +namespace +{ + +/** @brief Reports whether one scalar has the all-bits-zero representation. */ +template [[nodiscard]] bool has_zero_bits(element_t value) noexcept +{ + const auto bytes = std::bit_cast>(value); + for (const auto byte : bytes) + if (byte != std::byte{}) + return false; + return true; +} + +/** @brief Requires every native lane beyond a result's logical prefix to remain bitwise zero. */ +template void require_zero_suffix(value_t value) +{ + if constexpr (value_t::lane_count < value_t::api_type::element_count) + { + const auto lanes = value_t::api_type::to_array(value.to_native()); + for (std::size_t lane = value_t::lane_count; lane < value_t::api_type::element_count; ++lane) + REQUIRE(has_zero_bits(lanes[lane])); + } +} + +/** @brief Builds distinctive active lanes for one PartialRegister specialization. */ +template [[nodiscard]] constexpr std::array make_active_lanes() noexcept +{ + using element_t = typename value_t::element_type; + std::array result{}; + for (std::size_t lane = 0; lane < result.size(); ++lane) + result[lane] = static_cast(lane * 5 + 1); + return result; +} + +/** @brief Reports whether one partial type accepts its exact active identity selector list. */ +template [[nodiscard]] consteval bool has_active_shuffle() noexcept +{ + return [](std::index_sequence) consteval + { return SimdLib::IRegister::Shuffle; }(std::make_index_sequence{}); +} + +/** @brief Reports whether one partial type accepts its exact active-byte identity selector list. */ +template [[nodiscard]] consteval bool has_active_byte_shuffle() noexcept +{ + return [](std::index_sequence) consteval + { return SimdLib::IRegister::ShuffleBytes; }(std::make_index_sequence{}); +} + +/** @brief Verifies rearrangement surface parity for one partial geometry. */ +template [[nodiscard]] consteval bool has_rearrangement_surface_parity() noexcept +{ + using value_t = SimdLib::PartialRegister; + using api_t = typename value_t::api_type; + static_assert(SimdLib::IRegister::LowerHalf == (bits == 256 && SimdLib::IApi::LowerHalf)); + static_assert(SimdLib::IRegister::UnpackLow == (SimdLib::IApi::UnpackLow && SimdLib::IApi::BitwiseAnd)); + static_assert(SimdLib::IRegister::UnpackHigh == SimdLib::IApi::UnpackHigh); + static_assert(has_active_shuffle()); + static_assert(has_active_byte_shuffle()); + static_assert(SimdLib::IRegister::ShuffleLow == (SimdLib::IApi::ShuffleLow && SimdLib::IApi::BitwiseAnd)); + static_assert(SimdLib::IRegister::ShuffleHigh == (SimdLib::IApi::ShuffleHigh && SimdLib::IApi::BitwiseAnd)); + static_assert(SimdLib::IRegister::Blend == SimdLib::IApi::Blend); + return true; +} + +#define SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(element_type, width, count) static_assert(has_rearrangement_surface_parity()) +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(std::int8_t, 128, 13); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(std::uint8_t, 128, 13); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(std::int16_t, 128, 5); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(std::uint16_t, 128, 5); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(std::int32_t, 128, 3); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(std::uint32_t, 128, 3); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(std::int64_t, 128, 1); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(std::uint64_t, 128, 1); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(float, 128, 3); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(double, 128, 1); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(std::int8_t, 256, 19); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(std::uint8_t, 256, 19); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(std::int16_t, 256, 11); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(std::uint16_t, 256, 11); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(std::int32_t, 256, 5); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(std::uint32_t, 256, 5); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(std::int64_t, 256, 3); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(std::uint64_t, 256, 3); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(float, 256, 5); +SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE(double, 256, 3); +#endif +#undef SIMDLIB_PARTIAL_REARRANGEMENT_SURFACE + +using I8x13 = SimdLib::PartialRegister; +using U8x8 = SimdLib::PartialRegister; +using I16x5 = SimdLib::PartialRegister; +using I32x1 = SimdLib::PartialRegister; +using I32x3 = SimdLib::PartialRegister; +using U32x3 = SimdLib::PartialRegister; +using F32x3 = SimdLib::PartialRegister; + +static_assert(std::same_as, SimdLib::PartialRegister>); +static_assert(SimdLib::IRegister::BitCast && SimdLib::IRegister::BitCast && + SimdLib::IRegister::BitCast && SimdLib::IRegister::BitCast); +static_assert(!SimdLib::IRegister::BitCast, std::uint32_t>); +static_assert(SimdLib::IRegister::Convert && SimdLib::IRegister::Convert && SimdLib::IRegister::Convert); +static_assert(!SimdLib::IRegister::Convert && !SimdLib::IRegister::Convert); +static_assert(std::same_as, SimdLib::Register>); +static_assert(SimdLib::IRegister::WidenLow); +static_assert(SimdLib::IRegister::WidenLow); +static_assert(!SimdLib::IRegister::WidenLow); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 +static_assert(std::same_as, SimdLib::Register>); +static_assert(SimdLib::IRegister::WidenLow); +static_assert(!SimdLib::IRegister::WidenLow); +#endif + +/** @brief Computes an integer power for compile-time exhaustive selector enumeration. */ +[[nodiscard]] consteval std::size_t integer_power(std::size_t base, std::size_t exponent) noexcept +{ + std::size_t result = 1; + for (std::size_t index = 0; index < exponent; ++index) + result *= base; + return result; +} + +/** @brief Selects one base-N digit from an exhaustive selector-case index. */ +template [[nodiscard]] consteval std::size_t selector_digit() noexcept +{ + return (code / integer_power(base, position)) % base; +} + +/** @brief Verifies one logical-lane shuffle case against its independently decoded selector digits. */ +template +void require_logical_shuffle_case(value_t value, const std::array &source, + std::index_sequence) +{ + constexpr std::array selectors{selector_digit()...}; + const auto actual = value.template shuffle()...>().to_array(); + for (std::size_t lane = 0; lane < actual.size(); ++lane) + REQUIRE(actual[lane] == source[selectors[lane]]); + require_zero_suffix(value.template shuffle()...>()); +} + +/** @brief Instantiates every logical-lane selector sequence for one small PartialRegister geometry. */ +template void require_all_logical_shuffle_cases(std::index_sequence) +{ + const auto source = make_active_lanes(); + const auto value = value_t::from_array(source); + (require_logical_shuffle_case(value, source, std::make_index_sequence{}), ...); +} + +/** @brief Verifies one byte-shuffle case against its independently decoded selector digits. */ +template +void require_byte_shuffle_case(value_t value, const std::array &source, std::index_sequence) +{ + constexpr std::array selectors{selector_digit()...}; + const auto shuffled = value.template shuffle_bytes()...>(); + std::array actual{}; + shuffled.store_bytes(actual); + for (std::size_t byte = 0; byte < actual.size(); ++byte) + REQUIRE(std::to_integer(actual[byte]) == source[selectors[byte]]); + require_zero_suffix(shuffled); +} + +/** @brief Instantiates every byte selector sequence for one small PartialRegister geometry. */ +template void require_all_byte_shuffle_cases(std::index_sequence) +{ + static_assert(std::same_as); + const auto source = make_active_lanes(); + const auto value = value_t::from_array(source); + (require_byte_shuffle_case(value, source, std::make_index_sequence{}), ...); +} + +/** @brief Verifies projected unpack semantics for one odd or intrinsic-subgroup-sized logical prefix. */ +template void require_unpack_contract() +{ + using value_t = SimdLib::PartialRegister; + constexpr std::size_t group_lane_count = 128 / (sizeof(element_t) * 8); + constexpr std::size_t half_lane_count = group_lane_count / 2; + const auto lhs_values = make_active_lanes(); + auto rhs_values = make_active_lanes(); + for (auto &lane : rhs_values) + lane = static_cast(lane + static_cast(37)); + std::array lhs_native{}; + std::array rhs_native{}; + for (std::size_t lane = 0; lane < active_count; ++lane) + { + lhs_native[lane] = lhs_values[lane]; + rhs_native[lane] = rhs_values[lane]; + } + std::array expected_low{}; + std::array expected_high{}; + for (std::size_t group = 0; group < value_t::native_lane_count; group += group_lane_count) + for (std::size_t lane = 0; lane < half_lane_count; ++lane) + { + expected_low[group + lane * 2] = lhs_native[group + lane]; + expected_low[group + lane * 2 + 1] = rhs_native[group + lane]; + expected_high[group + lane * 2] = lhs_native[group + half_lane_count + lane]; + expected_high[group + lane * 2 + 1] = rhs_native[group + half_lane_count + lane]; + } + const auto lhs = value_t::from_array(lhs_values); + const auto rhs = value_t::from_array(rhs_values); + const auto low = lhs.unpack_low(rhs); + const auto high = lhs.unpack_high(rhs); + for (std::size_t lane = 0; lane < active_count; ++lane) + { + REQUIRE(low.to_array()[lane] == expected_low[lane]); + REQUIRE(high.to_array()[lane] == expected_high[lane]); + } + require_zero_suffix(low); + require_zero_suffix(high); +} + +/** @brief Verifies projected low/high four-lane shuffle semantics for one 16-bit logical prefix. */ +template void require_half_shuffle_contract() +{ + using value_t = SimdLib::PartialRegister; + constexpr int immediate = 0x1b; + constexpr std::size_t group_lane_count = 8; + const auto source = make_active_lanes(); + std::array native_source{}; + for (std::size_t lane = 0; lane < source.size(); ++lane) + native_source[lane] = source[lane]; + auto expected_low = native_source; + auto expected_high = native_source; + for (std::size_t group = 0; group < value_t::native_lane_count; group += group_lane_count) + for (std::size_t lane = 0; lane < 4; ++lane) + { + expected_low[group + lane] = native_source[group + 3 - lane]; + expected_high[group + 4 + lane] = native_source[group + 7 - lane]; + } + const auto value = value_t::from_array(source); + const auto low = value.template shuffle_low(); + const auto high = value.template shuffle_high(); + for (std::size_t lane = 0; lane < active_count; ++lane) + { + REQUIRE(low.to_array()[lane] == expected_low[lane]); + REQUIRE(high.to_array()[lane] == expected_high[lane]); + } + require_zero_suffix(low); + require_zero_suffix(high); +} + +/** @brief Verifies immediate blend selection and suffix closure for one supported partial geometry. */ +template void require_blend_contract() +{ + using value_t = SimdLib::PartialRegister; + constexpr int immediate = 0xa5; + const auto lhs_values = make_active_lanes(); + auto rhs_values = make_active_lanes(); + for (auto &lane : rhs_values) + lane = static_cast(lane + static_cast(53)); + const auto actual = value_t::from_array(lhs_values).template blend(value_t::from_array(rhs_values)); + for (std::size_t lane = 0; lane < active_count; ++lane) + REQUIRE(actual.to_array()[lane] == (((immediate >> (lane % 8)) & 1) != 0 ? rhs_values[lane] : lhs_values[lane])); + require_zero_suffix(actual); +} + +/** @brief Verifies one bit-cast matrix cell preserves exactly the active source bytes. */ +template void require_bit_cast_contract() +{ + using source_t = SimdLib::PartialRegister; + const auto source_values = make_active_lanes(); + const auto source = source_t::from_array(source_values); + const auto result = source.template bit_cast(); + using result_t = std::remove_cvref_t; + static_assert(result_t::lane_count == active_count / sizeof(target_t)); + std::array actual{}; + result.store_bytes(actual); + for (std::size_t byte = 0; byte < active_count; ++byte) + REQUIRE(std::to_integer(actual[byte]) == source_values[byte]); + require_zero_suffix(result); +} + +/** @brief Verifies one supported numeric conversion cell over active lanes only. */ +template void require_conversion_contract() +{ + using value_t = SimdLib::PartialRegister; + std::array source{}; + for (std::size_t lane = 0; lane < active_count; ++lane) + { + if constexpr (std::is_unsigned_v) + source[lane] = static_cast(lane + 1); + else + source[lane] = static_cast(static_cast(lane) - 2); + } + const auto result = value_t::from_array(source).template convert(); + using result_t = std::remove_cvref_t; + static_assert(result_t::lane_count == active_count); + for (std::size_t lane = 0; lane < active_count; ++lane) + REQUIRE(result.to_array()[lane] == static_cast(source[lane])); + require_zero_suffix(result); +} + +/** @brief Verifies one widening cell consumes and exposes exactly its documented active source prefix. */ +template void require_widen_contract() +{ + using value_t = SimdLib::PartialRegister; + std::array source{}; + for (std::size_t lane = 0; lane < active_count; ++lane) + source[lane] = static_cast(lane + 1); + const auto result = value_t::from_array(source).template widen_low(); + using result_t = std::remove_cvref_t; + constexpr std::size_t expected_count = active_count < result_t::api_type::element_count ? active_count : result_t::api_type::element_count; + static_assert(result_t::lane_count == expected_count); + for (std::size_t lane = 0; lane < expected_count; ++lane) + REQUIRE(result.to_array()[lane] == static_cast(source[lane])); + require_zero_suffix(result); +} + +TEST_CASE("PartialRegister exhaustively shuffles every selector sequence for three logical lanes", "[PartialRegister][Rearrangement]") +{ + using value_t = SimdLib::PartialRegister; + require_all_logical_shuffle_cases(std::make_index_sequence{}); +} + +TEST_CASE("PartialRegister exhaustively shuffles every selector sequence for three active bytes", "[PartialRegister][Rearrangement]") +{ + using value_t = SimdLib::PartialRegister; + require_all_byte_shuffle_cases(std::make_index_sequence{}); +} + +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 +TEST_CASE("PartialRegister logical shuffles cross the 128-bit boundary without exposing padding", "[PartialRegister][Rearrangement]") +{ + using lane_value_t = SimdLib::PartialRegister; + const auto lane_source = make_active_lanes(); + const auto lanes = lane_value_t::from_array(lane_source).template shuffle<4, 3, 2, 1, 0>(); + REQUIRE(lanes.to_array() == std::array{lane_source[4], lane_source[3], lane_source[2], lane_source[1], lane_source[0]}); + require_zero_suffix(lanes); + using byte_value_t = SimdLib::PartialRegister; + const auto byte_source = make_active_lanes(); + const auto bytes = byte_value_t::from_array(byte_source).template shuffle_bytes<18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0>(); + for (std::size_t byte = 0; byte < byte_source.size(); ++byte) + REQUIRE(bytes.to_array()[byte] == byte_source[18 - byte]); + require_zero_suffix(bytes); +} + +TEST_CASE("PartialRegister lower-half returns the complete meaningful low half", "[PartialRegister][Rearrangement]") +{ + using value_t = SimdLib::PartialRegister; + const auto source = make_active_lanes(); + const auto result = value_t::from_array(source).lower_half(); + static_assert(std::same_as, SimdLib::Register>); + REQUIRE(result.to_array() == std::array{source[0], source[1], source[2], source[3]}); +} +#endif + +TEST_CASE("PartialRegister unpack operations project odd and undersized intrinsic groups", "[PartialRegister][Rearrangement]") +{ + require_unpack_contract(); + require_unpack_contract(); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 + require_unpack_contract(); + require_unpack_contract(); +#endif +} + +TEST_CASE("PartialRegister half-local shuffles project unavailable source lanes as zero", "[PartialRegister][Rearrangement]") +{ + require_half_shuffle_contract<128, 3>(); + require_half_shuffle_contract<128, 5>(); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 + require_half_shuffle_contract<256, 9>(); +#endif +} + +TEST_CASE("PartialRegister blends retain intrinsic mask semantics over active lanes", "[PartialRegister][Rearrangement]") +{ + require_blend_contract(); + require_blend_contract(); + require_blend_contract(); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 + require_blend_contract(); + require_blend_contract(); +#endif +} + +TEST_CASE("PartialRegister bit casts preserve the exact active bit extent", "[PartialRegister][Conversion]") +{ + require_bit_cast_contract<128, 8, std::uint16_t>(); + require_bit_cast_contract<128, 8, std::uint32_t>(); + require_bit_cast_contract<128, 8, float>(); + require_bit_cast_contract<128, 8, double>(); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 + require_bit_cast_contract<256, 24, std::uint16_t>(); + require_bit_cast_contract<256, 24, std::uint32_t>(); + require_bit_cast_contract<256, 24, float>(); + require_bit_cast_contract<256, 24, double>(); +#endif +} + +TEST_CASE("PartialRegister numeric conversion retains exactly one output per active input", "[PartialRegister][Conversion]") +{ + require_conversion_contract(); + require_conversion_contract(); + require_conversion_contract(); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 + require_conversion_contract(); + require_conversion_contract(); + require_conversion_contract(); +#endif +} + +TEST_CASE("PartialRegister widening exposes exactly the consumed source prefix", "[PartialRegister][Conversion]") +{ + require_widen_contract(); + require_widen_contract(); + require_widen_contract(); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 + require_widen_contract(); + require_widen_contract(); + require_widen_contract(); +#endif +} + +} // namespace diff --git a/tests/PartialRegisterSpecializedOperations.tests.cpp b/tests/PartialRegisterSpecializedOperations.tests.cpp new file mode 100644 index 0000000..ebc2c54 --- /dev/null +++ b/tests/PartialRegisterSpecializedOperations.tests.cpp @@ -0,0 +1,347 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 +#define SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 SIMDLIB_HAS_AVX2 +#endif + +namespace +{ + +/** @brief Returns the first permitted active extent for one partial-register geometry. */ +template [[nodiscard]] consteval std::size_t first_active_lane_count() noexcept +{ + if constexpr (bits == 256) + return 128 / (sizeof(element_t) * 8) + 1; + else + return 1; +} + +/** @brief Exports every physical lane from a partial or complete result. */ +template [[nodiscard]] auto native_lanes(value_t value) noexcept +{ + return value_t::api_type::to_array(value.native); +} + +/** @brief Verifies the inactive physical suffix is represented by all-bits-zero lanes. */ +template void require_zero_suffix(value_t value) +{ + const auto lanes = native_lanes(value); + for (std::size_t lane = value_t::lane_count; lane < value_t::api_type::element_count; ++lane) + { + const auto bytes = std::bit_cast>(lanes[lane]); + for (const auto byte : bytes) + REQUIRE(std::to_integer(byte) == 0U); + } +} + +/** @brief Verifies grouped adjacent multiply-add against an independent physical-lane oracle. */ +template void require_adjacent_oracle() +{ + using source_t = SimdLib::PartialRegister; + using result_t = SimdLib::partial_multiply_add_adjacent_result_t; + using result_element_t = typename result_t::element_type; + constexpr std::size_t source_group_lanes = 128 / (sizeof(element_t) * 8); + constexpr std::size_t result_group_lanes = 128 / (sizeof(result_element_t) * 8); + std::array lhs{}; + std::array rhs{}; + for (std::size_t lane = 0; lane < active_lane_count; ++lane) + { + lhs[lane] = static_cast(lane % 5 + 1); + rhs[lane] = static_cast(lane % 3 + 2); + } + const auto actual = native_lanes(source_t::from_array(lhs).multiply_add_adjacent(source_t::from_array(rhs))); + std::array expected{}; + for (std::size_t group = 0; group < bits / 128; ++group) + { + const std::size_t source_base = group * source_group_lanes; + for (std::size_t pair = 0; pair < source_group_lanes / 2; ++pair) + { + const std::size_t low = source_base + pair * 2; + const auto lhs_low = low < active_lane_count ? lhs[low] : element_t{}; + const auto rhs_low = low < active_lane_count ? rhs[low] : element_t{}; + const auto lhs_high = low + 1 < active_lane_count ? lhs[low + 1] : element_t{}; + const auto rhs_high = low + 1 < active_lane_count ? rhs[low + 1] : element_t{}; + expected[group * result_group_lanes + pair] = + static_cast(static_cast(lhs_low) * static_cast(rhs_low) + + static_cast(lhs_high) * static_cast(rhs_high)); + } + } + CAPTURE(sizeof(element_t), std::is_signed_v, bits, active_lane_count); + REQUIRE(actual == expected); +} + +/** @brief Verifies byte multiply-add, SAD, and multi-SAD against independent byte-level oracles. */ +template void require_byte_specialized_oracles() +{ + using source_t = SimdLib::PartialRegister; + using source_api_t = typename source_t::api_type; + using byte_api_t = SimdLib::Api; + constexpr std::size_t active_byte_count = active_lane_count * sizeof(element_t); + std::array lhs_bytes{}; + std::array rhs_bytes{}; + for (std::size_t byte = 0; byte < active_byte_count; ++byte) + { + lhs_bytes[byte] = static_cast(byte % 7 + 1); + rhs_bytes[byte] = static_cast(byte % 5 + 1); + } + const auto lhs = source_t::from_native(byte_api_t::template bit_cast(byte_api_t::construct(lhs_bytes))); + const auto rhs = source_t::from_native(byte_api_t::template bit_cast(byte_api_t::construct(rhs_bytes))); + + if constexpr (SimdLib::IApi::ByteMultiplyAdd) + { + const auto actual = native_lanes(lhs.multiply_add_unsigned_signed_bytes(rhs)); + std::array expected{}; + for (std::size_t pair = 0; pair < (active_byte_count + 1) / 2; ++pair) + { + const std::size_t low = pair * 2; + expected[pair] = static_cast( + static_cast(lhs_bytes[low]) * static_cast(rhs_bytes[low]) + + (low + 1 < active_byte_count ? static_cast(lhs_bytes[low + 1]) * static_cast(rhs_bytes[low + 1]) : 0)); + } + REQUIRE(actual == expected); + } + + if constexpr (SimdLib::IApi::Sad) + { + const auto actual = native_lanes(lhs.sum_absolute_byte_differences(rhs)); + std::array expected{}; + for (std::size_t group = 0; group < (active_byte_count + 7) / 8; ++group) + for (std::size_t offset = 0; offset < 8 && group * 8 + offset < active_byte_count; ++offset) + expected[group] += + static_cast(std::abs(static_cast(lhs_bytes[group * 8 + offset]) - static_cast(rhs_bytes[group * 8 + offset]))); + REQUIRE(actual == expected); + } + + if constexpr (SimdLib::IApi::MultiSad) + { + const auto actual = lhs.template multi_sum_absolute_byte_differences(rhs).to_array(); + std::array expected{}; + for (std::size_t group = 0; group < bits / 128; ++group) + { + const unsigned control = (static_cast(multi_sad_control) >> (group * 3)) & 0x7U; + const std::size_t group_base = group * 16; + const std::size_t lhs_base = group_base + ((control >> 2) & 1U) * 4; + const std::size_t rhs_base = group_base + (control & 3U) * 4; + for (std::size_t output = 0; output < 8; ++output) + for (std::size_t offset = 0; offset < 4; ++offset) + expected[group * 8 + output] += static_cast( + std::abs(static_cast(lhs_bytes[lhs_base + output + offset]) - static_cast(rhs_bytes[rhs_base + offset]))); + } + REQUIRE(actual == expected); + } +} + +/** @brief Verifies magnitude and checked-magnitude grouping with independent perfect-square inputs. */ +template void require_magnitude_oracles() +{ + using value_t = SimdLib::PartialRegister; + constexpr std::size_t group_lane_count = 128 / (sizeof(element_t) * 8); + std::array source{}; + for (std::size_t group = 0; group < bits / 128; ++group) + { + const std::size_t base = group * group_lane_count; + if (base < active_lane_count) + source[base] = static_cast(3); + if (base + 1 < active_lane_count) + source[base + 1] = static_cast(4); + } + const auto value = value_t::from_array(source); + if constexpr (requires { value.magnitude(); }) + { + const auto magnitude = value.magnitude(); + const auto physical = native_lanes(magnitude); + for (std::size_t group = 0; group < bits / 128; ++group) + { + const std::size_t group_base = group * group_lane_count; + if (group_base < active_lane_count) + REQUIRE(physical[group_base] == (group_base + 1 < active_lane_count ? static_cast(5) : static_cast(3))); + } + require_zero_suffix(magnitude); + } + if constexpr (requires { value.magnitude_checked(); }) + { + const auto checked = value.magnitude_checked(); + const auto physical = native_lanes(checked); + for (std::size_t group = 0; group < bits / 128; ++group) + { + const std::size_t source_base = group * group_lane_count; + if (source_base >= active_lane_count) + continue; + REQUIRE(physical[source_base] == (source_base + 1 < active_lane_count ? static_cast(5) : static_cast(3))); + REQUIRE(physical[source_base + 1] == element_t{}); + } + if constexpr (requires { checked.to_native(); }) + require_zero_suffix(checked); + } +} + +/** @brief Verifies intrinsic-ordered horizontal operations against a grouped scalar oracle. */ +template void require_horizontal_oracles() +{ + using value_t = SimdLib::PartialRegister; + constexpr std::size_t group_lane_count = 128 / (sizeof(element_t) * 8); + std::array lhs_values{}; + std::array rhs_values{}; + for (std::size_t lane = 0; lane < active_lane_count; ++lane) + { + lhs_values[lane] = static_cast(lane % 4 + 1); + rhs_values[lane] = static_cast(lane % 3 + 6); + } + const auto lhs = value_t::from_array(lhs_values); + const auto rhs = value_t::from_array(rhs_values); + if constexpr (requires { lhs.horizontal_add(rhs); }) + { + std::array expected_add{}; + std::array expected_subtract{}; + for (std::size_t group = 0; group < bits / 128; ++group) + { + const std::size_t base = group * group_lane_count; + for (std::size_t pair = 0; pair < group_lane_count / 2; ++pair) + { + const std::size_t low = base + pair * 2; + const auto lhs_low = low < active_lane_count ? lhs_values[low] : element_t{}; + const auto lhs_high = low + 1 < active_lane_count ? lhs_values[low + 1] : element_t{}; + const auto rhs_low = low < active_lane_count ? rhs_values[low] : element_t{}; + const auto rhs_high = low + 1 < active_lane_count ? rhs_values[low + 1] : element_t{}; + if (base + pair < active_lane_count) + { + expected_add[base + pair] = static_cast(lhs_low + lhs_high); + expected_subtract[base + pair] = static_cast(lhs_low - lhs_high); + } + if (base + group_lane_count / 2 + pair < active_lane_count) + { + expected_add[base + group_lane_count / 2 + pair] = static_cast(rhs_low + rhs_high); + expected_subtract[base + group_lane_count / 2 + pair] = static_cast(rhs_low - rhs_high); + } + } + } + REQUIRE(lhs.horizontal_add(rhs).to_array() == expected_add); + REQUIRE(lhs.horizontal_subtract(rhs).to_array() == expected_subtract); + require_zero_suffix(lhs.horizontal_add(rhs)); + require_zero_suffix(lhs.horizontal_subtract(rhs)); + } +} + +/** @brief Verifies one floating dot-product control against an independent grouped scalar oracle. */ +template +void require_dot_product_oracle(const SimdLib::PartialRegister value) +{ + if constexpr (requires { value.template dot_product(value); }) + { + constexpr std::size_t group_lane_count = 128 / (sizeof(element_t) * 8); + const auto source = value.to_array(); + std::array expected{}; + for (std::size_t group = 0; group < bits / 128; ++group) + { + const std::size_t base = group * group_lane_count; + element_t sum{}; + for (std::size_t local = 0; local < group_lane_count; ++local) + if ((control & (1 << (local + 4))) != 0 && base + local < active_lane_count) + sum += source[base + local] * source[base + local]; + for (std::size_t local = 0; local < group_lane_count && base + local < active_lane_count; ++local) + if ((control & (1 << local)) != 0) + expected[base + local] = sum; + } + const auto actual = value.template dot_product(value); + for (std::size_t lane = 0; lane < active_lane_count; ++lane) + REQUIRE(actual.to_array()[lane] == Catch::Approx(expected[lane])); + require_zero_suffix(actual); + } +} + +/** @brief Runs all integral specialized-operation oracles for one element/width cell. */ +template void require_integral_specialized_matrix_cell() +{ + constexpr std::size_t first = first_active_lane_count(); + constexpr std::size_t last = SimdLib::Api::element_count - 1; + require_adjacent_oracle(); + if constexpr (first != last) + require_adjacent_oracle(); + require_byte_specialized_oracles(); + require_byte_specialized_oracles(); + require_byte_specialized_oracles(); + require_magnitude_oracles(); + require_horizontal_oracles(); +} + +/** @brief Verifies floating normalization and dot-product immediates for one element/width cell. */ +template void require_floating_specialized_matrix_cell() +{ + constexpr std::size_t active_lane_count = SimdLib::Api::element_count - 1; + using value_t = SimdLib::PartialRegister; + std::array source{}; + constexpr std::size_t group_lane_count = 128 / (sizeof(element_t) * 8); + for (std::size_t group = 0; group < bits / 128; ++group) + { + const std::size_t base = group * group_lane_count; + if (base < active_lane_count) + source[base] = static_cast(3); + if (base + 1 < active_lane_count) + source[base + 1] = static_cast(4); + } + const auto value = value_t::from_array(source); + const auto normalized = value.normalize(); + CAPTURE(sizeof(element_t), bits, active_lane_count); + for (std::size_t lane = 0; lane < active_lane_count; ++lane) + { + const std::size_t group_base = (lane / group_lane_count) * group_lane_count; + const std::size_t local = lane % group_lane_count; + const auto expected = local == 0 ? (group_base + 1 < active_lane_count ? static_cast(0.6) : static_cast(1)) + : local == 1 ? static_cast(0.8) + : element_t{}; + REQUIRE(normalized.to_array()[lane] == Catch::Approx(expected)); + } + require_zero_suffix(normalized); + require_dot_product_oracle<0x00>(value); + require_dot_product_oracle<0x11>(value); + require_dot_product_oracle<0x53>(value); + require_dot_product_oracle<0xf7>(value); + require_horizontal_oracles(); +} + +TEST_CASE("PartialRegister adjacent and byte-specialized operations match scalar oracles", "[simdlib][partial-register][specialized][oracle]") +{ +#define SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(type, width) require_integral_specialized_matrix_cell() + SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(std::int8_t, 128); + SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(std::uint8_t, 128); + SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(std::int16_t, 128); + SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(std::uint16_t, 128); + SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(std::int32_t, 128); + SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(std::uint32_t, 128); + SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(std::int64_t, 128); + SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(std::uint64_t, 128); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 + SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(std::int8_t, 256); + SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(std::uint8_t, 256); + SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(std::int16_t, 256); + SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(std::uint16_t, 256); + SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(std::int32_t, 256); + SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(std::uint32_t, 256); + SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(std::int64_t, 256); + SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED(std::uint64_t, 256); +#endif +#undef SIMDLIB_REQUIRE_INTEGRAL_SPECIALIZED +} + +TEST_CASE("PartialRegister floating specialized operations match grouped scalar oracles", "[simdlib][partial-register][specialized][floating]") +{ + require_floating_specialized_matrix_cell(); + require_floating_specialized_matrix_cell(); +#if SIMDLIB_PARTIAL_REGISTER_TEST_ENABLE_256 + require_floating_specialized_matrix_cell(); + require_floating_specialized_matrix_cell(); +#endif +} + +} // namespace diff --git a/tests/RegisterPreconditionFailure.tests.cpp b/tests/RegisterPreconditionFailure.tests.cpp index fef7041..98d1038 100644 --- a/tests/RegisterPreconditionFailure.tests.cpp +++ b/tests/RegisterPreconditionFailure.tests.cpp @@ -32,12 +32,14 @@ inline constexpr int register_precondition_failure_exit_code = 74; fail_register_precondition(message); \ } while (false) +#include #include #undef SIMDLIB_PRECONDITION #include #include +#include #include TEST_CASE("Register left shift rejects a negative per-lane count", "[simdlib][register][preconditions]") @@ -61,6 +63,27 @@ TEST_CASE("Register arithmetic right shift rejects a negative per-lane count", " FAIL("Register arithmetic right shift accepted a negative count"); } +TEST_CASE("PartialRegister left shift rejects a negative per-lane count", "[simdlib][partial_register][preconditions]") +{ + using value_type = SimdLib::PartialRegister; + (void)(value_type::broadcast(1U) << -1); + FAIL("PartialRegister left shift accepted a negative count"); +} + +TEST_CASE("PartialRegister logical right shift rejects a negative per-lane count", "[simdlib][partial_register][preconditions]") +{ + using value_type = SimdLib::PartialRegister; + (void)value_type::broadcast(-1).logical_shift_right(-1); + FAIL("PartialRegister logical right shift accepted a negative count"); +} + +TEST_CASE("PartialRegister arithmetic right shift rejects a negative per-lane count", "[simdlib][partial_register][preconditions]") +{ + using value_type = SimdLib::PartialRegister; + (void)(value_type::broadcast(-1) >> -1); + FAIL("PartialRegister arithmetic right shift accepted a negative count"); +} + TEST_CASE("Register aligned load rejects a misaligned source", "[simdlib][register][preconditions]") { using register_type = SimdLib::Register; @@ -76,3 +99,81 @@ TEST_CASE("Register aligned store rejects a misaligned destination", "[simdlib][ register_type::zero().store_aligned(std::span{destination.data() + 1, register_type::lane_count}); FAIL("Register aligned store accepted a misaligned destination"); } + +TEST_CASE("PartialRegisterMask rejects a noncanonical active native predicate", "[simdlib][partial_register][preconditions]") +{ + using mask_type = SimdLib::PartialRegisterMask; + using api_type = typename mask_type::api_type; + std::array lanes{}; + lanes[0] = 1U; + (void)mask_type::from_native(api_type::construct(lanes)); + FAIL("PartialRegisterMask accepted a noncanonical active native predicate"); +} + +TEST_CASE("PartialRegister rejects a nonzero inactive lane from direct aggregate initialization", "[simdlib][partial_register][preconditions]") +{ + using value_type = SimdLib::PartialRegister; + using api_type = typename value_type::api_type; + std::array lanes{}; + lanes[3] = 1U; + const value_type value{api_type::construct(lanes)}; + (void)value.to_native(); + FAIL("PartialRegister accepted a nonzero inactive lane from direct aggregate initialization"); +} + +TEST_CASE("PartialRegisterMask rejects a true inactive lane from direct aggregate initialization", "[simdlib][partial_register][preconditions]") +{ + using mask_type = SimdLib::PartialRegisterMask; + using api_type = typename mask_type::api_type; + std::array lanes{}; + lanes[3] = 0xffffffffU; + const mask_type value{api_type::construct(lanes)}; + (void)value.to_native(); + FAIL("PartialRegisterMask accepted a true inactive lane from direct aggregate initialization"); +} + +TEST_CASE("PartialRegister aligned load rejects a misaligned active source", "[simdlib][partial_register][preconditions]") +{ + using value_type = SimdLib::PartialRegister; + alignas(value_type::byte_count) std::array source{}; + (void)value_type::load_aligned(std::span{source.data() + 1, value_type::lane_count}); + FAIL("PartialRegister aligned load accepted a misaligned active source"); +} + +TEST_CASE("PartialRegister aligned store rejects a misaligned active destination", "[simdlib][partial_register][preconditions]") +{ + using value_type = SimdLib::PartialRegister; + alignas(value_type::byte_count) std::array destination{}; + value_type::zero().store_aligned(std::span{destination.data() + 1, value_type::lane_count}); + FAIL("PartialRegister aligned store accepted a misaligned active destination"); +} + +TEST_CASE("PartialRegister division rejects a zero active divisor", "[simdlib][partial_register][preconditions]") +{ + using value_type = SimdLib::PartialRegister; + (void)(value_type::broadcast(8) / value_type::from_lanes(2, 4, 8)); + (void)(value_type::broadcast(8) / value_type::from_lanes(2, 0, 4)); + FAIL("PartialRegister division accepted a zero active divisor"); +} + +TEST_CASE("PartialRegister modulus rejects a zero active divisor", "[simdlib][partial_register][preconditions]") +{ + using value_type = SimdLib::PartialRegister; + (void)(value_type::broadcast(8) % value_type::from_lanes(2, 4, 8)); + (void)(value_type::broadcast(8) % value_type::from_lanes(2, 4, 0)); + FAIL("PartialRegister modulus accepted a zero active divisor"); +} + +TEST_CASE("PartialRegister division rejects signed minimum divided by negative one", "[simdlib][partial_register][preconditions]") +{ + using value_type = SimdLib::PartialRegister; + (void)(value_type::from_lanes(8, std::numeric_limits::lowest(), 4) / value_type::from_lanes(2, -1, 2)); + FAIL("PartialRegister division accepted signed minimum divided by negative one"); +} + +TEST_CASE("PartialRegister modulus rejects signed minimum divided by negative one", "[simdlib][partial_register][preconditions]") +{ + using value_type = SimdLib::PartialRegister; + (void)(value_type::from_lanes(8, 4, std::numeric_limits::lowest()) % value_type::from_lanes(2, 2, -1)); + FAIL("PartialRegister modulus accepted signed minimum divided by negative one"); +} diff --git a/tests/availability/PartialRegisterEnabledProbe.cpp b/tests/availability/PartialRegisterEnabledProbe.cpp new file mode 100644 index 0000000..58c6bc1 --- /dev/null +++ b/tests/availability/PartialRegisterEnabledProbe.cpp @@ -0,0 +1,17 @@ +#include + +#include +#include + +#ifndef SIMDLIB_PARTIAL_REGISTER_AVAILABILITY_BITS +#define SIMDLIB_PARTIAL_REGISTER_AVAILABILITY_BITS 128 +#endif + +constexpr std::size_t partial_register_availability_lane_count = SIMDLIB_PARTIAL_REGISTER_AVAILABILITY_BITS == 256 ? 5 : 3; + +static_assert(SIMDLIB_REGISTER_INTERFACE_AVAILABLE == 1); +static_assert(SIMDLIB_REQUIRE_REGISTER_INTERFACE == 1); +using PartialRegisterAvailabilityProbe = + SimdLib::PartialRegister; +static_assert(std::is_final_v); +static_assert(SimdLib::IRegister::CoreSurface); diff --git a/tests/codegen/ApiPartialTransferCodegen.cpp b/tests/codegen/ApiPartialTransferCodegen.cpp new file mode 100644 index 0000000..aeac0eb --- /dev/null +++ b/tests/codegen/ApiPartialTransferCodegen.cpp @@ -0,0 +1,2 @@ +#define SIMDLIB_CODEGEN_USE_API 1 +#include "ApiPartialTransferCodegenFixture.h" diff --git a/tests/codegen/ApiPartialTransferCodegenFixture.h b/tests/codegen/ApiPartialTransferCodegenFixture.h new file mode 100644 index 0000000..af424cf --- /dev/null +++ b/tests/codegen/ApiPartialTransferCodegenFixture.h @@ -0,0 +1,145 @@ +#pragma once + +#include + +#include +#include +#include +#include + +#ifndef SIMDLIB_API_PARTIAL_CODEGEN_WIDTH +#error "SIMDLIB_API_PARTIAL_CODEGEN_WIDTH must select the fixture register width" +#endif + +namespace ApiPartialTransferCodegen +{ + +using api_t = SimdLib::Api; +using vector_t = typename api_t::vector_t; +constexpr inline std::size_t active_count = api_t::element_count / 2; +constexpr inline std::size_t active_byte_count = api_t::byte_count / 2; +using partial_array_t = std::array; + +/** @brief Broadcasts one scalar into exactly three active lanes and zero-fills the suffix. */ +extern "C" [[nodiscard]] vector_t simdlib_api_partial_codegen_broadcast_partial(std::uint32_t value) noexcept +{ +#if SIMDLIB_CODEGEN_USE_API + return api_t::template broadcast_partial<3>(value); +#else + return api_t::setr_partial(value, value, value); +#endif +} + +/** @brief Loads the exact low half of one register and zero-fills its high half. */ +extern "C" [[nodiscard]] vector_t simdlib_api_partial_codegen_load(const std::uint32_t *source) noexcept +{ +#if SIMDLIB_CODEGEN_USE_API + return api_t::template load_partial(std::span{source, active_count}); +#elif SIMDLIB_API_PARTIAL_CODEGEN_WIDTH == 128 + return _mm_loadl_epi64(reinterpret_cast(source)); +#else + return _mm256_inserti128_si256(_mm256_setzero_si256(), _mm_loadu_si128(reinterpret_cast(source)), 0); +#endif +} + +/** @brief Loads the exact aligned low half of one register and zero-fills its high half. */ +extern "C" [[nodiscard]] vector_t simdlib_api_partial_codegen_load_aligned(const std::uint32_t *source) noexcept +{ +#if SIMDLIB_CODEGEN_USE_API + return api_t::template load_partial_aligned(std::span{source, active_count}); +#elif SIMDLIB_API_PARTIAL_CODEGEN_WIDTH == 128 + return _mm_loadl_epi64(reinterpret_cast(source)); +#else + return _mm256_inserti128_si256(_mm256_setzero_si256(), _mm_load_si128(reinterpret_cast(source)), 0); +#endif +} + +/** @brief Loads one full aligned register through the partial-load API's complete-width branch. */ +extern "C" [[nodiscard]] vector_t simdlib_api_partial_codegen_load_aligned_full(const std::uint32_t *source) noexcept +{ +#if SIMDLIB_CODEGEN_USE_API + return api_t::template load_partial_aligned(std::span{source, api_t::element_count}); +#elif SIMDLIB_API_PARTIAL_CODEGEN_WIDTH == 128 + return _mm_load_si128(reinterpret_cast(source)); +#else + return _mm256_load_si256(reinterpret_cast(source)); +#endif +} + +/** @brief Loads the exact low half of one register's byte representation. */ +extern "C" [[nodiscard]] vector_t simdlib_api_partial_codegen_load_bytes(const std::byte *source) noexcept +{ +#if SIMDLIB_CODEGEN_USE_API + return api_t::template load_bytes_partial(std::span{source, active_byte_count}); +#elif SIMDLIB_API_PARTIAL_CODEGEN_WIDTH == 128 + return _mm_loadl_epi64(reinterpret_cast(source)); +#else + return _mm256_inserti128_si256(_mm256_setzero_si256(), _mm_loadu_si128(reinterpret_cast(source)), 0); +#endif +} + +/** @brief Stores the exact low half of one register without touching its suffix. */ +extern "C" void simdlib_api_partial_codegen_store(vector_t value, std::uint32_t *destination) noexcept +{ +#if SIMDLIB_CODEGEN_USE_API + api_t::template store_partial(value, std::span{destination, active_count}); +#elif SIMDLIB_API_PARTIAL_CODEGEN_WIDTH == 128 + _mm_storel_epi64(reinterpret_cast<__m128i *>(destination), value); +#else + _mm_storeu_si128(reinterpret_cast<__m128i *>(destination), _mm256_castsi256_si128(value)); +#endif +} + +/** @brief Stores the exact aligned low half of one register without touching its suffix. */ +extern "C" void simdlib_api_partial_codegen_store_aligned(vector_t value, std::uint32_t *destination) noexcept +{ +#if SIMDLIB_CODEGEN_USE_API + api_t::template store_partial_aligned(value, std::span{destination, active_count}); +#elif SIMDLIB_API_PARTIAL_CODEGEN_WIDTH == 128 + _mm_storel_epi64(reinterpret_cast<__m128i *>(destination), value); +#else + _mm_store_si128(reinterpret_cast<__m128i *>(destination), _mm256_castsi256_si128(value)); +#endif +} + +/** @brief Stores one full aligned register through the partial-store API's complete-width branch. */ +extern "C" void simdlib_api_partial_codegen_store_aligned_full(vector_t value, std::uint32_t *destination) noexcept +{ +#if SIMDLIB_CODEGEN_USE_API + api_t::template store_partial_aligned(value, std::span{destination, api_t::element_count}); +#elif SIMDLIB_API_PARTIAL_CODEGEN_WIDTH == 128 + _mm_store_si128(reinterpret_cast<__m128i *>(destination), value); +#else + _mm256_store_si256(reinterpret_cast<__m256i *>(destination), value); +#endif +} + +/** @brief Stores the exact low half of one register's byte representation. */ +extern "C" void simdlib_api_partial_codegen_store_bytes(vector_t value, std::byte *destination) noexcept +{ +#if SIMDLIB_CODEGEN_USE_API + api_t::template store_bytes_partial(value, std::span{destination, active_byte_count}); +#elif SIMDLIB_API_PARTIAL_CODEGEN_WIDTH == 128 + _mm_storel_epi64(reinterpret_cast<__m128i *>(destination), value); +#else + _mm_storeu_si128(reinterpret_cast<__m128i *>(destination), _mm256_castsi256_si128(value)); +#endif +} + +/** @brief Returns exactly the low half of one register after observing it as an array. */ +[[nodiscard]] partial_array_t simdlib_api_partial_codegen_to_array(vector_t value) noexcept +{ +#if SIMDLIB_CODEGEN_USE_API + return api_t::template to_array_partial(value); +#else + partial_array_t result{}; +#if SIMDLIB_API_PARTIAL_CODEGEN_WIDTH == 128 + _mm_storel_epi64(reinterpret_cast<__m128i *>(result.data()), value); +#else + _mm_storeu_si128(reinterpret_cast<__m128i *>(result.data()), _mm256_castsi256_si128(value)); +#endif + return result; +#endif +} + +} // namespace ApiPartialTransferCodegen diff --git a/tests/codegen/ApiPartialTransferCodegenRaw.cpp b/tests/codegen/ApiPartialTransferCodegenRaw.cpp new file mode 100644 index 0000000..2378a6b --- /dev/null +++ b/tests/codegen/ApiPartialTransferCodegenRaw.cpp @@ -0,0 +1,2 @@ +#define SIMDLIB_CODEGEN_USE_API 0 +#include "ApiPartialTransferCodegenFixture.h" diff --git a/tests/codegen/PartialRegisterAbi.cpp b/tests/codegen/PartialRegisterAbi.cpp new file mode 100644 index 0000000..26f721e --- /dev/null +++ b/tests/codegen/PartialRegisterAbi.cpp @@ -0,0 +1,204 @@ +#include + +#include +#include +#include + +#if defined(__clang__) || defined(__GNUC__) +#define SIMDLIB_PARTIAL_ABI_NOINLINE __attribute__((noinline, used)) +#elif SIMDLIB_COMPILER_MSVC +#define SIMDLIB_PARTIAL_ABI_NOINLINE __declspec(noinline) __declspec(dllexport) +#else +#define SIMDLIB_PARTIAL_ABI_NOINLINE __attribute__((noinline)) +#endif + +using partial_type = SimdLib::PartialRegister; +using integer_partial_type = SimdLib::PartialRegister; +using native_type = typename partial_type::native_type; +using integer_native_type = typename integer_partial_type::native_type; +using mask_type = typename partial_type::mask_type; + +/** @brief Non-inlined value parameter/return proof for one available specialization. */ +template class simdlib_partial_abi_matrix_cell final +{ + public: + using value_type = SimdLib::PartialRegister; + + /** @brief Passes the specialization unchanged across a value boundary. */ + SIMDLIB_PARTIAL_ABI_NOINLINE static value_type SIMD_FLAGS(InOut, RegisterOnly) pass(value_type value) noexcept + { + return value; + } +}; + +#define SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, count) template class simdlib_partial_abi_matrix_cell +#if SIMDLIB_PARTIAL_ABI_WIDTH == 128 +#define SIMDLIB_PARTIAL_ABI_COUNTS_8(type) \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 1); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 2); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 3); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 4); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 5); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 6); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 7); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 8); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 9); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 10); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 11); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 12); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 13); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 14); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 15) +#define SIMDLIB_PARTIAL_ABI_COUNTS_16(type) \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 1); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 2); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 3); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 4); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 5); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 6); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 7) +#define SIMDLIB_PARTIAL_ABI_COUNTS_32(type) \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 1); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 2); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 3) +#define SIMDLIB_PARTIAL_ABI_COUNTS_64(type) SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 1) +#else +#define SIMDLIB_PARTIAL_ABI_COUNTS_8(type) \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 17); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 18); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 19); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 20); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 21); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 22); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 23); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 24); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 25); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 26); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 27); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 28); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 29); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 30); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 31) +#define SIMDLIB_PARTIAL_ABI_COUNTS_16(type) \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 9); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 10); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 11); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 12); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 13); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 14); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 15) +#define SIMDLIB_PARTIAL_ABI_COUNTS_32(type) \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 5); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 6); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 7) +#define SIMDLIB_PARTIAL_ABI_COUNTS_64(type) SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 3) +#endif + +SIMDLIB_PARTIAL_ABI_COUNTS_8(std::int8_t); +SIMDLIB_PARTIAL_ABI_COUNTS_8(std::uint8_t); +SIMDLIB_PARTIAL_ABI_COUNTS_16(std::int16_t); +SIMDLIB_PARTIAL_ABI_COUNTS_16(std::uint16_t); +SIMDLIB_PARTIAL_ABI_COUNTS_32(std::int32_t); +SIMDLIB_PARTIAL_ABI_COUNTS_32(std::uint32_t); +SIMDLIB_PARTIAL_ABI_COUNTS_32(float); +SIMDLIB_PARTIAL_ABI_COUNTS_64(std::int64_t); +SIMDLIB_PARTIAL_ABI_COUNTS_64(std::uint64_t); +SIMDLIB_PARTIAL_ABI_COUNTS_64(double); + +#undef SIMDLIB_PARTIAL_ABI_COUNTS_64 +#undef SIMDLIB_PARTIAL_ABI_COUNTS_32 +#undef SIMDLIB_PARTIAL_ABI_COUNTS_16 +#undef SIMDLIB_PARTIAL_ABI_COUNTS_8 +#undef SIMDLIB_PARTIAL_ABI_INSTANTIATE + +/** + * @brief Verifies native size and alignment for every available active count of one lane type. + * @tparam element_t Lane type under qualification. + * @tparam indices Zero-based candidates mapped to active counts starting at one. + * @return True when every available partial specialization is exactly one native register. + */ +template [[nodiscard]] consteval bool simdlib_partial_abi_layouts(std::index_sequence) noexcept +{ + return ( + []() consteval + { + if constexpr (SimdLib::PartialRegisterAvailable) + { + using candidate = SimdLib::PartialRegister; + using candidate_native = typename candidate::native_type; + return sizeof(candidate) == sizeof(candidate_native) && alignof(candidate) == alignof(candidate_native); + } + else + { + return true; + } + }.template operator()() && + ...); +} + +/** @brief Returns one partial value across a non-inlined value boundary. */ +SIMDLIB_PARTIAL_ABI_NOINLINE partial_type SIMD_FLAGS(InOut, RegisterOnly) simdlib_partial_abi_return(partial_type lhs, partial_type rhs) noexcept +{ + return lhs + rhs; +} + +/** @brief Passes one partial value across a non-inlined value boundary. */ +SIMDLIB_PARTIAL_ABI_NOINLINE native_type SIMD_FLAGS(InOut, RegisterOnly) simdlib_partial_abi_pass(partial_type value) noexcept +{ + return value.native; +} + +/** @brief Reassigns one partial value through a non-inlined reference boundary. */ +SIMDLIB_PARTIAL_ABI_NOINLINE auto SIMD_FLAGS(In) simdlib_partial_abi_reassign(partial_type &lhs, partial_type rhs) noexcept -> partial_type & +{ + lhs = lhs + rhs; + return lhs; +} + +/** @brief Returns a partial predicate across a non-inlined boundary. */ +SIMDLIB_PARTIAL_ABI_NOINLINE mask_type SIMD_FLAGS(In, RegisterOnly) simdlib_partial_abi_mask(partial_type lhs, partial_type rhs) noexcept +{ + return lhs.compare_greater(rhs); +} + +/** @brief Returns an active-lane scalar observation across a non-inlined boundary. */ +SIMDLIB_PARTIAL_ABI_NOINLINE std::uint32_t SIMD_FLAGS(In, RegisterOnly) simdlib_partial_abi_scalar(integer_partial_type value) noexcept +{ + return value.movemask(); +} + +/** @brief Returns a type-changing bit-cast result across a non-inlined boundary. */ +SIMDLIB_PARTIAL_ABI_NOINLINE integer_native_type SIMD_FLAGS(InOut, RegisterOnly) simdlib_partial_abi_type_change(partial_type value) noexcept +{ + return value.template bit_cast().native; +} + +/** @brief Calls a non-inlined partial-value boundary to expose opaque-call handling. */ +SIMDLIB_PARTIAL_ABI_NOINLINE partial_type SIMD_FLAGS(InOut, RegisterOnly) simdlib_partial_abi_opaque(partial_type lhs, partial_type rhs) noexcept +{ + return simdlib_partial_abi_return(lhs, rhs) - rhs; +} + +/** @brief Keeps several partial values live across arithmetic to expose register pressure. */ +SIMDLIB_PARTIAL_ABI_NOINLINE partial_type SIMD_FLAGS(InOut, RegisterOnly) + simdlib_partial_abi_pressure(partial_type a, partial_type b, partial_type c, partial_type d) noexcept +{ + return (a + b) * (c - d); +} + +static_assert(sizeof(partial_type) == sizeof(native_type)); +static_assert(alignof(partial_type) == alignof(native_type)); +static_assert(sizeof(integer_partial_type) == sizeof(integer_native_type)); +static_assert(alignof(integer_partial_type) == alignof(integer_native_type)); +static_assert(simdlib_partial_abi_layouts(std::make_index_sequence::element_count>{})); +static_assert(simdlib_partial_abi_layouts(std::make_index_sequence::element_count>{})); +static_assert(simdlib_partial_abi_layouts(std::make_index_sequence::element_count>{})); +static_assert(simdlib_partial_abi_layouts(std::make_index_sequence::element_count>{})); +static_assert(simdlib_partial_abi_layouts(std::make_index_sequence::element_count>{})); +static_assert(simdlib_partial_abi_layouts(std::make_index_sequence::element_count>{})); +static_assert(simdlib_partial_abi_layouts(std::make_index_sequence::element_count>{})); +static_assert(simdlib_partial_abi_layouts(std::make_index_sequence::element_count>{})); +static_assert(simdlib_partial_abi_layouts(std::make_index_sequence::element_count>{})); +static_assert(simdlib_partial_abi_layouts(std::make_index_sequence::element_count>{})); + +#undef SIMDLIB_PARTIAL_ABI_NOINLINE diff --git a/tests/codegen/PartialRegisterAbiRaw.cpp b/tests/codegen/PartialRegisterAbiRaw.cpp new file mode 100644 index 0000000..0d09dbb --- /dev/null +++ b/tests/codegen/PartialRegisterAbiRaw.cpp @@ -0,0 +1,176 @@ +#include + +#include +#include +#include +#include + +#if SIMDLIB_COMPILER_MSVC +#define SIMDLIB_PARTIAL_ABI_NOINLINE __declspec(noinline) +#else +#define SIMDLIB_PARTIAL_ABI_NOINLINE __attribute__((noinline)) +#endif + +using api_type = SimdLib::Api; +using integer_api_type = SimdLib::Api; +using native_type = typename api_type::vector_t; +using integer_native_type = typename integer_api_type::vector_t; + +/** @brief Native-vector value parameter/return mirror for one specialization cell. */ +template class simdlib_partial_abi_matrix_cell final +{ + public: + using value_type = typename SimdLib::Api::vector_t; + + /** @brief Passes the native vector unchanged across a value boundary. */ + SIMDLIB_PARTIAL_ABI_NOINLINE static value_type SIMD_FLAGS(InOut) pass(value_type value) noexcept + { + return value; + } +}; + +#define SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, count) template class simdlib_partial_abi_matrix_cell +#if SIMDLIB_PARTIAL_ABI_WIDTH == 128 +#define SIMDLIB_PARTIAL_ABI_COUNTS_8(type) \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 1); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 2); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 3); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 4); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 5); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 6); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 7); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 8); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 9); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 10); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 11); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 12); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 13); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 14); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 15) +#define SIMDLIB_PARTIAL_ABI_COUNTS_16(type) \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 1); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 2); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 3); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 4); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 5); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 6); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 7) +#define SIMDLIB_PARTIAL_ABI_COUNTS_32(type) \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 1); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 2); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 3) +#define SIMDLIB_PARTIAL_ABI_COUNTS_64(type) SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 1) +#else +#define SIMDLIB_PARTIAL_ABI_COUNTS_8(type) \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 17); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 18); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 19); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 20); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 21); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 22); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 23); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 24); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 25); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 26); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 27); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 28); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 29); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 30); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 31) +#define SIMDLIB_PARTIAL_ABI_COUNTS_16(type) \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 9); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 10); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 11); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 12); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 13); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 14); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 15) +#define SIMDLIB_PARTIAL_ABI_COUNTS_32(type) \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 5); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 6); \ + SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 7) +#define SIMDLIB_PARTIAL_ABI_COUNTS_64(type) SIMDLIB_PARTIAL_ABI_INSTANTIATE(type, 3) +#endif + +SIMDLIB_PARTIAL_ABI_COUNTS_8(std::int8_t); +SIMDLIB_PARTIAL_ABI_COUNTS_8(std::uint8_t); +SIMDLIB_PARTIAL_ABI_COUNTS_16(std::int16_t); +SIMDLIB_PARTIAL_ABI_COUNTS_16(std::uint16_t); +SIMDLIB_PARTIAL_ABI_COUNTS_32(std::int32_t); +SIMDLIB_PARTIAL_ABI_COUNTS_32(std::uint32_t); +SIMDLIB_PARTIAL_ABI_COUNTS_32(float); +SIMDLIB_PARTIAL_ABI_COUNTS_64(std::int64_t); +SIMDLIB_PARTIAL_ABI_COUNTS_64(std::uint64_t); +SIMDLIB_PARTIAL_ABI_COUNTS_64(double); + +#undef SIMDLIB_PARTIAL_ABI_COUNTS_64 +#undef SIMDLIB_PARTIAL_ABI_COUNTS_32 +#undef SIMDLIB_PARTIAL_ABI_COUNTS_16 +#undef SIMDLIB_PARTIAL_ABI_COUNTS_8 +#undef SIMDLIB_PARTIAL_ABI_INSTANTIATE + +/** @brief Applies the PartialRegister inactive-suffix projection to a raw floating vector. */ +[[nodiscard]] constexpr native_type normalize_partial(native_type value) noexcept +{ + constexpr std::size_t active_lanes = SIMDLIB_PARTIAL_ABI_WIDTH == 256 ? 5 : 3; + constexpr auto filter = [] + { + std::array lanes{}; + constexpr auto true_bits = std::array{std::byte{0xff}, std::byte{0xff}, std::byte{0xff}, std::byte{0xff}}; + for (std::size_t lane = 0; lane < active_lanes; ++lane) + lanes[lane] = std::bit_cast(true_bits); + return lanes; + }(); + return api_type::bitwise_and(value, api_type::construct(filter)); +} + +/** @brief Raw native mirror for a partial value return. */ +SIMDLIB_PARTIAL_ABI_NOINLINE native_type SIMD_FLAGS(InOut) simdlib_partial_abi_return(native_type lhs, native_type rhs) noexcept +{ + return api_type::add(lhs, rhs); +} + +/** @brief Raw native mirror for a partial value parameter. */ +SIMDLIB_PARTIAL_ABI_NOINLINE native_type SIMD_FLAGS(InOut) simdlib_partial_abi_pass(native_type value) noexcept +{ + return value; +} + +/** @brief Raw native mirror for partial-value reassignment. */ +SIMDLIB_PARTIAL_ABI_NOINLINE auto SIMD_FLAGS(In) simdlib_partial_abi_reassign(native_type &lhs, native_type rhs) noexcept -> native_type & +{ + lhs = api_type::add(lhs, rhs); + return lhs; +} + +/** @brief Raw native mirror for a partial predicate result. */ +SIMDLIB_PARTIAL_ABI_NOINLINE native_type SIMD_FLAGS(InOut) simdlib_partial_abi_mask(native_type lhs, native_type rhs) noexcept +{ + return normalize_partial(api_type::compare_greater(lhs, rhs)); +} + +/** @brief Raw native mirror for an active-lane scalar observation. */ +SIMDLIB_PARTIAL_ABI_NOINLINE std::uint32_t SIMD_FLAGS(In) simdlib_partial_abi_scalar(integer_native_type value) noexcept +{ + return integer_api_type::movemask(value); +} + +/** @brief Raw native mirror for a same-width type-changing result. */ +SIMDLIB_PARTIAL_ABI_NOINLINE integer_native_type SIMD_FLAGS(InOut) simdlib_partial_abi_type_change(native_type value) noexcept +{ + return std::bit_cast(value); +} + +/** @brief Raw native mirror for opaque-call handling. */ +SIMDLIB_PARTIAL_ABI_NOINLINE native_type SIMD_FLAGS(InOut) simdlib_partial_abi_opaque(native_type lhs, native_type rhs) noexcept +{ + return normalize_partial(api_type::subtract(simdlib_partial_abi_return(lhs, rhs), rhs)); +} + +/** @brief Raw native mirror for register-pressure handling. */ +SIMDLIB_PARTIAL_ABI_NOINLINE native_type SIMD_FLAGS(InOut) simdlib_partial_abi_pressure(native_type a, native_type b, native_type c, native_type d) noexcept +{ + return api_type::multiply(api_type::add(a, b), normalize_partial(api_type::subtract(c, d))); +} + +#undef SIMDLIB_PARTIAL_ABI_NOINLINE diff --git a/tests/codegen/PartialRegisterArithmeticCodegen.cpp b/tests/codegen/PartialRegisterArithmeticCodegen.cpp new file mode 100644 index 0000000..034ba82 --- /dev/null +++ b/tests/codegen/PartialRegisterArithmeticCodegen.cpp @@ -0,0 +1,229 @@ +#include + +#include +#include + +#ifndef SIMDLIB_PARTIAL_ARITHMETIC_CODEGEN_WIDTH +#error "SIMDLIB_PARTIAL_ARITHMETIC_CODEGEN_WIDTH must select the fixture register width" +#endif + +namespace +{ +constexpr std::size_t i32_lanes = SIMDLIB_PARTIAL_ARITHMETIC_CODEGEN_WIDTH / 32 - 1; +constexpr std::size_t i16_lanes = SIMDLIB_PARTIAL_ARITHMETIC_CODEGEN_WIDTH / 16 - 1; +constexpr std::size_t u8_lanes = SIMDLIB_PARTIAL_ARITHMETIC_CODEGEN_WIDTH / 8 - 1; +constexpr std::size_t f32_lanes = SIMDLIB_PARTIAL_ARITHMETIC_CODEGEN_WIDTH / 32 - 1; +} // namespace + +using partial_i32 = SimdLib::PartialRegister; +using partial_i16 = SimdLib::PartialRegister; +using partial_u8 = SimdLib::PartialRegister; +using partial_f32 = SimdLib::PartialRegister; + +/** @brief Emits partial-register addition. */ +extern "C" [[nodiscard]] partial_i32::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_add(partial_i32 lhs, + partial_i32 rhs) noexcept +{ + return (lhs + rhs).native; +} + +/** @brief Emits partial-register subtraction. */ +extern "C" [[nodiscard]] partial_f32::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_subtract(partial_f32 lhs, + partial_f32 rhs) noexcept +{ + return (lhs - rhs).native; +} + +/** @brief Emits partial-register multiplication. */ +extern "C" [[nodiscard]] partial_i32::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_multiply(partial_i32 lhs, + partial_i32 rhs) noexcept +{ + return (lhs * rhs).native; +} + +/** @brief Emits neutralized partial-register division. */ +extern "C" [[nodiscard]] partial_i32::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_divide(partial_i32 lhs, + partial_i32 rhs) noexcept +{ + return (lhs / rhs).native; +} + +/** @brief Emits neutralized partial-register modulus. */ +extern "C" [[nodiscard]] partial_i32::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_modulus(partial_i32 lhs, + partial_i32 rhs) noexcept +{ + return (lhs % rhs).native; +} + +/** @brief Emits projected partial-register negation. */ +extern "C" [[nodiscard]] partial_f32::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_negate(partial_f32 value) noexcept +{ + return (-value).native; +} + +/** @brief Emits zero-closed partial-register minimum. */ +extern "C" [[nodiscard]] partial_i32::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_min(partial_i32 lhs, + partial_i32 rhs) noexcept +{ + return lhs.min(rhs).native; +} + +/** @brief Emits zero-closed partial-register maximum. */ +extern "C" [[nodiscard]] partial_i32::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_max(partial_i32 lhs, + partial_i32 rhs) noexcept +{ + return lhs.max(rhs).native; +} + +/** @brief Emits partial-register absolute value. */ +extern "C" [[nodiscard]] partial_i32::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_absolute(partial_i32 value) noexcept +{ + return value.absolute().native; +} + +/** @brief Emits partial-register square root. */ +extern "C" [[nodiscard]] partial_f32::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_sqrt(partial_f32 value) noexcept +{ + return value.sqrt().native; +} + +/** @brief Emits partial-register average. */ +extern "C" [[nodiscard]] partial_u8::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_average(partial_u8 lhs, + partial_u8 rhs) noexcept +{ + return lhs.average(rhs).native; +} + +/** @brief Emits partial-register multiply-add. */ +extern "C" [[nodiscard]] partial_f32::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_multiply_add(partial_f32 lhs, + partial_f32 rhs, + partial_f32 addend) noexcept +{ + return lhs.multiply_add(rhs, addend).native; +} + +/** @brief Emits projected grouped magnitude. */ +extern "C" [[nodiscard]] partial_i32::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_magnitude(partial_i32 value) noexcept +{ + return value.magnitude().native; +} + +/** @brief Emits checked grouped magnitude and its deliberate result mapping. */ +extern "C" [[nodiscard]] partial_i16::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_magnitude_checked(partial_i16 value) noexcept +{ + return value.magnitude_checked().native; +} + +/** @brief Emits projected grouped normalization. */ +extern "C" [[nodiscard]] partial_f32::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_normalize(partial_f32 value) noexcept +{ + return value.normalize().native; +} + +/** @brief Emits projected horizontal addition. */ +extern "C" [[nodiscard]] partial_i16::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_horizontal_add(partial_i16 lhs, + partial_i16 rhs) noexcept +{ + return lhs.horizontal_add(rhs).native; +} + +/** @brief Emits projected horizontal subtraction. */ +extern "C" [[nodiscard]] partial_i16::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_horizontal_subtract(partial_i16 lhs, partial_i16 rhs) noexcept +{ + return lhs.horizontal_subtract(rhs).native; +} + +/** @brief Emits adjacent multiply-add with its deliberate result mapping. */ +extern "C" [[nodiscard]] partial_i16::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_multiply_add_adjacent(partial_i16 lhs, partial_i16 rhs) noexcept +{ + return lhs.multiply_add_adjacent(rhs).native; +} + +/** @brief Emits unsigned/signed byte multiply-add with its deliberate result mapping. */ +extern "C" [[nodiscard]] partial_u8::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_byte_multiply_add(partial_u8 lhs, + partial_u8 rhs) noexcept +{ + return lhs.multiply_add_unsigned_signed_bytes(rhs).native; +} + +/** @brief Emits byte sum-of-absolute-differences with its deliberate result mapping. */ +extern "C" [[nodiscard]] partial_u8::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_sad(partial_u8 lhs, + partial_u8 rhs) noexcept +{ + return lhs.sum_absolute_byte_differences(rhs).native; +} + +/** @brief Emits immediate-controlled multi-SAD. */ +extern "C" [[nodiscard]] partial_u8::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_multi_sad(partial_u8 lhs, + partial_u8 rhs) noexcept +{ + return lhs.template multi_sum_absolute_byte_differences<0x35>(rhs).native; +} + +/** @brief Emits minimum-position reduction with inactive-lane exclusion. */ +extern "C" [[nodiscard]] std::size_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_min_position(partial_i16 value) noexcept +{ + return value.min_position(); +} + +/** @brief Emits maximum-position reduction with inactive-lane exclusion. */ +extern "C" [[nodiscard]] std::size_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_max_position(partial_i16 value) noexcept +{ + return value.max_position(); +} + +/** @brief Emits saturating partial-register addition. */ +extern "C" [[nodiscard]] partial_u8::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_add_saturated(partial_u8 lhs, + partial_u8 rhs) noexcept +{ + return lhs.add_saturated(rhs).native; +} + +/** @brief Emits saturating partial-register subtraction. */ +extern "C" [[nodiscard]] partial_u8::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_subtract_saturated(partial_u8 lhs, partial_u8 rhs) noexcept +{ + return lhs.subtract_saturated(rhs).native; +} + +/** @brief Emits projected saturated horizontal addition. */ +extern "C" [[nodiscard]] partial_i16::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_horizontal_add_saturated(partial_i16 lhs, partial_i16 rhs) noexcept +{ + return lhs.horizontal_add_saturated(rhs).native; +} + +/** @brief Emits projected saturated horizontal subtraction. */ +extern "C" [[nodiscard]] partial_i16::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_horizontal_subtract_saturated(partial_i16 lhs, partial_i16 rhs) noexcept +{ + return lhs.horizontal_subtract_saturated(rhs).native; +} + +/** @brief Emits projected alternating subtraction and addition. */ +extern "C" [[nodiscard]] partial_f32::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_add_subtract(partial_f32 lhs, + partial_f32 rhs) noexcept +{ + return lhs.add_subtract(rhs).native; +} + +/** @brief Emits immediate-controlled dot product without redundant projection. */ +extern "C" [[nodiscard]] partial_f32::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_dot_product(partial_f32 lhs, + partial_f32 rhs) noexcept +{ + return lhs.template dot_product<0x11>(rhs).native; +} + +#if SIMDLIB_PARTIAL_ARITHMETIC_CODEGEN_WIDTH == 256 +using partial_i64 = SimdLib::PartialRegister; + +/** @brief Emits the sparse complete-register adjacent result for a three-lane 64-bit source. */ +extern "C" [[nodiscard]] partial_i64::native_type SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_sparse_adjacent(partial_i64 lhs, + partial_i64 rhs) noexcept +{ + return lhs.multiply_add_adjacent(rhs).native; +} +#endif diff --git a/tests/codegen/PartialRegisterArithmeticCodegenRaw.cpp b/tests/codegen/PartialRegisterArithmeticCodegenRaw.cpp new file mode 100644 index 0000000..be5f930 --- /dev/null +++ b/tests/codegen/PartialRegisterArithmeticCodegenRaw.cpp @@ -0,0 +1,309 @@ +#include + +#include +#include +#include +#include +#include + +#ifndef SIMDLIB_PARTIAL_ARITHMETIC_CODEGEN_WIDTH +#error "SIMDLIB_PARTIAL_ARITHMETIC_CODEGEN_WIDTH must select the fixture register width" +#endif + +namespace +{ +constexpr std::size_t i32_lanes = SIMDLIB_PARTIAL_ARITHMETIC_CODEGEN_WIDTH / 32 - 1; +constexpr std::size_t i16_lanes = SIMDLIB_PARTIAL_ARITHMETIC_CODEGEN_WIDTH / 16 - 1; +constexpr std::size_t f32_lanes = SIMDLIB_PARTIAL_ARITHMETIC_CODEGEN_WIDTH / 32 - 1; + +/** @brief Clears every physical lane beyond a compile-time logical prefix. */ +template +[[nodiscard]] typename api_t::vector_t SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) project(typename api_t::vector_t native) noexcept +{ + constexpr auto filter = []() constexpr + { + std::array result{}; + std::array one_bytes{}; + one_bytes.fill(std::byte{0xff}); + const auto one = std::bit_cast(one_bytes); + for (std::size_t lane = 0; lane < active_lane_count; ++lane) + result[lane] = one; + return result; + }(); + return api_t::bitwise_and(native, api_t::construct(filter)); +} + +/** @brief Replaces every inactive divisor lane with one. */ +template +[[nodiscard]] typename api_t::vector_t SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) neutralize_divisors(typename api_t::vector_t native) noexcept +{ + constexpr auto identity = []() constexpr + { + std::array result{}; + for (std::size_t lane = active_lane_count; lane < api_t::element_count; ++lane) + result[lane] = element_t{1}; + return result; + }(); + return api_t::bitwise_or(native, api_t::construct(identity)); +} + +/** @brief Replaces inactive lanes with an extrema-search sentinel. */ +template +[[nodiscard]] typename api_t::vector_t SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) apply_position_sentinel(typename api_t::vector_t native) noexcept +{ + constexpr auto identity = []() constexpr + { + std::array result{}; + for (std::size_t lane = active_lane_count; lane < api_t::element_count; ++lane) + result[lane] = minimum_search ? std::numeric_limits::max() : std::numeric_limits::lowest(); + return result; + }(); + return api_t::bitwise_or(native, api_t::construct(identity)); +} +} // namespace + +using partial_i32 = SimdLib::Api; +using partial_i16 = SimdLib::Api; +using partial_u8 = SimdLib::Api; +using partial_f32 = SimdLib::Api; + +/** @brief Raw API mirror for partial-register addition. */ +extern "C" [[nodiscard]] typename partial_i32::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_add(typename partial_i32::vector_t lhs, typename partial_i32::vector_t rhs) noexcept +{ + return partial_i32::add(rhs, lhs); +} + +/** @brief Raw API mirror for projected partial-register subtraction. */ +extern "C" [[nodiscard]] typename partial_f32::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_subtract(typename partial_f32::vector_t lhs, typename partial_f32::vector_t rhs) noexcept +{ + using api_t = partial_f32; + return project(api_t::subtract(lhs, rhs)); +} + +/** @brief Raw API mirror for partial-register multiplication. */ +extern "C" [[nodiscard]] typename partial_i32::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_multiply(typename partial_i32::vector_t lhs, typename partial_i32::vector_t rhs) noexcept +{ + return partial_i32::multiply(rhs, lhs); +} + +/** @brief Raw API mirror for neutralized partial-register division. */ +extern "C" [[nodiscard]] typename partial_i32::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_divide(typename partial_i32::vector_t lhs, typename partial_i32::vector_t rhs) noexcept +{ + using api_t = partial_i32; + return project(api_t::divide(lhs, neutralize_divisors(rhs))); +} + +/** @brief Raw API mirror for neutralized partial-register modulus. */ +extern "C" [[nodiscard]] typename partial_i32::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_modulus(typename partial_i32::vector_t lhs, typename partial_i32::vector_t rhs) noexcept +{ + using api_t = partial_i32; + return project(api_t::modulus(lhs, neutralize_divisors(rhs))); +} + +/** @brief Raw API mirror for projected partial-register negation. */ +extern "C" [[nodiscard]] typename partial_f32::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_negate(typename partial_f32::vector_t value) noexcept +{ + using api_t = partial_f32; + return project(api_t::negate(value)); +} + +/** @brief Raw API mirror for zero-closed partial-register minimum. */ +extern "C" [[nodiscard]] typename partial_i32::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_min(typename partial_i32::vector_t lhs, typename partial_i32::vector_t rhs) noexcept +{ + return partial_i32::min(lhs, rhs); +} + +/** @brief Raw API mirror for zero-closed partial-register maximum. */ +extern "C" [[nodiscard]] typename partial_i32::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_max(typename partial_i32::vector_t lhs, typename partial_i32::vector_t rhs) noexcept +{ + return partial_i32::max(lhs, rhs); +} + +/** @brief Raw API mirror for partial-register absolute value. */ +extern "C" [[nodiscard]] typename partial_i32::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_absolute(typename partial_i32::vector_t value) noexcept +{ + return partial_i32::absolute(value); +} + +/** @brief Raw API mirror for partial-register square root. */ +extern "C" [[nodiscard]] typename partial_f32::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_sqrt(typename partial_f32::vector_t value) noexcept +{ + return partial_f32::sqrt(value); +} + +/** @brief Raw API mirror for partial-register average. */ +extern "C" [[nodiscard]] typename partial_u8::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_average(typename partial_u8::vector_t lhs, typename partial_u8::vector_t rhs) noexcept +{ + return partial_u8::avg(lhs, rhs); +} + +/** @brief Raw API mirror for partial-register multiply-add. */ +extern "C" [[nodiscard]] typename partial_f32::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS simdlib_partial_arithmetic_codegen_multiply_add( + typename partial_f32::vector_t lhs, typename partial_f32::vector_t rhs, typename partial_f32::vector_t addend) noexcept +{ +#if SIMDLIB_COMPILER_GCC + return partial_f32::multiply_add(lhs, rhs, addend); +#else + return partial_f32::multiply_add(rhs, lhs, addend); +#endif +} + +/** @brief Raw API mirror for projected grouped magnitude. */ +extern "C" [[nodiscard]] typename partial_i32::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_magnitude(typename partial_i32::vector_t value) noexcept +{ + using api_t = partial_i32; + return project(api_t::magnitude(value)); +} + +/** @brief Raw API mirror for checked grouped magnitude and result projection. */ +extern "C" [[nodiscard]] typename partial_i16::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_magnitude_checked(typename partial_i16::vector_t value) noexcept +{ + using source_api_t = partial_i16; + constexpr std::size_t group_lane_count = 128 / (sizeof(std::int16_t) * 8); + constexpr std::size_t result_lane_count = ((i16_lanes - 1) / group_lane_count) * group_lane_count + 2; + if constexpr (result_lane_count == source_api_t::element_count) + return source_api_t::magnitude_checked(value); + else + return project(source_api_t::magnitude_checked(value)); +} + +/** @brief Raw API mirror for projected grouped normalization. */ +extern "C" [[nodiscard]] typename partial_f32::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_normalize(typename partial_f32::vector_t value) noexcept +{ + using api_t = partial_f32; + return project(api_t::normalize(value)); +} + +/** @brief Raw API mirror for projected horizontal addition. */ +extern "C" [[nodiscard]] typename partial_i16::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_horizontal_add(typename partial_i16::vector_t lhs, typename partial_i16::vector_t rhs) noexcept +{ + using api_t = partial_i16; + return project(api_t::add_horizontal(lhs, rhs)); +} + +/** @brief Raw API mirror for projected horizontal subtraction. */ +extern "C" [[nodiscard]] typename partial_i16::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_horizontal_subtract(typename partial_i16::vector_t lhs, typename partial_i16::vector_t rhs) noexcept +{ + using api_t = partial_i16; + return project(api_t::subtract_horizontal(lhs, rhs)); +} + +/** @brief Raw API mirror for adjacent multiply-add. */ +extern "C" [[nodiscard]] typename partial_i16::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_multiply_add_adjacent(typename partial_i16::vector_t lhs, typename partial_i16::vector_t rhs) noexcept +{ + return partial_i16::multiply_add_adjacent(lhs, rhs); +} + +/** @brief Raw API mirror for unsigned/signed byte multiply-add. */ +extern "C" [[nodiscard]] typename partial_u8::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_byte_multiply_add(typename partial_u8::vector_t lhs, typename partial_u8::vector_t rhs) noexcept +{ + return partial_u8::multiply_add_unsigned_signed_bytes(lhs, rhs); +} + +/** @brief Raw API mirror for byte sum-of-absolute-differences. */ +extern "C" [[nodiscard]] typename partial_u8::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_sad(typename partial_u8::vector_t lhs, typename partial_u8::vector_t rhs) noexcept +{ + return partial_u8::sum_absolute_byte_differences(lhs, rhs); +} + +/** @brief Raw API mirror for immediate-controlled multi-SAD. */ +extern "C" [[nodiscard]] typename partial_u8::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_multi_sad(typename partial_u8::vector_t lhs, typename partial_u8::vector_t rhs) noexcept +{ + return partial_u8::template multi_sum_absolute_byte_differences<0x35>(lhs, rhs); +} + +/** @brief Raw API mirror for minimum-position reduction with inactive-lane exclusion. */ +extern "C" [[nodiscard]] std::size_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_min_position(typename partial_i16::vector_t value) noexcept +{ + using api_t = partial_i16; + return api_t::min_position(apply_position_sentinel(value)); +} + +/** @brief Raw API mirror for maximum-position reduction with inactive-lane exclusion. */ +extern "C" [[nodiscard]] std::size_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_max_position(typename partial_i16::vector_t value) noexcept +{ + using api_t = partial_i16; + return api_t::max_position(apply_position_sentinel(value)); +} + +/** @brief Raw API mirror for saturating partial-register addition. */ +extern "C" [[nodiscard]] typename partial_u8::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_add_saturated(typename partial_u8::vector_t lhs, typename partial_u8::vector_t rhs) noexcept +{ + return partial_u8::add_saturated(lhs, rhs); +} + +/** @brief Raw API mirror for saturating partial-register subtraction. */ +extern "C" [[nodiscard]] typename partial_u8::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_subtract_saturated(typename partial_u8::vector_t lhs, typename partial_u8::vector_t rhs) noexcept +{ + return partial_u8::subtract_saturated(lhs, rhs); +} + +/** @brief Raw API mirror for projected saturated horizontal addition. */ +extern "C" [[nodiscard]] typename partial_i16::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_horizontal_add_saturated(typename partial_i16::vector_t lhs, typename partial_i16::vector_t rhs) noexcept +{ + using api_t = partial_i16; + return project(api_t::hadd_saturated(lhs, rhs)); +} + +/** @brief Raw API mirror for projected saturated horizontal subtraction. */ +extern "C" [[nodiscard]] typename partial_i16::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_horizontal_subtract_saturated(typename partial_i16::vector_t lhs, typename partial_i16::vector_t rhs) noexcept +{ + using api_t = partial_i16; + return project(api_t::hsubtract_saturated(lhs, rhs)); +} + +/** @brief Raw API mirror for projected alternating subtraction and addition. */ +extern "C" [[nodiscard]] typename partial_f32::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_add_subtract(typename partial_f32::vector_t lhs, typename partial_f32::vector_t rhs) noexcept +{ + using api_t = partial_f32; + return project(api_t::add_subtract(lhs, rhs)); +} + +/** @brief Raw API mirror for immediate-controlled dot product without projection. */ +extern "C" [[nodiscard]] typename partial_f32::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_dot_product(typename partial_f32::vector_t lhs, typename partial_f32::vector_t rhs) noexcept +{ + return partial_f32::template dot_product<0x11>(lhs, rhs); +} + +#if SIMDLIB_PARTIAL_ARITHMETIC_CODEGEN_WIDTH == 256 +using partial_i64 = SimdLib::Api<256, std::int64_t>; + +/** @brief Raw API mirror for the sparse complete-register adjacent result. */ +extern "C" [[nodiscard]] typename partial_i64::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_arithmetic_codegen_sparse_adjacent(typename partial_i64::vector_t lhs, typename partial_i64::vector_t rhs) noexcept +{ +#if SIMDLIB_COMPILER_GCC + return partial_i64::multiply_add_adjacent(lhs, rhs); +#else + return partial_i64::multiply_add_adjacent(rhs, lhs); +#endif +} +#endif diff --git a/tests/codegen/PartialRegisterGeneralCodegen.cpp b/tests/codegen/PartialRegisterGeneralCodegen.cpp new file mode 100644 index 0000000..3e638dd --- /dev/null +++ b/tests/codegen/PartialRegisterGeneralCodegen.cpp @@ -0,0 +1,85 @@ +#include + +#include +#include +#include + +#ifndef SIMDLIB_PARTIAL_GENERAL_CODEGEN_WIDTH +#error "SIMDLIB_PARTIAL_GENERAL_CODEGEN_WIDTH must select the fixture register width" +#endif + +constexpr std::size_t active_lanes = SIMDLIB_PARTIAL_GENERAL_CODEGEN_WIDTH == 256 ? 5 : 3; +using partial_u32 = SimdLib::PartialRegister; +using partial_f32 = SimdLib::PartialRegister; +#if SIMDLIB_PARTIAL_GENERAL_CODEGEN_WIDTH == 128 +using partial_u16 = SimdLib::PartialRegister; +#endif + +/** @brief Loads, offsets, and stores exactly the active logical extent. */ +extern "C" void simdlib_partial_general_codegen_transfer(const std::uint32_t *source, std::uint32_t *destination) noexcept +{ + const auto value = partial_u32::load(std::span{source, active_lanes}); + (value + partial_u32::broadcast(1U)).store(std::span{destination, active_lanes}); +} + +/** @brief Covers unary bitwise work and per-lane shifting. */ +extern "C" [[nodiscard]] partial_u32::native_type simdlib_partial_general_codegen_unary_shift(partial_u32 value, int count) noexcept +{ + return ((~value) << count).native; +} + +/** @brief Covers logical payload shifting with required suffix projection. */ +extern "C" [[nodiscard]] partial_u32::native_type simdlib_partial_general_codegen_payload_shift(partial_u32 value) noexcept +{ + return value.template shift_bytes_left<1>().native; +} + +/** @brief Covers the opposite logical payload-shift direction. */ +extern "C" [[nodiscard]] partial_u32::native_type simdlib_partial_general_codegen_payload_shift_right(partial_u32 value) noexcept +{ + return value.template shift_bytes_right<1>().native; +} + +/** @brief Covers active-prefix interleaving and required result projection. */ +extern "C" [[nodiscard]] partial_u32::native_type simdlib_partial_general_codegen_rearrange(partial_u32 lhs, partial_u32 rhs) noexcept +{ + return lhs.unpack_low(rhs).native; +} + +/** @brief Covers immediate-lane blending as a second rearrangement shape. */ +extern "C" [[nodiscard]] partial_u32::native_type simdlib_partial_general_codegen_blend(partial_u32 lhs, partial_u32 rhs) noexcept +{ + return lhs.template blend<0b0101>(rhs).native; +} + +/** @brief Covers a same-width type-changing result. */ +extern "C" [[nodiscard]] partial_u32::native_type simdlib_partial_general_codegen_convert(partial_u32 value) noexcept +{ + return value.template bit_cast().native; +} + +/** @brief Covers numeric conversion while preserving the active logical count. */ +extern "C" [[nodiscard]] partial_f32::native_type simdlib_partial_general_codegen_numeric_convert(partial_u32 value) noexcept +{ + return value.template convert().native; +} + +#if SIMDLIB_PARTIAL_GENERAL_CODEGEN_WIDTH == 128 +/** @brief Covers low-prefix widening into a wider integral lane type. */ +extern "C" [[nodiscard]] partial_u32::native_type simdlib_partial_general_codegen_widen_low(partial_u16 value) noexcept +{ + return value.template widen_low().native; +} +#endif + +/** @brief Covers native import normalization and complete-register export. */ +extern "C" [[nodiscard]] partial_u32::native_type simdlib_partial_general_codegen_native_transfer(partial_u32::native_type value) noexcept +{ + return partial_u32::from_native(value).to_register().native; +} + +/** @brief Covers comparison, predicate use, and a composed expression. */ +extern "C" [[nodiscard]] partial_u32::native_type simdlib_partial_general_codegen_composed(partial_u32 lhs, partial_u32 rhs) noexcept +{ + return (lhs.compare_greater(rhs).select(lhs, rhs) + rhs).native; +} diff --git a/tests/codegen/PartialRegisterGeneralCodegenRaw.cpp b/tests/codegen/PartialRegisterGeneralCodegenRaw.cpp new file mode 100644 index 0000000..a51fd86 --- /dev/null +++ b/tests/codegen/PartialRegisterGeneralCodegenRaw.cpp @@ -0,0 +1,106 @@ +#include + +#include +#include +#include +#include + +#ifndef SIMDLIB_PARTIAL_GENERAL_CODEGEN_WIDTH +#error "SIMDLIB_PARTIAL_GENERAL_CODEGEN_WIDTH must select the fixture register width" +#endif + +constexpr std::size_t active_lanes = SIMDLIB_PARTIAL_GENERAL_CODEGEN_WIDTH == 256 ? 5 : 3; +using api_u32 = SimdLib::Api; +using native_u32 = typename api_u32::vector_t; +using api_f32 = SimdLib::Api; +using native_f32 = typename api_f32::vector_t; +#if SIMDLIB_PARTIAL_GENERAL_CODEGEN_WIDTH == 128 +using api_u16 = SimdLib::Api<128, std::uint16_t>; +using native_u16 = typename api_u16::vector_t; +#endif + +/** @brief Compile-time active-lane filter shared by every raw normalization. */ +alignas(SIMDLIB_PARTIAL_GENERAL_CODEGEN_WIDTH / 8) constexpr auto active_lane_filter = [] +{ + std::array result{}; + for (std::size_t lane = 0; lane < active_lanes; ++lane) + result[lane] = ~std::uint32_t{}; + return result; +}(); + +/** @brief Applies the required inactive-suffix projection to a raw result. */ +[[nodiscard]] constexpr native_u32 normalize(native_u32 value) noexcept +{ + return api_u32::bitwise_and(value, api_u32::construct(active_lane_filter)); +} + +/** @brief Raw Api mirror for active-extent load, broadcast, addition, and store. */ +extern "C" void simdlib_partial_general_codegen_transfer(const std::uint32_t *source, std::uint32_t *destination) noexcept +{ + const auto value = api_u32::template load_partial(std::span{source, active_lanes}); + const auto one = api_u32::template broadcast_partial(1U); + api_u32::template store_partial(api_u32::add(value, one), std::span{destination, active_lanes}); +} + +/** @brief Raw Api mirror for unary bitwise work and per-lane shifting. */ +extern "C" [[nodiscard]] native_u32 simdlib_partial_general_codegen_unary_shift(native_u32 value, int count) noexcept +{ + return api_u32::shift_left(normalize(api_u32::bitwise_not(value)), count); +} + +/** @brief Raw Api mirror for projected logical-payload shifting. */ +extern "C" [[nodiscard]] native_u32 simdlib_partial_general_codegen_payload_shift(native_u32 value) noexcept +{ + return normalize(api_u32::template shift_bytes_left<1>(value)); +} + +/** @brief Raw Api mirror for the opposite projected payload shift. */ +extern "C" [[nodiscard]] native_u32 simdlib_partial_general_codegen_payload_shift_right(native_u32 value) noexcept +{ + return api_u32::template shift_bytes_right<1>(value); +} + +/** @brief Raw Api mirror for projected active-prefix interleaving. */ +extern "C" [[nodiscard]] native_u32 simdlib_partial_general_codegen_rearrange(native_u32 lhs, native_u32 rhs) noexcept +{ + return normalize(api_u32::unpack_lo(lhs, rhs)); +} + +/** @brief Raw Api mirror for immediate-lane blending. */ +extern "C" [[nodiscard]] native_u32 simdlib_partial_general_codegen_blend(native_u32 lhs, native_u32 rhs) noexcept +{ + return api_u32::template blend<0b0101>(lhs, rhs); +} + +/** @brief Raw Api mirror for a same-width type-changing result. */ +extern "C" [[nodiscard]] native_u32 simdlib_partial_general_codegen_convert(native_u32 value) noexcept +{ + return value; +} + +/** @brief Raw Api mirror for active-count-preserving numeric conversion. */ +extern "C" [[nodiscard]] native_f32 simdlib_partial_general_codegen_numeric_convert(native_u32 value) noexcept +{ + return api_u32::template convert(value); +} + +#if SIMDLIB_PARTIAL_GENERAL_CODEGEN_WIDTH == 128 +/** @brief Raw Api mirror for low-prefix integral widening. */ +extern "C" [[nodiscard]] native_u32 simdlib_partial_general_codegen_widen_low(native_u16 value) noexcept +{ + return api_u16::template widen(value); +} +#endif + +/** @brief Raw Api mirror for native import normalization and export. */ +extern "C" [[nodiscard]] native_u32 simdlib_partial_general_codegen_native_transfer(native_u32 value) noexcept +{ + return normalize(value); +} + +/** @brief Raw Api mirror for comparison, predicate selection, and composition. */ +extern "C" [[nodiscard]] native_u32 simdlib_partial_general_codegen_composed(native_u32 lhs, native_u32 rhs) noexcept +{ + const auto predicate = normalize(api_u32::compare_greater(lhs, rhs)); + return api_u32::add(api_u32::select(predicate, lhs, rhs), rhs); +} diff --git a/tests/codegen/PartialRegisterMaskCodegen.cpp b/tests/codegen/PartialRegisterMaskCodegen.cpp new file mode 100644 index 0000000..6794455 --- /dev/null +++ b/tests/codegen/PartialRegisterMaskCodegen.cpp @@ -0,0 +1,45 @@ +#include + +#include +#include + +#ifndef SIMDLIB_PARTIAL_MASK_CODEGEN_WIDTH +#error "SIMDLIB_PARTIAL_MASK_CODEGEN_WIDTH must select the fixture register width" +#endif + +constexpr std::size_t partial_codegen_active_lane_count = SIMDLIB_PARTIAL_MASK_CODEGEN_WIDTH == 256 ? 5 : 3; + +/** @brief Composes two partial predicates without introducing mutable predicate storage. */ +extern "C" [[nodiscard]] typename SimdLib::Api::vector_t simdlib_partial_mask_codegen_compose( + typename SimdLib::Api::vector_t lhs, + typename SimdLib::Api::vector_t rhs) noexcept +{ + using mask_t = SimdLib::PartialRegisterMask; + return (~(mask_t::from_native(lhs) ^ mask_t::from_native(rhs))).to_native(); +} + +/** @brief Imports one native value through the PartialRegister inactive-lane projection boundary. */ +extern "C" [[nodiscard]] typename SimdLib::Api::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_register_codegen_import(typename SimdLib::Api::vector_t native) noexcept +{ + using register_t = SimdLib::PartialRegister; + return register_t::from_native(native).to_native(); +} + +/** @brief Adds two already-canonical three-lane partial registers without suffix repair. */ +extern "C" [[nodiscard]] typename SimdLib::Api::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_register_codegen_add( + SimdLib::PartialRegister lhs, + SimdLib::PartialRegister rhs) noexcept +{ + return (lhs + rhs).native; +} + +/** @brief Divides three active lanes while neutralizing and clearing the inactive divisor suffix. */ +extern "C" [[nodiscard]] typename SimdLib::Api::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_register_codegen_divide( + SimdLib::PartialRegister lhs, + SimdLib::PartialRegister rhs) noexcept +{ + return (lhs / rhs).native; +} diff --git a/tests/codegen/PartialRegisterMaskCodegenRaw.cpp b/tests/codegen/PartialRegisterMaskCodegenRaw.cpp new file mode 100644 index 0000000..0452468 --- /dev/null +++ b/tests/codegen/PartialRegisterMaskCodegenRaw.cpp @@ -0,0 +1,82 @@ +#include + +#include +#include +#include + +#ifndef SIMDLIB_PARTIAL_MASK_CODEGEN_WIDTH +#error "SIMDLIB_PARTIAL_MASK_CODEGEN_WIDTH must select the fixture register width" +#endif + +constexpr std::size_t partial_codegen_active_lane_count = SIMDLIB_PARTIAL_MASK_CODEGEN_WIDTH == 256 ? 5 : 3; + +namespace +{ + +/** @brief Clears the inactive suffix required by the partial-predicate invariant. */ +[[nodiscard]] constexpr typename SimdLib::Api::vector_t normalize_partial_mask_native( + typename SimdLib::Api::vector_t native) noexcept +{ + using api_t = SimdLib::Api; + constexpr auto active_lane_filter = []() constexpr + { + std::array lanes{}; + for (std::size_t lane = 0; lane < partial_codegen_active_lane_count; ++lane) + lanes[lane] = 0xffffffffU; + return lanes; + }(); + return api_t::bitwise_and(native, api_t::construct(active_lane_filter)); +} + +} // namespace + +/** @brief Raw Api mirror for immutable partial-predicate composition with required suffix projection. */ +extern "C" [[nodiscard]] typename SimdLib::Api::vector_t simdlib_partial_mask_codegen_compose( + typename SimdLib::Api::vector_t lhs, + typename SimdLib::Api::vector_t rhs) noexcept +{ + using api_t = SimdLib::Api; + const auto normalized_rhs = normalize_partial_mask_native(rhs); + const auto normalized_lhs = normalize_partial_mask_native(lhs); + return normalize_partial_mask_native(api_t::bitwise_not(api_t::bitwise_xor(normalized_rhs, normalized_lhs))); +} + +/** @brief Raw Api mirror for the PartialRegister inactive-lane projection boundary. */ +extern "C" [[nodiscard]] typename SimdLib::Api::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_register_codegen_import(typename SimdLib::Api::vector_t native) noexcept +{ + return normalize_partial_mask_native(native); +} + +/** @brief Raw Api mirror for closed three-lane partial-register addition. */ +extern "C" [[nodiscard]] typename SimdLib::Api::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_register_codegen_add(typename SimdLib::Api::vector_t lhs, + typename SimdLib::Api::vector_t rhs) noexcept +{ + using api_t = SimdLib::Api; + return api_t::add(lhs, rhs); +} + +/** @brief Raw Api mirror for neutralized three-lane partial-register division. */ +extern "C" [[nodiscard]] typename SimdLib::Api::vector_t SIMDLIB_METHOD_FLAGS_SAFE_BUFFERS +simdlib_partial_register_codegen_divide(typename SimdLib::Api::vector_t lhs, + typename SimdLib::Api::vector_t rhs) noexcept +{ + using api_t = SimdLib::Api; + constexpr auto inactive_identity = []() constexpr + { + std::array lanes{}; + for (std::size_t lane = partial_codegen_active_lane_count; lane < api_t::element_count; ++lane) + lanes[lane] = 1; + return lanes; + }(); + constexpr auto active_filter = []() constexpr + { + std::array lanes{}; + for (std::size_t lane = 0; lane < partial_codegen_active_lane_count; ++lane) + lanes[lane] = -1; + return lanes; + }(); + const auto divisors = api_t::bitwise_or(rhs, api_t::construct(inactive_identity)); + return api_t::bitwise_and(api_t::divide(lhs, divisors), api_t::construct(active_filter)); +} diff --git a/tests/compile_fail/register/PartialRegisterExcessiveActiveCount.cpp b/tests/compile_fail/register/PartialRegisterExcessiveActiveCount.cpp new file mode 100644 index 0000000..73d4120 --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterExcessiveActiveCount.cpp @@ -0,0 +1,8 @@ +#define SIMDLIB_HAS_SSE42 1 +#include + +#include + +using invalid_partial_register = SimdLib::PartialRegister; + +invalid_partial_register value; diff --git a/tests/compile_fail/register/PartialRegisterFractionalBitCast.cpp b/tests/compile_fail/register/PartialRegisterFractionalBitCast.cpp new file mode 100644 index 0000000..b557857 --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterFractionalBitCast.cpp @@ -0,0 +1,9 @@ +#define SIMDLIB_HAS_SSE42 1 +#include +#include + +#include + +using register_type = SimdLib::PartialRegister; + +static_assert(SimdLib::IRegister::BitCast, "SIMDLIB_PARTIAL_REGISTER_REJECTS_FRACTIONAL_BIT_CAST_RESULT"); diff --git a/tests/compile_fail/register/PartialRegisterFullTransferExtent.cpp b/tests/compile_fail/register/PartialRegisterFullTransferExtent.cpp new file mode 100644 index 0000000..a099db2 --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterFullTransferExtent.cpp @@ -0,0 +1,39 @@ +#define SIMDLIB_HAS_SSE42 1 +#include + +#include +#include + +using partial_register_type = SimdLib::PartialRegister; + +/** @brief Reports whether a full-native-width element load bypasses the active transfer extent. */ +template +concept accepts_full_element_load = requires(std::span source) { value_t::load(source); }; + +/** @brief Reports whether a full-native-width aligned element load bypasses the active transfer extent. */ +template +concept accepts_full_aligned_element_load = + requires(std::span source) { value_t::load_aligned(source); }; + +/** @brief Reports whether a full-native-width byte load bypasses the active byte extent. */ +template +concept accepts_full_byte_load = requires(std::span source) { value_t::load_bytes(source); }; + +/** @brief Reports whether a full-native-width element store bypasses the active transfer extent. */ +template +concept accepts_full_element_store = + requires(value_t value, std::span destination) { value.store(destination); }; + +/** @brief Reports whether a full-native-width aligned element store bypasses the active transfer extent. */ +template +concept accepts_full_aligned_element_store = + requires(value_t value, std::span destination) { value.store_aligned(destination); }; + +/** @brief Reports whether a full-native-width byte store bypasses the active byte extent. */ +template +concept accepts_full_byte_store = requires(value_t value, std::span destination) { value.store_bytes(destination); }; + +static_assert(accepts_full_element_load || accepts_full_aligned_element_load || + accepts_full_byte_load || accepts_full_element_store || + accepts_full_aligned_element_store || accepts_full_byte_store, + "SIMDLIB_PARTIAL_REGISTER_REJECTS_FULL_TRANSFER_EXTENT"); diff --git a/tests/compile_fail/register/PartialRegisterHeaderCxx20.cpp b/tests/compile_fail/register/PartialRegisterHeaderCxx20.cpp new file mode 100644 index 0000000..bd51f6c --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterHeaderCxx20.cpp @@ -0,0 +1,7 @@ +#include + +/** @brief Provides an entry point when an invalid PartialRegister header inclusion unexpectedly compiles. */ +int main() +{ + return 0; +} diff --git a/tests/compile_fail/register/PartialRegisterImplicitNative.cpp b/tests/compile_fail/register/PartialRegisterImplicitNative.cpp new file mode 100644 index 0000000..0e56ba6 --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterImplicitNative.cpp @@ -0,0 +1,9 @@ +#define SIMDLIB_HAS_SSE42 1 +#include + +#include +#include + +using partial_register_type = SimdLib::PartialRegister; + +static_assert(std::is_convertible_v, "SIMDLIB_PARTIAL_REGISTER_REJECTS_IMPLICIT_NATIVE"); diff --git a/tests/compile_fail/register/PartialRegisterImplicitScalar.cpp b/tests/compile_fail/register/PartialRegisterImplicitScalar.cpp new file mode 100644 index 0000000..df59915 --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterImplicitScalar.cpp @@ -0,0 +1,9 @@ +#define SIMDLIB_HAS_SSE42 1 +#include + +#include +#include + +using partial_register_type = SimdLib::PartialRegister; + +static_assert(std::is_convertible_v, "SIMDLIB_PARTIAL_REGISTER_REJECTS_IMPLICIT_SCALAR"); diff --git a/tests/compile_fail/register/PartialRegisterInactiveLaneIndex.cpp b/tests/compile_fail/register/PartialRegisterInactiveLaneIndex.cpp new file mode 100644 index 0000000..0d0fb93 --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterInactiveLaneIndex.cpp @@ -0,0 +1,17 @@ +#define SIMDLIB_HAS_SSE42 1 +#include + +#include + +using partial_register_type = SimdLib::PartialRegister; + +/** @brief Reports whether observation accepts the first inactive lane index. */ +template +concept observes_inactive_lane = requires(value_t value) { value.template lane(); }; + +/** @brief Reports whether replacement accepts the first inactive lane index. */ +template +concept replaces_inactive_lane = requires(value_t value) { value.template with_lane(typename value_t::element_type{}); }; + +static_assert(observes_inactive_lane || replaces_inactive_lane, + "SIMDLIB_PARTIAL_REGISTER_REJECTS_INACTIVE_LANE_INDEX"); diff --git a/tests/compile_fail/register/PartialRegisterInactiveUpperHalf.cpp b/tests/compile_fail/register/PartialRegisterInactiveUpperHalf.cpp new file mode 100644 index 0000000..a86f990 --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterInactiveUpperHalf.cpp @@ -0,0 +1,14 @@ +#include + +#include + +#ifndef SIMDLIB_PARTIAL_REGISTER_REJECTS_INACTIVE_UPPER_HALF +#error "SIMDLIB_PARTIAL_REGISTER_REJECTS_INACTIVE_UPPER_HALF" +#endif + +using invalid_partial_register = SimdLib::PartialRegister; + +int main() +{ + return static_cast(sizeof(invalid_partial_register)); +} diff --git a/tests/compile_fail/register/PartialRegisterInactiveWidenResult.cpp b/tests/compile_fail/register/PartialRegisterInactiveWidenResult.cpp new file mode 100644 index 0000000..c380386 --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterInactiveWidenResult.cpp @@ -0,0 +1,10 @@ +#define SIMDLIB_HAS_SSE42 1 +#define SIMDLIB_HAS_AVX2 1 +#include +#include + +#include + +using register_type = SimdLib::PartialRegister; + +static_assert(SimdLib::IRegister::WidenLow, "SIMDLIB_PARTIAL_REGISTER_REJECTS_INACTIVE_UPPER_HALF_WIDEN_RESULT"); diff --git a/tests/compile_fail/register/PartialRegisterInvalidByteShuffleSelector.cpp b/tests/compile_fail/register/PartialRegisterInvalidByteShuffleSelector.cpp new file mode 100644 index 0000000..d5914c7 --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterInvalidByteShuffleSelector.cpp @@ -0,0 +1,9 @@ +#define SIMDLIB_HAS_SSE42 1 +#include +#include + +#include + +using register_type = SimdLib::PartialRegister; + +static_assert(SimdLib::IRegister::ShuffleBytes, "SIMDLIB_PARTIAL_REGISTER_REJECTS_INVALID_BYTE_SHUFFLE_SELECTOR"); diff --git a/tests/compile_fail/register/PartialRegisterInvalidShuffleSelector.cpp b/tests/compile_fail/register/PartialRegisterInvalidShuffleSelector.cpp new file mode 100644 index 0000000..22f7715 --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterInvalidShuffleSelector.cpp @@ -0,0 +1,9 @@ +#define SIMDLIB_HAS_SSE42 1 +#include +#include + +#include + +using register_type = SimdLib::PartialRegister; + +static_assert(SimdLib::IRegister::Shuffle, "SIMDLIB_PARTIAL_REGISTER_REJECTS_INVALID_SHUFFLE_SELECTOR"); diff --git a/tests/compile_fail/register/PartialRegisterInvalidShuffleSelectorCount.cpp b/tests/compile_fail/register/PartialRegisterInvalidShuffleSelectorCount.cpp new file mode 100644 index 0000000..f4f4b55 --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterInvalidShuffleSelectorCount.cpp @@ -0,0 +1,9 @@ +#define SIMDLIB_HAS_SSE42 1 +#include +#include + +#include + +using register_type = SimdLib::PartialRegister; + +static_assert(SimdLib::IRegister::Shuffle, "SIMDLIB_PARTIAL_REGISTER_REJECTS_INVALID_SHUFFLE_SELECTOR_COUNT"); diff --git a/tests/compile_fail/register/PartialRegisterMaskInvalidActiveCount.cpp b/tests/compile_fail/register/PartialRegisterMaskInvalidActiveCount.cpp new file mode 100644 index 0000000..0884956 --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterMaskInvalidActiveCount.cpp @@ -0,0 +1,5 @@ +#include + +#include + +using InvalidPartialRegisterMask = SimdLib::PartialRegisterMask; diff --git a/tests/compile_fail/register/PartialRegisterNegativeBitShiftLeft.cpp b/tests/compile_fail/register/PartialRegisterNegativeBitShiftLeft.cpp new file mode 100644 index 0000000..14b6df1 --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterNegativeBitShiftLeft.cpp @@ -0,0 +1,12 @@ +#define SIMDLIB_HAS_SSE42 1 +#include + +#include + +using register_type = SimdLib::PartialRegister; + +/** @brief Instantiates an invalid negative PartialRegister immediate bit-left count. */ +void invalid_negative_partial_bit_left_shift(register_type value) +{ + (void)value.template shift_bits_left<-1>(); +} diff --git a/tests/compile_fail/register/PartialRegisterNegativeBitShiftRight.cpp b/tests/compile_fail/register/PartialRegisterNegativeBitShiftRight.cpp new file mode 100644 index 0000000..8adb085 --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterNegativeBitShiftRight.cpp @@ -0,0 +1,12 @@ +#define SIMDLIB_HAS_SSE42 1 +#include + +#include + +using register_type = SimdLib::PartialRegister; + +/** @brief Instantiates an invalid negative PartialRegister immediate bit-right count. */ +void invalid_negative_partial_bit_right_shift(register_type value) +{ + (void)value.template shift_bits_right<-1>(); +} diff --git a/tests/compile_fail/register/PartialRegisterNegativeByteShiftLeft.cpp b/tests/compile_fail/register/PartialRegisterNegativeByteShiftLeft.cpp new file mode 100644 index 0000000..f970493 --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterNegativeByteShiftLeft.cpp @@ -0,0 +1,12 @@ +#define SIMDLIB_HAS_SSE42 1 +#include + +#include + +using register_type = SimdLib::PartialRegister; + +/** @brief Instantiates an invalid negative PartialRegister immediate byte-left count. */ +void invalid_negative_partial_byte_left_shift(register_type value) +{ + (void)value.template shift_bytes_left<-1>(); +} diff --git a/tests/compile_fail/register/PartialRegisterNegativeByteShiftRight.cpp b/tests/compile_fail/register/PartialRegisterNegativeByteShiftRight.cpp new file mode 100644 index 0000000..7dfddca --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterNegativeByteShiftRight.cpp @@ -0,0 +1,12 @@ +#define SIMDLIB_HAS_SSE42 1 +#include + +#include + +using register_type = SimdLib::PartialRegister; + +/** @brief Instantiates an invalid negative PartialRegister immediate byte-right count. */ +void invalid_negative_partial_byte_right_shift(register_type value) +{ + (void)value.template shift_bytes_right<-1>(); +} diff --git a/tests/compile_fail/register/PartialRegisterOversizedLaneList.cpp b/tests/compile_fail/register/PartialRegisterOversizedLaneList.cpp new file mode 100644 index 0000000..3b2b0cb --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterOversizedLaneList.cpp @@ -0,0 +1,12 @@ +#define SIMDLIB_HAS_SSE42 1 +#include + +#include + +using partial_register_type = SimdLib::PartialRegister; + +/** @brief Reports whether an oversized active lane list is accepted. */ +template +concept accepts_oversized_lane_list = requires { value_t::from_lanes(1, 2, 3, 4); }; + +static_assert(accepts_oversized_lane_list, "SIMDLIB_PARTIAL_REGISTER_REJECTS_OVERSIZED_LANE_LIST"); diff --git a/tests/compile_fail/register/PartialRegisterRequirementCxx20.cpp b/tests/compile_fail/register/PartialRegisterRequirementCxx20.cpp new file mode 100644 index 0000000..02ceaaf --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterRequirementCxx20.cpp @@ -0,0 +1,8 @@ +#define SIMDLIB_REQUIRE_REGISTER_INTERFACE 1 +#include + +/** @brief Provides an entry point when an unavailable PartialRegister requirement unexpectedly compiles. */ +int main() +{ + return 0; +} diff --git a/tests/compile_fail/register/PartialRegisterUnavailableWidth.cpp b/tests/compile_fail/register/PartialRegisterUnavailableWidth.cpp new file mode 100644 index 0000000..082193c --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterUnavailableWidth.cpp @@ -0,0 +1,9 @@ +#define SIMDLIB_HAS_SSE42 1 +#define SIMDLIB_HAS_AVX2 0 +#include + +#include + +using unavailable_partial_register = SimdLib::PartialRegister; + +unavailable_partial_register value; diff --git a/tests/compile_fail/register/PartialRegisterZeroActiveCount.cpp b/tests/compile_fail/register/PartialRegisterZeroActiveCount.cpp new file mode 100644 index 0000000..82f4564 --- /dev/null +++ b/tests/compile_fail/register/PartialRegisterZeroActiveCount.cpp @@ -0,0 +1,8 @@ +#define SIMDLIB_HAS_SSE42 1 +#include + +#include + +using invalid_partial_register = SimdLib::PartialRegister; + +invalid_partial_register value; diff --git a/tests/constexpr/ApiConstexprContracts.h b/tests/constexpr/ApiConstexprContracts.h index 7b09094..2f99a18 100644 --- a/tests/constexpr/ApiConstexprContracts.h +++ b/tests/constexpr/ApiConstexprContracts.h @@ -127,9 +127,29 @@ template [[nodiscard]] consteval bool constru if (simd::to_array(constructed) != values) return false; constexpr auto partial = simd::template load_partial(std::span{values}); + constexpr auto alignedPartial = simd::template load_partial_aligned(std::span{values}); auto partialExpected = values; partialExpected.back() = Element{}; - if (simd::to_array(partial) != partialExpected) + if (simd::to_array(partial) != partialExpected || simd::to_array(alignedPartial) != partialExpected || + simd::template to_array_partial(partial) != + [](const auto &source, std::index_sequence) constexpr noexcept + { return std::array{source[indices]...}; }(values, std::make_index_sequence{})) + return false; + std::array partialStored{}; + partialStored.fill(static_cast(99)); + simd::template store_partial(partial, std::span{partialStored}); + for (std::size_t index = 0; index < simd::element_count - 1; ++index) + if (partialStored[index] != values[index]) + return false; + if (partialStored.back() != static_cast(99)) + return false; + std::array alignedPartialStored{}; + alignedPartialStored.fill(static_cast(98)); + simd::template store_partial_aligned(partial, std::span{alignedPartialStored}); + for (std::size_t index = 0; index < simd::element_count - 1; ++index) + if (alignedPartialStored[index] != values[index]) + return false; + if (alignedPartialStored.back() != static_cast(98)) return false; if (simd::to_array(simd::setzero()) != std::array{}) return false; @@ -138,6 +158,10 @@ template [[nodiscard]] consteval bool constru broadcastExpected.fill(static_cast(7)); if (simd::to_array(simd::set1(static_cast(7))) != broadcastExpected) return false; + auto partialBroadcastExpected = broadcastExpected; + partialBroadcastExpected.back() = Element{}; + if (simd::to_array(simd::template broadcast_partial(static_cast(7))) != partialBroadcastExpected) + return false; constexpr auto setrValue = [](std::index_sequence) constexpr noexcept { return simd::setr(static_cast(Indices + 1)...); }(std::make_index_sequence{}); diff --git a/tests/constexpr/PartialRegisterConstexpr.tests.cpp b/tests/constexpr/PartialRegisterConstexpr.tests.cpp new file mode 100644 index 0000000..c785601 --- /dev/null +++ b/tests/constexpr/PartialRegisterConstexpr.tests.cpp @@ -0,0 +1,244 @@ +#include + +#include +#include +#include +#include +#include +#include + +#ifndef SIMDLIB_PARTIAL_REGISTER_CONSTEXPR_ENABLE_256 +#define SIMDLIB_PARTIAL_REGISTER_CONSTEXPR_ENABLE_256 0 +#endif + +namespace +{ + +/** @brief Reports whether one scalar value has an all-bits-zero representation. */ +template [[nodiscard]] consteval bool has_zero_bits(element_t value) noexcept +{ + const auto bytes = std::bit_cast>(value); + for (const auto byte : bytes) + if (byte != std::byte{}) + return false; + return true; +} + +/** @brief Verifies constant-evaluated partial construction and active-only observation. */ +consteval bool has_constexpr_partial_construction() noexcept +{ + using value_t = SimdLib::PartialRegister; + using api_t = typename value_t::api_type; + constexpr std::array source{1U, 2U, 3U}; + const auto zero = value_t::zero(); + const auto broadcast = value_t::broadcast(7U); + const auto listed = value_t::from_lanes(1U, 2U, 3U); + const auto array_value = value_t::from_array(source); + const auto listed_native = api_t::to_array(listed.to_native()); + return zero.to_array() == std::array{} && broadcast.to_array() == std::array{7U, 7U, 7U} && + listed.to_array() == source && array_value.to_array() == source && listed_native[0] == 1U && listed_native[1] == 2U && listed_native[2] == 3U && + has_zero_bits(listed_native[3]); +} + +/** @brief Verifies constant-evaluated active lane extraction and replacement. */ +consteval bool has_constexpr_partial_lane_access() noexcept +{ + using value_t = SimdLib::PartialRegister; + using api_t = typename value_t::api_type; + const auto original = value_t::from_lanes(11U); + const auto replaced = original.template with_lane<0>(29U); + const auto native = api_t::to_array(replaced.to_native()); + return original.template lane<0>() == 11U && replaced.template lane<0>() == 29U && native[0] == 29U && has_zero_bits(native[1]); +} + +/** @brief Verifies constant-evaluated extrema positions ignore the inactive zero suffix. */ +consteval bool has_constexpr_partial_positions() noexcept +{ + using value_t = SimdLib::PartialRegister; + const auto value = value_t::from_lanes(5, 2, 9); + const auto ties = value_t::from_lanes(4, 4, 4); + return value.min_position() == 1 && value.max_position() == 2 && ties.min_position() == 0 && ties.max_position() == 0; +} + +/** @brief Reports whether a partial register or predicate has a bitwise-zero inactive native suffix. */ +template [[nodiscard]] consteval bool has_zero_native_suffix(value_t value) noexcept +{ + using api_t = typename value_t::api_type; + const auto native_lanes = api_t::to_array(value.to_native()); + for (std::size_t lane = value_t::lane_count; lane < value_t::native_lane_count; ++lane) + if (!has_zero_bits(native_lanes[lane])) + return false; + return true; +} + +/** @brief Verifies every constant-evaluated bitwise and sign-mask operation over the logical prefix. */ +consteval bool has_constexpr_partial_bitwise_operations() noexcept +{ + using value_t = SimdLib::PartialRegister; + const auto lhs = value_t::from_lanes(1U, 2U, 4U); + const auto rhs = value_t::from_lanes(3U, 2U, 1U); + const auto intersection = lhs & rhs; + const auto union_value = lhs | rhs; + const auto exclusive = lhs ^ rhs; + const auto complement = ~lhs; + const auto difference = lhs.andnot(rhs); + using signed_value_t = SimdLib::PartialRegister; + const auto signed_value = signed_value_t::from_lanes(-1, 2, -3); + return intersection.to_array() == std::array{1U, 2U, 0U} && union_value.to_array() == std::array{3U, 2U, 5U} && + exclusive.to_array() == std::array{2U, 0U, 5U} && complement.to_array() == std::array{~1U, ~2U, ~4U} && + difference.to_array() == std::array{2U, 0U, 1U} && signed_value.movemask() == 0x0f0f && signed_value.lane_sign_bits() == 0b101 && + has_zero_native_suffix(intersection) && has_zero_native_suffix(union_value) && has_zero_native_suffix(exclusive) && + has_zero_native_suffix(complement) && has_zero_native_suffix(difference); +} + +/** @brief Verifies every constant-evaluated per-lane shift form and its inactive suffix. */ +consteval bool has_constexpr_partial_lane_shifts() noexcept +{ + using unsigned_value_t = SimdLib::PartialRegister; + const auto unsigned_value = unsigned_value_t::from_lanes(1U, 2U, 4U); + const auto left = unsigned_value << 1; + const auto logical_right = unsigned_value.logical_shift_right(1); + const auto unsigned_right = unsigned_value >> 1; + using signed_value_t = SimdLib::PartialRegister; + const auto signed_value = signed_value_t::from_lanes(-8, 8, -1); + const auto signed_logical_right = signed_value.logical_shift_right(1); + const auto signed_right = signed_value >> 1; + return left.to_array() == std::array{2U, 4U, 8U} && logical_right.to_array() == std::array{0U, 1U, 2U} && + unsigned_right.to_array() == logical_right.to_array() && signed_logical_right.to_array() == std::array{0x7ffffffc, 4, 0x7fffffff} && + signed_right.to_array() == std::array{-4, 4, -1} && has_zero_native_suffix(left) && has_zero_native_suffix(logical_right) && + has_zero_native_suffix(unsigned_right) && has_zero_native_suffix(signed_logical_right) && has_zero_native_suffix(signed_right); +} + +/** @brief Verifies every runtime-count and immediate-count whole-payload shift form over an awkward active byte extent. */ +consteval bool has_constexpr_partial_payload_shifts() noexcept +{ + using value_t = SimdLib::PartialRegister; + std::array source{}; + for (std::size_t lane = 0; lane < source.size(); ++lane) + source[lane] = static_cast(lane + 1); + source.back() = 0xf1U; + const auto value = value_t::from_array(source); + std::array expected_bytes_left{}; + std::array expected_bytes_right{}; + for (std::size_t lane = 3; lane < source.size(); ++lane) + expected_bytes_left[lane] = source[lane - 3]; + for (std::size_t lane = 0; lane + 3 < source.size(); ++lane) + expected_bytes_right[lane] = source[lane + 3]; + std::array expected_bits_left{}; + std::array expected_bits_right{}; + for (std::size_t lane = 0; lane < source.size(); ++lane) + { + expected_bits_left[lane] = static_cast(source[lane] << 4); + if (lane != 0) + expected_bits_left[lane] = static_cast(expected_bits_left[lane] | (source[lane - 1] >> 4)); + expected_bits_right[lane] = static_cast(source[lane] >> 4); + if (lane + 1 < source.size()) + expected_bits_right[lane] = static_cast(expected_bits_right[lane] | (source[lane + 1] << 4)); + } + const auto bytes_left_slow = value.shift_bytes_left_slow(3); + const auto bytes_right_slow = value.shift_bytes_right_slow(3); + const auto bytes_left = value.template shift_bytes_left<3>(); + const auto bytes_right = value.template shift_bytes_right<3>(); + const auto bits_left_slow = value.shift_bits_left_slow(4); + const auto bits_right_slow = value.shift_bits_right_slow(4); + const auto bits_left = value.template shift_bits_left<4>(); + const auto bits_right = value.template shift_bits_right<4>(); + return bytes_left_slow.to_array() == expected_bytes_left && bytes_right_slow.to_array() == expected_bytes_right && + bytes_left.to_array() == expected_bytes_left && bytes_right.to_array() == expected_bytes_right && bits_left_slow.to_array() == expected_bits_left && + bits_right_slow.to_array() == expected_bits_right && bits_left.to_array() == expected_bits_left && bits_right.to_array() == expected_bits_right && + has_zero_native_suffix(bytes_left_slow) && has_zero_native_suffix(bytes_right_slow) && has_zero_native_suffix(bytes_left) && + has_zero_native_suffix(bytes_right) && has_zero_native_suffix(bits_left_slow) && has_zero_native_suffix(bits_right_slow) && + has_zero_native_suffix(bits_left) && has_zero_native_suffix(bits_right); +} + +/** @brief Verifies every constant-evaluated active-only comparison and false inactive predicate suffix. */ +consteval bool has_constexpr_partial_comparisons() noexcept +{ + using value_t = SimdLib::PartialRegister; + const auto lhs = value_t::from_lanes(1U, 2U, 4U); + const auto rhs = value_t::from_lanes(3U, 2U, 1U); + const auto equal = lhs.compare_equal(rhs); + const auto greater = lhs.compare_greater(rhs); + const auto greater_equal = lhs.compare_greater_equal(rhs); + const auto less = lhs.compare_less(rhs); + const auto less_equal = lhs.compare_less_equal(rhs); + return equal.bits() == 0b010U && greater.bits() == 0b100U && greater_equal.bits() == 0b110U && less.bits() == 0b001U && less_equal.bits() == 0b011U && + lhs != rhs && lhs == lhs && has_zero_native_suffix(equal) && has_zero_native_suffix(greater) && has_zero_native_suffix(greater_equal) && + has_zero_native_suffix(less) && has_zero_native_suffix(less_equal); +} + +/** @brief Verifies constant-evaluated scalar equality preserves floating NaN and signed-zero semantics. */ +consteval bool has_constexpr_partial_floating_equality() noexcept +{ + using value_t = SimdLib::PartialRegister; + const auto positive_zero = value_t::from_lanes(0.0F, 2.0F, 3.0F); + const auto negative_zero = value_t::from_lanes(-0.0F, 2.0F, 3.0F); + const auto nan_value = value_t::from_lanes(std::numeric_limits::quiet_NaN(), 2.0F, 3.0F); + return positive_zero == negative_zero && !(nan_value == nan_value) && nan_value != nan_value; +} + +/** @brief Verifies constant-evaluated rearrangements retain only their documented logical prefix. */ +consteval bool has_constexpr_partial_rearrangements() noexcept +{ + using lane_value_t = SimdLib::PartialRegister; + const auto lhs = lane_value_t::from_lanes(1U, 2U, 3U); + const auto rhs = lane_value_t::from_lanes(4U, 5U, 6U); + const auto shuffled = lhs.template shuffle<2, 0, 1>(); + const auto low = lhs.unpack_low(rhs); + const auto high = lhs.unpack_high(rhs); + const auto blended = lhs.template blend<0b101>(rhs); + using byte_value_t = SimdLib::PartialRegister; + const auto bytes = byte_value_t::from_lanes(1U, 2U, 3U).template shuffle_bytes<2, 1, 0>(); + using half_value_t = SimdLib::PartialRegister; + const auto half = half_value_t::from_lanes(1, 2, 3, 4, 5).template shuffle_low<0x1b>(); + return shuffled.to_array() == std::array{3U, 1U, 2U} && low.to_array() == std::array{1U, 4U, 2U} && + high.to_array() == std::array{3U, 6U, 0U} && blended.to_array() == std::array{4U, 2U, 6U} && + bytes.to_array() == std::array{3U, 2U, 1U} && half.to_array() == std::array{4, 3, 2, 1, 5} && + has_zero_native_suffix(shuffled) && has_zero_native_suffix(low) && has_zero_native_suffix(high) && has_zero_native_suffix(blended) && + has_zero_native_suffix(bytes) && has_zero_native_suffix(half); +} + +/** @brief Verifies constant-evaluated conversions derive and preserve their meaningful target-lane counts. */ +consteval bool has_constexpr_partial_conversions() noexcept +{ + using byte_value_t = SimdLib::PartialRegister; + const auto cast = byte_value_t::from_lanes(1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U).template bit_cast(); + using integer_value_t = SimdLib::PartialRegister; + const auto converted = integer_value_t::from_lanes(-2, 0, 3).template convert(); + using narrow_value_t = SimdLib::PartialRegister; + const auto widened = narrow_value_t::from_lanes(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13).template widen_low(); + return cast.to_array() == std::array{0x04030201U, 0x08070605U} && converted.to_array() == std::array{-2.0F, 0.0F, 3.0F} && + widened.to_array() == std::array{1, 2, 3, 4, 5, 6, 7, 8} && has_zero_native_suffix(cast) && has_zero_native_suffix(converted); +} + +#if SIMDLIB_PARTIAL_REGISTER_CONSTEXPR_ENABLE_256 +/** @brief Verifies constant-evaluated 256-bit cross-half rearrangement and low-half extraction. */ +consteval bool has_constexpr_partial_256_rearrangements() noexcept +{ + using value_t = SimdLib::PartialRegister; + const auto value = value_t::from_lanes(1U, 2U, 3U, 4U, 5U); + const auto shuffled = value.template shuffle<4, 3, 2, 1, 0>(); + const auto lower = value.lower_half(); + return shuffled.to_array() == std::array{5U, 4U, 3U, 2U, 1U} && lower.to_array() == std::array{1U, 2U, 3U, 4U} && + has_zero_native_suffix(shuffled); +} +#endif + +static_assert(has_constexpr_partial_construction()); +static_assert(has_constexpr_partial_lane_access()); +static_assert(has_constexpr_partial_positions()); +// MSVC 19.44 ICEs while inspecting new explicit-object PartialRegister operation results in constant evaluation. +#if !defined(_MSC_VER) || defined(__clang__) +static_assert(has_constexpr_partial_bitwise_operations()); +static_assert(has_constexpr_partial_lane_shifts()); +static_assert(has_constexpr_partial_payload_shifts()); +static_assert(has_constexpr_partial_comparisons()); +static_assert(has_constexpr_partial_floating_equality()); +static_assert(has_constexpr_partial_rearrangements()); +static_assert(has_constexpr_partial_conversions()); +#if SIMDLIB_PARTIAL_REGISTER_CONSTEXPR_ENABLE_256 +static_assert(has_constexpr_partial_256_rearrangements()); +#endif +#endif + +} // namespace diff --git a/tests/constexpr/PartialRegisterMaskConstexpr.tests.cpp b/tests/constexpr/PartialRegisterMaskConstexpr.tests.cpp new file mode 100644 index 0000000..91065ea --- /dev/null +++ b/tests/constexpr/PartialRegisterMaskConstexpr.tests.cpp @@ -0,0 +1,55 @@ +#include + +#include +#include + +namespace +{ + +/** @brief Creates a canonical partial predicate for constant-evaluation tests. */ +consteval auto constexpr_true_mask() +{ + using mask_t = SimdLib::PartialRegisterMask; + using api_t = typename mask_t::api_type; + constexpr std::array all_true{0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU}; + return mask_t::from_native(api_t::construct(all_true)); +} + +/** @brief Verifies active-only predicate reductions during constant evaluation. */ +consteval bool has_constexpr_partial_mask_reductions() +{ + const auto true_mask = constexpr_true_mask(); + return true_mask.bits() == 0x7U && true_mask.any() && true_mask.all() && !true_mask.none(); +} + +/** @brief Verifies immutable predicate composition during constant evaluation. */ +consteval bool has_constexpr_partial_mask_composition() +{ + const auto true_mask = constexpr_true_mask(); + return (true_mask ^ true_mask).none(); +} + +/** @brief Verifies mixed partial-predicate selection during constant evaluation. */ +consteval bool has_constexpr_partial_mask_selection() +{ + using mask_t = SimdLib::PartialRegisterMask; + using value_t = typename mask_t::register_type; + using api_t = typename mask_t::api_type; + constexpr std::array mixed{0xffffffffffffffffULL, 0U}; + constexpr std::array when_true{1U, 2U}; + constexpr std::array when_false{5U, 6U}; + const auto mixed_mask = mask_t::from_native(api_t::construct(mixed)); + const auto selected = mixed_mask.select(value_t::from_native(api_t::construct(when_true)), value_t::from_native(api_t::construct(when_false))); + const auto lanes = api_t::to_array(selected.to_native()); + return lanes[0] == 1U && lanes[1] == 0U; +} + +static_assert(has_constexpr_partial_mask_reductions()); + +// MSVC 19.44 ICEs while inspecting a PartialRegisterMask result from composition or select() in constant evaluation. +#if !defined(_MSC_VER) || defined(__clang__) +static_assert(has_constexpr_partial_mask_composition()); +static_assert(has_constexpr_partial_mask_selection()); +#endif + +} // namespace diff --git a/tests/consumer/register.cpp b/tests/consumer/register.cpp index a0e573a..699daee 100644 --- a/tests/consumer/register.cpp +++ b/tests/consumer/register.cpp @@ -1,5 +1,10 @@ #include "register_api.h" +#include +#include +#include +#include + #if !SIMDLIB_REQUIRE_REGISTER_INTERFACE #error "The Register target must publish its requirement signal to consumers" #endif @@ -26,5 +31,18 @@ int main() } const auto native = increment_native(_mm_set1_epi32(3)); - return _mm_cvtsi128_si32(native) == 4 ? 0 : 2; + if (_mm_cvtsi128_si32(native) != 4) + return 2; + + const std::array source{2, 4, 8}; + const PartialRegister partial = increment_partial(PartialRegister::load(std::span{source})); + std::array stored{}; + partial.store(std::span{stored}); + if (stored != std::array{3, 5, 9}) + return 3; + + const auto native_lanes = PartialRegister::api_type::to_array(partial.to_native()); + if (std::bit_cast>(native_lanes[3]) != std::array{}) + return 4; + return 0; } diff --git a/tests/consumer/register_api.cpp b/tests/consumer/register_api.cpp index 341f8f3..086f676 100644 --- a/tests/consumer/register_api.cpp +++ b/tests/consumer/register_api.cpp @@ -13,4 +13,10 @@ native_type SIMD_FLAGS(InOut, RegisterOnly) increment_native(native_type value) { return _mm_add_epi32(value, _mm_set1_epi32(1)); } + +/** Defines the downstream PartialRegister boundary in a separate translation unit. */ +PartialRegister SIMD_FLAGS(InOut, RegisterOnly) increment_partial(PartialRegister value) noexcept +{ + return value + PartialRegister::broadcast(1); +} } // namespace SimdLibConsumer diff --git a/tests/consumer/register_api.h b/tests/consumer/register_api.h index 65eb593..29cb151 100644 --- a/tests/consumer/register_api.h +++ b/tests/consumer/register_api.h @@ -9,6 +9,7 @@ namespace SimdLibConsumer { using Register = SimdLib::Register; using RegisterMask = Register::mask_type; +using PartialRegister = SimdLib::partial_uint32x4<3>; using native_type = __m128i; /** @@ -24,4 +25,11 @@ using native_type = __m128i; * @return Input register increased by one in every lane. */ [[nodiscard]] native_type SIMD_FLAGS(InOut, RegisterOnly) increment_native(native_type value) noexcept; + +/** + * @brief Increments every active lane of a downstream PartialRegister value. + * @param value Input value with three active lanes and a zero inactive suffix. + * @return Active lanes increased by one with the inactive suffix still bitwise zero. + */ +[[nodiscard]] PartialRegister SIMD_FLAGS(InOut, RegisterOnly) increment_partial(PartialRegister value) noexcept; } // namespace SimdLibConsumer diff --git a/tests/headers/AliasesHeaderProbe.cpp b/tests/headers/AliasesHeaderProbe.cpp index 7eb4b04..4cb2f50 100644 --- a/tests/headers/AliasesHeaderProbe.cpp +++ b/tests/headers/AliasesHeaderProbe.cpp @@ -15,3 +15,8 @@ static_assert(std::same_as static_assert(std::same_as>); static_assert(std::same_as>); static_assert(std::same_as>); + +static_assert(std::same_as, SimdLib::PartialRegister>); +static_assert(std::same_as, SimdLib::PartialRegister>); +static_assert(std::same_as, SimdLib::PartialRegister>); +static_assert(std::same_as, SimdLib::PartialRegister>); diff --git a/tests/headers/IPartialRegisterMaskHeaderProbe.cpp b/tests/headers/IPartialRegisterMaskHeaderProbe.cpp new file mode 100644 index 0000000..b5f7ef2 --- /dev/null +++ b/tests/headers/IPartialRegisterMaskHeaderProbe.cpp @@ -0,0 +1,3 @@ +#include + +static_assert(!SimdLib::IPartialRegisterMask::Type); diff --git a/tests/headers/InstalledPartialRegisterHeaderProbe.cpp b/tests/headers/InstalledPartialRegisterHeaderProbe.cpp new file mode 100644 index 0000000..b8515cd --- /dev/null +++ b/tests/headers/InstalledPartialRegisterHeaderProbe.cpp @@ -0,0 +1,17 @@ +#include + +#include + +/** + * @brief Exercises a PartialRegister boundary using only the isolated public headers. + * @param value PartialRegister value returned unchanged. + * @return The supplied partial value. + */ +SimdLib::PartialRegister SIMD_FLAGS(InOut, RegisterOnly, ForceInline) + installed_partial_register_identity(const SimdLib::PartialRegister value) noexcept +{ + return value; +} + +static_assert(sizeof(SimdLib::PartialRegister) == 16); +static_assert(SimdLib::IRegister::CoreSurface>); diff --git a/tests/headers/InstalledPartialRegisterMaskHeaderProbe.cpp b/tests/headers/InstalledPartialRegisterMaskHeaderProbe.cpp new file mode 100644 index 0000000..c99ecdd --- /dev/null +++ b/tests/headers/InstalledPartialRegisterMaskHeaderProbe.cpp @@ -0,0 +1,7 @@ +#include + +#include + +using InstalledPartialRegisterMaskHeaderProbe = SimdLib::PartialRegisterMask; + +static_assert(InstalledPartialRegisterMaskHeaderProbe::native_lane_count == 4); diff --git a/tests/headers/PartialRegisterFwdHeaderProbe.cpp b/tests/headers/PartialRegisterFwdHeaderProbe.cpp new file mode 100644 index 0000000..e601966 --- /dev/null +++ b/tests/headers/PartialRegisterFwdHeaderProbe.cpp @@ -0,0 +1,10 @@ +#include + +#include + +#ifndef SIMDLIB_PARTIAL_REGISTER_HEADER_PROBE_BITS +#define SIMDLIB_PARTIAL_REGISTER_HEADER_PROBE_BITS 128 +#endif + +constexpr std::size_t partial_register_fwd_probe_lane_count = SIMDLIB_PARTIAL_REGISTER_HEADER_PROBE_BITS == 256 ? 5 : 3; +using PartialRegisterFwdProbe = SimdLib::PartialRegister; diff --git a/tests/headers/PartialRegisterHeaderProbe.cpp b/tests/headers/PartialRegisterHeaderProbe.cpp new file mode 100644 index 0000000..17d1615 --- /dev/null +++ b/tests/headers/PartialRegisterHeaderProbe.cpp @@ -0,0 +1,17 @@ +#include + +#include + +#ifndef SIMDLIB_PARTIAL_REGISTER_HEADER_PROBE_BITS +#define SIMDLIB_PARTIAL_REGISTER_HEADER_PROBE_BITS 128 +#endif + +constexpr std::size_t partial_register_header_probe_lane_count = SIMDLIB_PARTIAL_REGISTER_HEADER_PROBE_BITS == 256 ? 5 : 3; +using PartialRegisterHeaderProbe = + SimdLib::PartialRegister; +using CompleteRegisterHeaderProbe = SimdLib::Register; + +static_assert(PartialRegisterHeaderProbe::lane_count == partial_register_header_probe_lane_count); +static_assert(SimdLib::IRegister::CoreSurface); +static_assert(SimdLib::IRegister::CoreSurface); +static_assert(SimdLib::IPartialRegisterMask::Type); diff --git a/tests/headers/PartialRegisterMaskHeaderProbe.cpp b/tests/headers/PartialRegisterMaskHeaderProbe.cpp new file mode 100644 index 0000000..7597d2c --- /dev/null +++ b/tests/headers/PartialRegisterMaskHeaderProbe.cpp @@ -0,0 +1,14 @@ +#include + +#include + +#ifndef SIMDLIB_PARTIAL_REGISTER_HEADER_PROBE_BITS +#define SIMDLIB_PARTIAL_REGISTER_HEADER_PROBE_BITS 128 +#endif + +constexpr std::size_t partial_register_mask_header_probe_lane_count = SIMDLIB_PARTIAL_REGISTER_HEADER_PROBE_BITS == 256 ? 5 : 3; +using PartialRegisterMaskHeaderProbe = + SimdLib::PartialRegisterMask; + +static_assert(PartialRegisterMaskHeaderProbe::lane_count == partial_register_mask_header_probe_lane_count); +static_assert(SimdLib::IPartialRegisterMask::Type); diff --git a/tests/headers/SimdLibRegisterHeaderProbe.cpp b/tests/headers/SimdLibRegisterHeaderProbe.cpp index d7d3b95..c4ae35c 100644 --- a/tests/headers/SimdLibRegisterHeaderProbe.cpp +++ b/tests/headers/SimdLibRegisterHeaderProbe.cpp @@ -9,8 +9,12 @@ static_assert(SIMDLIB_REQUIRE_REGISTER_INTERFACE == 1); using UmbrellaRegister = SimdLib::Register; using UmbrellaNativeRegister = SimdLib::NativeRegister; using UmbrellaRegisterMask = typename UmbrellaRegister::mask_type; +using UmbrellaPartialRegister = SimdLib::PartialRegister; static_assert(SimdLib::IRegister::Type); static_assert(std::same_as); static_assert(SimdLib::IRegister::Type); static_assert(SimdLib::IRegisterMask::Type); +static_assert(SimdLib::IRegister::CoreSurface); +static_assert(SimdLib::IPartialRegisterMask::Type); +static_assert(UmbrellaPartialRegister::inactive_lane_count == 1); diff --git a/tests/installed_consumer/CMakeLists.txt b/tests/installed_consumer/CMakeLists.txt new file mode 100644 index 0000000..87c45ba --- /dev/null +++ b/tests/installed_consumer/CMakeLists.txt @@ -0,0 +1,61 @@ +cmake_minimum_required(VERSION 3.31) + +project(SimdLibInstalledConsumer LANGUAGES CXX) + +find_package(SimdLib CONFIG REQUIRED) + +if(NOT TARGET SimdLib::SimdLib OR NOT TARGET SimdLib::Register) + message(FATAL_ERROR "The installed package must export both public SimdLib targets") +endif() + +get_target_property(simdlib_register_features SimdLib::Register INTERFACE_COMPILE_FEATURES) +get_target_property(simdlib_register_definitions SimdLib::Register INTERFACE_COMPILE_DEFINITIONS) +get_target_property(simdlib_core_includes SimdLib::SimdLib INTERFACE_INCLUDE_DIRECTORIES) +if(NOT "cxx_std_23" IN_LIST simdlib_register_features) + message(FATAL_ERROR "The installed Register target must retain its C++23 requirement") +endif() +if(NOT "SIMDLIB_REQUIRE_REGISTER_INTERFACE=1" IN_LIST simdlib_register_definitions) + message(FATAL_ERROR "The installed Register target must retain its interface requirement signal") +endif() +foreach(simdlib_core_include IN LISTS simdlib_core_includes) + cmake_path(IS_PREFIX SIMDLIB_FORBIDDEN_INCLUDE_DIR "${simdlib_core_include}" NORMALIZE simdlib_uses_source_tree) + if(simdlib_uses_source_tree) + message(FATAL_ERROR "Installed target exposes source-tree include path: ${simdlib_core_include}") + endif() +endforeach() + +enable_testing() + +# @brief Adds one installed-package consumer for a PartialRegister ISA profile. +# @param target Downstream executable target name. +# @param bits Physical PartialRegister width exercised by the consumer. +function(simdlib_add_installed_partial_register_consumer target bits) + add_executable(${target} main.cpp) + target_link_libraries(${target} PRIVATE SimdLib::Register) + target_compile_definitions(${target} PRIVATE + SIMDLIB_INSTALLED_CONSUMER_BITS=${bits} + SIMDLIB_HAS_SSE=1 SIMDLIB_HAS_SSE2=1 SIMDLIB_HAS_SSE3=1 + SIMDLIB_HAS_SSSE3=1 SIMDLIB_HAS_SSE41=1 SIMDLIB_HAS_SSE42=1 + SIMDLIB_HAS_FMA=0) + if(bits EQUAL 256) + target_compile_definitions(${target} PRIVATE SIMDLIB_HAS_AVX=1 SIMDLIB_HAS_AVX2=1) + if(MSVC) + target_compile_options(${target} PRIVATE /arch:AVX2) + else() + target_compile_options(${target} PRIVATE -mavx2) + endif() + else() + target_compile_definitions(${target} PRIVATE SIMDLIB_HAS_AVX=0 SIMDLIB_HAS_AVX2=0) + if(MSVC) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(${target} PRIVATE /clang:-msse4.2 /clang:-mno-avx /clang:-mno-avx2) + endif() + else() + target_compile_options(${target} PRIVATE -msse4.2 -mno-avx -mno-avx2) + endif() + endif() + add_test(NAME ${target} COMMAND ${target}) +endfunction() + +simdlib_add_installed_partial_register_consumer(SimdLibInstalledConsumerSse42 128) +simdlib_add_installed_partial_register_consumer(SimdLibInstalledConsumerAvx2 256) diff --git a/tests/installed_consumer/main.cpp b/tests/installed_consumer/main.cpp new file mode 100644 index 0000000..2d308e2 --- /dev/null +++ b/tests/installed_consumer/main.cpp @@ -0,0 +1,50 @@ +#include + +#include +#include +#include +#include +#include + +#ifndef SIMDLIB_INSTALLED_CONSUMER_BITS +#define SIMDLIB_INSTALLED_CONSUMER_BITS 128 +#endif + +/** + * @brief Exercises PartialRegister exclusively through an installed SimdLib package. + * @return Zero when logical transfers, operations, predicates, and inactive lanes satisfy their contracts. + */ +int main() +{ +#if SIMDLIB_INSTALLED_CONSUMER_BITS == 256 + using Register = SimdLib::partial_uint32x8<5>; +#else + using Register = SimdLib::partial_uint32x4<3>; +#endif + static_assert(SimdLib::IRegister::CoreSurface); + + std::array source{}; + for (std::size_t lane = 0; lane < source.size(); ++lane) + source[lane] = static_cast(lane + 1); + const Register loaded = Register::load(std::span{source}); + const Register result = (loaded + Register::broadcast(2)).max(Register::broadcast(4)); + const auto selected = result.compare_greater(Register::broadcast(5)).select(result, Register::zero()); + + std::array stored{}; + selected.store(std::span{stored}); + for (std::size_t lane = 0; lane < stored.size(); ++lane) + { + const auto incremented = source[lane] + 2; + const auto clamped = incremented < 4 ? 4 : incremented; + if (stored[lane] != (clamped > 5 ? clamped : 0)) + return 1; + } + + const auto native_lanes = Register::api_type::to_array(selected.to_native()); + for (std::size_t lane = Register::lane_count; lane < Register::native_lane_count; ++lane) + { + if (std::bit_cast>(native_lanes[lane]) != std::array{}) + return 2; + } + return 0; +} diff --git a/tests/partial_register/PartialRegisterRepresentation.tests.cpp b/tests/partial_register/PartialRegisterRepresentation.tests.cpp new file mode 100644 index 0000000..4a90bc0 --- /dev/null +++ b/tests/partial_register/PartialRegisterRepresentation.tests.cpp @@ -0,0 +1,114 @@ +#include +#include + +#include +#include + +namespace +{ + +/** @brief Reports whether a mask type exposes mutable intersection assignment. */ +template +concept HasBitwiseAndAssign = requires(mask_t lhs, mask_t rhs) { lhs &= rhs; }; + +/** @brief Reports whether a mask type exposes mutable union assignment. */ +template +concept HasBitwiseOrAssign = requires(mask_t lhs, mask_t rhs) { lhs |= rhs; }; + +/** @brief Reports whether a mask type exposes mutable exclusive-union assignment. */ +template +concept HasBitwiseXorAssign = requires(mask_t lhs, mask_t rhs) { lhs ^= rhs; }; + +/** @brief Checks object layout and compile-time geometry for one partial SIMD value. */ +template consteval bool has_partial_register_shape() +{ + using value_t = SimdLib::PartialRegister; + using native_t = typename value_t::native_type; + return std::is_final_v && std::is_aggregate_v && !std::is_polymorphic_v && sizeof(value_t) == sizeof(native_t) && + alignof(value_t) == alignof(native_t) && std::is_standard_layout_v && std::is_trivially_copy_constructible_v && + !std::is_trivially_default_constructible_v && std::is_trivially_move_constructible_v && + std::is_trivially_copy_assignable_v && std::is_trivially_move_assignable_v && std::is_trivially_destructible_v && + std::is_trivially_copyable_v && value_t::register_width == bits && value_t::byte_count == bits / 8 && + value_t::native_lane_count == bits / (sizeof(element_t) * 8) && value_t::lane_count == active_lane_count && + value_t::active_byte_count == active_lane_count * sizeof(element_t) && + value_t::inactive_lane_count == value_t::native_lane_count - active_lane_count; +} + +/** @brief Checks object layout, concepts, and metadata for one partial predicate geometry. */ +template consteval bool has_partial_register_mask_shape() +{ + using mask_t = SimdLib::PartialRegisterMask; + using native_t = typename mask_t::native_type; + return std::is_final_v && std::is_aggregate_v && !std::is_polymorphic_v && sizeof(mask_t) == sizeof(native_t) && + alignof(mask_t) == alignof(native_t) && std::is_standard_layout_v && std::is_trivially_copy_constructible_v && + !std::is_trivially_default_constructible_v && std::is_trivially_move_constructible_v && + std::is_trivially_copy_assignable_v && std::is_trivially_move_assignable_v && std::is_trivially_destructible_v && + std::is_trivially_copyable_v && SimdLib::IPartialRegisterMask::Type && SimdLib::IPartialRegisterMask::Reductions && + SimdLib::IPartialRegisterMask::Composition && SimdLib::IPartialRegisterMask::Select && !HasBitwiseAndAssign && + !HasBitwiseOrAssign && !HasBitwiseXorAssign && mask_t::register_width == bits && + mask_t::native_lane_count == bits / (sizeof(element_t) * 8) && mask_t::lane_count == active_lane_count; +} + +/** @brief Checks every supported partial extent for one element and register-width pair. */ +template +consteval bool has_partial_register_shapes(std::index_sequence) +{ + return ( + []() consteval + { + if constexpr (SimdLib::PartialRegisterAvailable) + return has_partial_register_shape(); + else + return true; + }.template operator()() && + ...); +} + +/** @brief Checks every supported partial predicate extent for one element and register-width pair. */ +template +consteval bool has_partial_register_mask_shapes(std::index_sequence) +{ + return ( + []() consteval + { + if constexpr (SimdLib::PartialRegisterAvailable) + return has_partial_register_mask_shape(); + else + return true; + }.template operator()() && + ...); +} + +/** @brief Checks all non-complete logical extents for one element and register-width pair. */ +template consteval bool has_all_partial_register_shapes() +{ + return has_partial_register_shapes(std::make_index_sequence::element_count - 1>{}); +} + +/** @brief Checks all non-complete logical predicate extents for one element and register-width pair. */ +template consteval bool has_all_partial_register_mask_shapes() +{ + return has_partial_register_mask_shapes(std::make_index_sequence::element_count - 1>{}); +} + +#define SIMDLIB_ASSERT_PARTIAL_REGISTER_SHAPES(element_type, width) \ + static_assert(has_all_partial_register_shapes()); \ + static_assert(has_all_partial_register_mask_shapes()) + +SIMDLIB_ASSERT_PARTIAL_REGISTER_SHAPES(std::int8_t, SIMDLIB_REGISTER_TEST_WIDTH); +SIMDLIB_ASSERT_PARTIAL_REGISTER_SHAPES(std::uint8_t, SIMDLIB_REGISTER_TEST_WIDTH); +SIMDLIB_ASSERT_PARTIAL_REGISTER_SHAPES(std::int16_t, SIMDLIB_REGISTER_TEST_WIDTH); +SIMDLIB_ASSERT_PARTIAL_REGISTER_SHAPES(std::uint16_t, SIMDLIB_REGISTER_TEST_WIDTH); +SIMDLIB_ASSERT_PARTIAL_REGISTER_SHAPES(std::int32_t, SIMDLIB_REGISTER_TEST_WIDTH); +SIMDLIB_ASSERT_PARTIAL_REGISTER_SHAPES(std::uint32_t, SIMDLIB_REGISTER_TEST_WIDTH); +SIMDLIB_ASSERT_PARTIAL_REGISTER_SHAPES(std::int64_t, SIMDLIB_REGISTER_TEST_WIDTH); +SIMDLIB_ASSERT_PARTIAL_REGISTER_SHAPES(std::uint64_t, SIMDLIB_REGISTER_TEST_WIDTH); +SIMDLIB_ASSERT_PARTIAL_REGISTER_SHAPES(float, SIMDLIB_REGISTER_TEST_WIDTH); +SIMDLIB_ASSERT_PARTIAL_REGISTER_SHAPES(double, SIMDLIB_REGISTER_TEST_WIDTH); + +#undef SIMDLIB_ASSERT_PARTIAL_REGISTER_SHAPES + +using native_partial_register_type = SimdLib::NativePartialRegister; +static_assert(native_partial_register_type::register_width == 128); + +} // namespace diff --git a/tests/partial_register_odr/fixture.h b/tests/partial_register_odr/fixture.h new file mode 100644 index 0000000..e72d1f3 --- /dev/null +++ b/tests/partial_register_odr/fixture.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include +#include + +#ifndef SIMDLIB_PARTIAL_REGISTER_ODR_BITS +#define SIMDLIB_PARTIAL_REGISTER_ODR_BITS 128 +#endif + +namespace SimdLibPartialRegisterOdr +{ + +constexpr inline std::size_t active_lane_count = SIMDLIB_PARTIAL_REGISTER_ODR_BITS == 256 ? 5 : 3; +using Register = SimdLib::PartialRegister; +using RegisterMask = Register::mask_type; + +/** + * @brief Adds two partial registers in a separate translation unit. + * @param lhs Left operand with a zero inactive suffix. + * @param rhs Right operand with a zero inactive suffix. + * @return Lane-wise active-prefix sum with a zero inactive suffix. + */ +[[nodiscard]] Register SIMD_FLAGS(InOut, RegisterOnly) add(Register lhs, Register rhs) noexcept; + +/** + * @brief Compares two partial registers in a separate translation unit. + * @param lhs Left operand with a zero inactive suffix. + * @param rhs Right operand with a zero inactive suffix. + * @return Active-prefix equality predicate with false inactive lanes. + */ +[[nodiscard]] RegisterMask SIMD_FLAGS(InOut, RegisterOnly) equal(Register lhs, Register rhs) noexcept; + +} // namespace SimdLibPartialRegisterOdr diff --git a/tests/partial_register_odr/main.cpp b/tests/partial_register_odr/main.cpp new file mode 100644 index 0000000..68927ff --- /dev/null +++ b/tests/partial_register_odr/main.cpp @@ -0,0 +1,25 @@ +#include "fixture.h" + +#include +#include + +/** + * @brief Verifies PartialRegister and its predicate across translation-unit boundaries. + * @return Zero when active results match and every inactive lane remains bitwise zero. + */ +int main() +{ + using namespace SimdLibPartialRegisterOdr; + const Register actual = add(Register::broadcast(2), Register::broadcast(3)); + const Register expected = Register::broadcast(5); + if (!equal(actual, expected).all() || actual.to_array() != expected.to_array()) + return 1; + + const auto native_lanes = Register::api_type::to_array(actual.to_native()); + for (std::size_t lane = Register::lane_count; lane < Register::native_lane_count; ++lane) + { + if (std::bit_cast>(native_lanes[lane]) != std::array{}) + return 2; + } + return 0; +} diff --git a/tests/partial_register_odr/second_translation_unit.cpp b/tests/partial_register_odr/second_translation_unit.cpp new file mode 100644 index 0000000..f5d5c98 --- /dev/null +++ b/tests/partial_register_odr/second_translation_unit.cpp @@ -0,0 +1,18 @@ +#include "fixture.h" + +namespace SimdLibPartialRegisterOdr +{ + +/** Defines the cross-translation-unit active-prefix addition boundary. */ +Register SIMD_FLAGS(InOut, RegisterOnly) add(Register lhs, Register rhs) noexcept +{ + return lhs + rhs; +} + +/** Defines the cross-translation-unit active-prefix comparison boundary. */ +RegisterMask SIMD_FLAGS(InOut, RegisterOnly) equal(Register lhs, Register rhs) noexcept +{ + return lhs.compare_equal(rhs); +} + +} // namespace SimdLibPartialRegisterOdr diff --git a/tools/Run-WindowsClang20Container.ps1 b/tools/Run-WindowsClang20Container.ps1 new file mode 100644 index 0000000..6b7acb0 --- /dev/null +++ b/tools/Run-WindowsClang20Container.ps1 @@ -0,0 +1,59 @@ +<# +.SYNOPSIS +Builds or consumes the dedicated Windows clang-cl 20 compatibility-floor container. +.DESCRIPTION +The source tree and pipeline output directory are bind-mounted into a pinned +Windows Server Core image containing Visual Studio Build Tools and the explicit +LLVM 20.1.8 compatibility-floor toolchain. +.PARAMETER Action +Pipeline operation to execute inside the container. +.PARAMETER SkipImageBuild +Reuses the existing local image instead of rebuilding it. +#> +[CmdletBinding()] +param( + [ValidateSet('Build', 'Test', 'BuildBenchmarks')] + [string]$Action = 'Build', + [switch]$SkipImageBuild +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +$repositoryRoot = Split-Path -Parent $PSScriptRoot +$dockerfile = Join-Path $repositoryRoot 'containers/Dockerfile.windows-clang20' +$image = 'simdlib/windows-clang20:local' + +if (-not $IsWindows) { throw 'The Windows clang-cl 20 container requires a Windows host.' } +if (-not (Get-Command docker -CommandType Application -ErrorAction SilentlyContinue)) { + throw 'Docker is required to run the Windows clang-cl 20 container.' +} + +$serverOs = (& docker version --format '{{.Server.Os}}' 2>&1).Trim() +if ($LASTEXITCODE -ne 0) { throw "Unable to query the Docker engine: $serverOs" } +if ($serverOs -ne 'windows') { + throw "The Windows clang-cl 20 container requires a Windows Docker engine; selected engine: $serverOs" +} + +if (-not $SkipImageBuild) { + & docker build --isolation=process --memory 4GB --file $dockerfile --tag $image $repositoryRoot + if ($LASTEXITCODE -ne 0) { throw "Unable to build $image" } +} + +$script = switch ($Action) { + 'Build' { 'tools/Build.ps1' } + 'Test' { 'tools/Run-Tests.ps1' } + 'BuildBenchmarks' { 'tools/Build-Benchmarks.ps1' } +} +$revision = (& git -C $repositoryRoot rev-parse HEAD).Trim() +if ($LASTEXITCODE -ne 0 -or $revision -notmatch '^[0-9a-f]{40}$') { + throw 'Unable to determine the source revision for container provenance.' +} + +$mount = "$($repositoryRoot):C:\workspace" +& docker run --rm --isolation=process --memory 8GB ` + --volume $mount --workdir C:\workspace ` + --env CI=1 --env GITHUB_ACTIONS=$env:GITHUB_ACTIONS ` + --env SIMDLIB_BUILD_REVISION=$revision ` + $image -File $script -Scope Native -Compiler ClangCl +if ($LASTEXITCODE -ne 0) { throw "Windows clang-cl 20 container action failed: $Action" } diff --git a/tools/validation-matrix.json b/tools/validation-matrix.json index 8096e2d..b637806 100644 --- a/tools/validation-matrix.json +++ b/tools/validation-matrix.json @@ -507,7 +507,8 @@ "resolvers": [ "tools/Pipeline.Common.psm1", "tools/Run-NativeMatrix.ps1", - "tools/Run-ContainerMatrix.ps1" + "tools/Run-ContainerMatrix.ps1", + "tools/Run-WindowsClang20Container.ps1" ], "orchestration": [ "tools/Build.ps1", @@ -527,7 +528,7 @@ ], "containerRouting": [ "compose.yml", - "containers/container-entrypoint.sh" + "containers" ], "publicConsumerBoundary": [ "tools/Test-PublicConsumerBoundary.ps1", diff --git a/wiki/Technical-Reference.md b/wiki/Technical-Reference.md index ca21df8..625d7df 100644 --- a/wiki/Technical-Reference.md +++ b/wiki/Technical-Reference.md @@ -20,7 +20,7 @@ example, start with the [project README](../README.md). ## Library model SimdLib is a header-only library with a C++20 core and an opt-in C++23 -complete-register interface. Its CMake targets are `INTERFACE_LIBRARY` +register-value interface. Its CMake targets are `INTERFACE_LIBRARY` targets; they do not produce a DLL or static library. The public API lives in the `SimdLib` namespace, while `SimdLib::Detail` contains implementation details that consumer code must not name. @@ -31,6 +31,11 @@ The main API families are: that selects the widest available register; - `Register` and `RegisterMask`, the explicit-width complete-register value and predicate types; +- `NativePartialRegister`, the target-selected + C++23 partial-register value; +- `PartialRegister` and + `PartialRegisterMask`, the explicit-width active-prefix value and predicate + types whose inactive suffix is always all-bits-zero; - `NativeApi`, the C++20 backend facade that selects the widest available register; - `Api`, a typed intrinsic facade; @@ -55,8 +60,8 @@ add_subdirectory(external/SimdLib) target_link_libraries(MyTarget PRIVATE SimdLib::SimdLib) ``` -Targets that use `Register`, `RegisterMask`, or `NativeRegister` link the -C++23 interface target instead: +Targets that use complete- or partial-register values link the C++23 interface +target instead: ```cmake target_link_libraries(MyRegisterTarget PRIVATE SimdLib::Register) @@ -85,7 +90,7 @@ translation units pay for formatting support only when they use it. The repository's CMake project requires CMake 3.31 or newer. Consumers that integrate the headers without the provided CMake project need a supported C++20 -compiler for the core, a supported C++23 compiler for the Register interface, +compiler for the core, a supported C++23 compiler for the register-value interface, and the appropriate target flags. ## Supported environments @@ -116,6 +121,14 @@ resolves to the widest available `Register` specialization. Use explicit the target configuration. This is a compile-time choice based on compiler flags; it is not runtime CPU detection. +Use `PartialRegister` when one +native register carries a compile-time contiguous low-lane prefix. Every +inactive high lane has the all-bits-zero representation. Use +`NativePartialRegister` only when target-selected width is acceptable. A +`SimdVector` instead models one fixed logical value, while `SimdAlgo` and other +collection-level code own iteration and runtime tail handling; a +`PartialRegister` is not itself a dynamic collection-tail policy. + Use `SimdLib::NativeApi` for C++20, collection helpers, or direct backend access. It resolves to `Api<256, element_t>` when the compile target enables AVX2 and SSE4.2, and otherwise resolves to `Api<128, element_t>` when @@ -143,8 +156,11 @@ FMA-disabled paths, and all four BMI1/BMI2 combinations. | `` | Auto-sized `NativeApi`, explicit-width `Api`, and availability query | | `` | C++23 `Register` and `NativeRegister` complete-register values | | `` | C++23 `RegisterMask` predicate values | +| `` | C++23 explicit-width and target-selected active-prefix register values | +| `` | C++23 active-prefix predicate values whose inactive lanes are false | +| `` | Forward declarations and PartialRegister availability queries | | `` | Deprecated compatibility forwarding header; use `Api.h` | -| `` | C++23 named `Register` aliases exposed when their SSE4.2 or AVX2 width is available | +| `` | C++23 named complete-register aliases and `partial_*` alias templates for available widths | | `` | `SimdVector` value type | | `` | Fixed-extent and dynamic-span `SimdAlgo` operations | | `` | Byte-mask reduction and expansion functions | @@ -155,6 +171,47 @@ FMA-disabled paths, and all four BMI1/BMI2 combinations. Headers and declarations below `SimdLib::Detail` are implementation-only. +### Partial-register workflows + +The following examples use only public APIs. The active count is part of the +type, and `store()` writes exactly that many elements. + +```cpp +#include + +#include +#include + +using Tail = SimdLib::PartialRegister; + +const auto input = Tail::from_array(std::array{2, 4, 6}); +const auto adjusted = (input + Tail::broadcast(1)) * Tail::broadcast(2); +const auto positive = adjusted.compare_greater(Tail::zero()); +const auto selected = positive.select(adjusted, Tail::zero()); +const auto reordered = selected.shuffle<2, 0, 1>(); + +std::array output{}; +reordered.store(output); +``` + +Bit reinterpretation, numeric conversion, and widening are distinct public +operations with independently selected result types: + +```cpp +const auto reinterpreted = input.bit_cast(); +const auto converted = input.convert(); + +using Narrow = SimdLib::PartialRegister; +const auto widened = Narrow::from_lanes(1, 2, 3, 4, 5, 6, 7) + .widen_low(); +``` + +`bit_cast()` preserves the active bit extent, `convert()` preserves the active +lane count when the target cell is available, and `widen_low()` consumes the +documented low source prefix. See +[`PartialRegisterOperationLedger.md`](../docs/PartialRegisterOperationLedger.md) +for the complete result-type rules. + SimdLib 0.2.0 uses `Api` as the primary facade name. The deprecated `SimdApi` spelling remains available through `` until 1.0.0. Wide integer BMI operations are available under `SimdLib::Bmi`; the former root @@ -370,9 +427,11 @@ for example ## Continuous validation `.github/workflows/ci.yml` delegates to the same scoped `Build.ps1` and -`Run-Tests.ps1` commands used locally. Native MSVC, native clang-cl -plus coverage, and Linux container compilers each build their assigned -fingerprints once and then run test-only operations. Each benchmark-owning CI +`Run-Tests.ps1` commands used locally. Native MSVC, the explicit +Chocolatey-provisioned Windows clang-cl 20 compatibility-floor container, +native clang-cl 22 plus coverage, and Linux container compilers +each build their assigned fingerprints once and then run test-only operations. +Each benchmark-owning CI job invokes `Build-Benchmarks.ps1` explicitly after correctness testing; the default build remains benchmark-free. Clang ASan+UBSan remains an independent instrumented fingerprint. Mandatory instruction-family labels, From 45cd6c9a178c2b708b9a44492219a1395a6e3bda Mon Sep 17 00:00:00 2001 From: David Sisco Date: Sat, 12 Sep 2026 19:51:23 -0700 Subject: [PATCH 03/16] dev: set "Build" to not be the default task, so we can actually see the vscode tasks menu --- .vscode/tasks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 159e599..8679e6d 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -23,7 +23,7 @@ }, "group": { "kind": "build", - "isDefault": true + "isDefault": false }, "detail": "Builds the complete native and container validation matrix without benchmark artifacts." }, From 75d4bee9eaee44571f0f003789c326c0de036ad4 Mon Sep 17 00:00:00 2001 From: David Sisco Date: Sat, 12 Sep 2026 19:53:25 -0700 Subject: [PATCH 04/16] docs: improve register type proposal docs: improve register type proposal docs: implementation plan for Register type [Phase 0]: Freeze the Contract and Record the Baseline [Phase 1]: Add Language Availability and Build Integration docs: add new phase for implementing containerized multi-compiler build matrix [Phase 2]: Establish Reproducible Containerized Compiler Environments fix: only force full project rebuilds within CI environments [Phae 3]: initial phase 3 chore: always compare compiler codegen with stack protection flags turned on fix: move constexpr code out of method body to prevent MSVC being stupid and forcing bad codegen fix: MSVC has bad heuristics fix: compilers (MSVC) can de-optimize methods due to characteristics of constexpr code if its within the main body perf: implement method flattening for transform methods chore: remove redundant inline statements dev: add todo tasks for supporting Intel ICX and NVC++ compilers chore: update phase 3 tasks [Phase 3]: Establish the Representation and Performance Harness [Phase 4]: Implement Register Construction, Observation, and Transfer feat: implement missing api methods & drop 32bit support [Phase 5]: Implement RegisterMask, Comparisons, and Selection perf: mark applicable methods for flattening and register-only-access [Phase 6]: Implement Basic Arithmetic, Bitwise Operations, and Shifts perf: improve code gen for MSVC docs: additional project tasks [Phase 7]: Implement Specialized Arithmetic and Reductions refactor: encapsulate all layer concepts into interface-like namespaces [Phase 8]: Implement Rearrangement and Conversion Operations dev: setup clang format chore: fix formatting error dev: add task to format all files chore: run format all [Phase 9]: Complete the Operation and Constraint Matrix [Phase 10]: Qualify Correctness, Constexpr, Preconditions, ABI, and Performance [Phase 11]: Expose, Migrate, Document, and Close Out dev: update project todo docs: refresh Register closeout evidence dev: update project todo dev: update project todo docs: implementation tasklist for build pipeline improvements docs: revise implementation plan chore: commit temp changes before refactoring work [Phase 0]: Freeze the Matrix and Measure the Baseline [Phase 1]: Create Exhaustive CMake Build Profiles [Phase 2]: Separate Build and Test Responsibilities [Phase 3]: Refactor Container Matrix Orchestration [Phase 4]: Add Native Compiler and Top-Level Commands [Phase 5]: Migrate CI Without Losing Coverage [Phase 6]: Prove Completeness and Cache Reuse [Phase 7]: Document and Migrate Interfaces docs: add additional phase for build-time improvement [Phase 7]: Document and Migrate Interfaces [Phase 8]: Remove Retired Windows GNU Support References chore: remove completed task list docs: task list for improving build performance [Phase 0]: Establish the Compilation Baseline [Phase 1]: Separate Benchmark Compilation from the Default Build [Phase 2]: Evaluate the Constexpr Compilation Burden [Phase 3]: Analyze the `uint128_t` Compilation Burden [Phase 4]: Analyze the `Bmi.h` Compilation Burden docs: plan to expand Api::shuffle support to all integer widths [Phase 0]: Freeze the Existing Surface and Baseline [Phase 1]: Establish Independent Behavioral and Constraint Oracles [Phase 2]: Implement the 128-Bit Backends [Phase 3]: Implement the 256-Bit Backends [Phase 4]: Generalize the Public Layers [Phase 5]: Prove Runtime Code Quality and Compilation Cost [Phase 6]: Document and Complete Validation docs: remove completed task list chore: code comments refactor: encapsulate vector aliases within their own file dev: update project task list chore: remove completed task list docs: proposal plan for new unified SIMD-FLAGS macro system refactor: redefine alias types to point to register type instead of old SimdVector feat: implement `shuffle_bytes<>` method for Simd Register class tests: test coverage for Register `shuffle_bytes` method docs: implementation plan for new unified SIMD-FLAGS macro system [Phase 0]: Freeze the Grammar and Contract [Phase 1]: Prove the Macro Grammar Is Implementable revise SIMD_FLAGS macro prototype [Phase 2]: Qualify Compiler Placement and Attribute Composition [Phase 3]: Implement the Public Macro and Compiler Adapters [Phase 4]: Establish Contract and Code-Generation Tests [Phase 5]: Inventory and Classify Existing Declarations fix: minor corrections to api & implementation layers docs: refactor tasklist for SIMD layer methodss which use stack memory during runtime [Task 1]: Restore a Focused Compilable Baseline [Task 2]: Constant-Evaluation Helper Boundary [Task 3]: Unconditional API Delegation [Task 4]: Specialized 128-Bit Runtime Extraction docs: expand planning tasklist [Task 5]: Specialized 256-Bit Runtime Extraction docs: amend planning doc with new performance phase [Task 6]: Implementation Extraction Naming Consolidation [Task 7]: Specialized 128-Bit Runtime Insertion [Task 8/9]: Specialized 256-Bit Runtime Insertion [Task 10]: Specialized 128-Bit Integer Remainder Extensions docs: extend planning tasks with codegen test suite cleanup work [Task 11]: Specialized 256-Bit Integer Remainder Extensions [Task 12]: Complete-Register Runtime Byte Shifts docs: extend planning tasks with codegen test suite cleanup work [Task 13]: Complete-Register Bit Shifts [Task 14]: 128-Bit 64-Bit-Lane `setr` [Task 15]: Immediate-Control Runtime Naming [Task 16]: Method-Flag Inventory Reconciliation [Task 17]: Focused Cross-Compiler Validation [Task 18]: Branchless 256-Bit Runtime Extraction Evaluation [Task 19]: Permanent Generated-Code Fixture Rationalization [Task 20]: Complete Permanent Generated-Code Suite Audit and Final Integration docs: remove completed task list chore: code comments docs: initial build pipeline optimization proposal [Phase 0]: Freeze Validation Ownership and Baseline the Pipeline [Phase 1]: Replace the Monolithic Artifact Sweep with Scoped Aggregates [Phase 2]: Deduplicate Repository and Compiler-Front-End Contracts [Phase 3]: Separate Optimized Codegen Gates from Diagnostic Codegen [Phase 4]: Reduce the Ordinary Debug Compiler Matrix [Phase 5]: Slim Runtime, Sanitizer, and Coverage Target Sets [Phase 6]: Deduplicate Examples, ODR, Smoke, and External Consumers [Phase 7]: Refactor Presets and Unified Pipeline Orchestration [Phase 8]: Add Matrix-Ownership and No-Rebuild Regression Coverage [Phase 9]: Measure, Qualify, and Document [Phase 10]: Remove Temporary Planning and Evidence Documentation [Phase 6]: Migrate Implementation and Api Layers [Phase 7]: Migrate Register-Facing and Remaining Public Code [Phase 8]: Remove the Legacy Declaration Surface and Add Audits [Phase 9]: Document, Qualify, and Close Out chore: removing leftover legacy macro usages chore: remove completed task list dev: update project task list chore: remove obsolete files chore: Remove `MethodFlagsInventory.csv` from the repo and audit tooling chore: Remove `MethodFlagsRegisterOnly.csv` from the repo and audit tooling. docs: build pipeline cleanup plan [Phase 1]: Retire Method-Flags Source Auditing [Phase 2]: Remove Public-Header `static_assert` Auditing [Phase 3]: Restructure Validation-Matrix and Pipeline Validation chore: remove completed task list docs: register byte shift extension plan dev: update project todo [Phase 1]: Fix the Naming and Semantic Contract [Phase 2]: Rename the Existing Complete-Register Shift Surface [Phase 3]: Implement Immediate Complete-Register Byte Shifts [Phase 4]: Expose the Immediate API Through `Api` and `Register` dev: update project todo [Phase 5]: Prove Semantics, Availability, and Generated Code [Phase 6]: Documentation and Final Cleanup chore: remove completed task list docs: implementation plan for compile-time register constants [Phase 1]: Fix the Public and Layer Contracts chore: remove deprecated task list chore: remove python from github ci and use choco instead fix: save & restore Clang/CMake paths before & after importing the MSVC environment so they arent overwritten dev: lower minimum version for CMake to 3.31 & CLang to 20 dev: fix linux container build for github ci docs: initial implementation plan for PartialRegister type docs: amend implementation plan for PartialRegister type [Phase 1]: Freeze the Public Contract and Operation Ledger [Phase 2]: Establish Headers, Availability, and the Object Model [Phase 3]: Implement PartialRegisterMask and Predicate Semantics [Phase 4]: Implement Construction, Transfer, and Observation [Phase 5]: Implement Arithmetic and Specialized Operations [Phase 6]: Implement Bitwise, Shift, and Comparison Operations [Phase 7]: Implement Rearrangement and Conversion Operations [Phase 8]: Complete the Public Surface and Integration [Phase 9]: Qualify ABI, Generated Code, and Performance [Phase 10]: Documentation, Full Validation, and Cleanup chore: remove unneeded documents chore: remove unneeded documents dev: add MSVC LLVM 20 to build matrix dev: extract partial register codegen snapshot tests into json config file docs: initial proposal for aligning Register types codegen tests with new standards --- .vscode/tasks.json | 2 +- cmake/development/RuntimeTests.cmake | 8 +++---- include/SimdLib/Api.h | 36 ---------------------------- 3 files changed, 5 insertions(+), 41 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 8679e6d..8094c74 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -103,7 +103,7 @@ "args": [ "-NoProfile", "-Command", - "& clang-format -i --style=file --fallback-style=none @(& git ls-files -- '*.c' '*.cc' '*.cpp' '*.cxx' '*.h' '*.hh' '*.hpp' '*.hxx' '*.inl' '*.ipp' '*.cu' '*.cuh')" + "$clangFormat = (Get-Command clang-format -ErrorAction SilentlyContinue).Source; if (-not $clangFormat) { $llvmFormat = Join-Path $env:ProgramFiles 'LLVM\\bin\\clang-format.exe'; if (Test-Path $llvmFormat) { $clangFormat = $llvmFormat } }; if (-not $clangFormat) { throw 'clang-format was not found on PATH or in C:\\Program Files\\LLVM\\bin' }; & $clangFormat -i --style=file --fallback-style=none @(git ls-files -- '*.c' '*.cc' '*.cpp' '*.cxx' '*.h' '*.hh' '*.hpp' '*.hxx' '*.inl' '*.ipp' '*.cu' '*.cuh')" ], "options": { "cwd": "${workspaceFolder}" diff --git a/cmake/development/RuntimeTests.cmake b/cmake/development/RuntimeTests.cmake index f03324e..0d25461 100644 --- a/cmake/development/RuntimeTests.cmake +++ b/cmake/development/RuntimeTests.cmake @@ -175,7 +175,7 @@ if(SIMDLIB_BUILD_RUNTIME_TESTS) endif() endif() - if(SIMDLIB_BUILD_API_SSE42_TESTS) + if(SIMDLIB_BUILD_API_SSE42_TESTS) simdlib_add_catch_test(ImplHalfTransfer128Tests tests/ImplementationHalfTransfer.tests.cpp Implementation.HalfTransfer128 "IMPLEMENTATION;PARTIAL_TRANSFER;SSE42") target_compile_definitions(ImplHalfTransfer128Tests PRIVATE @@ -235,7 +235,7 @@ if(SIMDLIB_BUILD_RUNTIME_TESTS) target_compile_definitions(${uint128_target} PRIVATE SIMDLIB_TEST_CONSTEXPR_ASSERTIONS=$) endforeach() - target_compile_definitions(UInt128PortableTests PRIVATE + target_compile_definitions(UInt128PortableTests PRIVATE SIMDLIB_USE_COMPILER_CARRY_INTRINSICS=0 SIMDLIB_EXPECT_CARRY_PATH=0 SIMDLIB_HAS_BMI1=0) target_compile_definitions(UInt128ScalarTests PRIVATE SIMDLIB_EXPECT_CARRY_PATH=0) target_compile_definitions(UInt128OptimizedTests PRIVATE SIMDLIB_HAS_BMI1=1) @@ -258,7 +258,7 @@ if(SIMDLIB_BUILD_RUNTIME_TESTS) target_compile_options(UInt128OptimizedTests PRIVATE /arch:AVX2) target_compile_options(UInt128PortableTests PRIVATE /arch:AVX2) endif() - else() + else() target_compile_options(UInt128OptimizedTests PRIVATE -msse4.2 -mbmi) target_compile_options(UInt128PortableTests PRIVATE -msse4.2 -mno-bmi) endif() @@ -279,7 +279,7 @@ if(SIMDLIB_BUILD_RUNTIME_TESTS) simdlib_register_development_test(UInt128ScalarResultSetEquivalence RUNTIME_VALIDATION) endif() - if(SIMDLIB_BUILD_API_AVX2_TESTS) + if(SIMDLIB_BUILD_API_AVX2_TESTS) simdlib_add_catch_test(ImplHalfTransfer256Tests tests/ImplementationHalfTransfer.tests.cpp Implementation.HalfTransfer256 "IMPLEMENTATION;PARTIAL_TRANSFER;AVX2") target_compile_definitions(ImplHalfTransfer256Tests PRIVATE diff --git a/include/SimdLib/Api.h b/include/SimdLib/Api.h index 316e314..4a90d01 100644 --- a/include/SimdLib/Api.h +++ b/include/SimdLib/Api.h @@ -412,42 +412,6 @@ struct Api : public Detail::SimdMappings } } - /** @brief Finishes integer magnitude by summing the SIMD-produced pairwise squares per 128-bit lane and broadcasting the root. - * @tparam partial_element_t Integer lane type produced by the first pairwise square-and-sum step. - * @param pairSums Register containing `x*x + y*y` style partial sums for each 128-bit lane group. - * @return Register containing the lane-local magnitudes broadcast to every source lane. - */ - template - SIMDLIB_FORCE_INLINE static vector_t VECTORCALL - FinishIntegerMagnitudeFromPairSums(typename Api::vector_t pairSums) noexcept - { - using partial_simd = Api; - using accumulation_t = std::conditional_t, int64_t, uint64_t>; - constexpr std::size_t LaneGroupCount = register_width / 128; - constexpr std::size_t SourceLaneWidth = element_count / LaneGroupCount; - constexpr std::size_t PartialLaneWidth = partial_simd::element_count / LaneGroupCount; - - const auto partialValues = partial_simd::to_array(pairSums); - std::array output{}; - for (std::size_t groupIndex = 0; groupIndex < LaneGroupCount; ++groupIndex) - { - accumulation_t total{}; - const std::size_t partialStart = groupIndex * PartialLaneWidth; - for (std::size_t partialOffset = 0; partialOffset < PartialLaneWidth; ++partialOffset) - { - total += static_cast(partialValues[partialStart + partialOffset]); - } - - const element_t laneMagnitude = static_cast(std::round(std::sqrt(static_cast(total)))); - const std::size_t laneStart = groupIndex * SourceLaneWidth; - for (std::size_t laneOffset = 0; laneOffset < SourceLaneWidth; ++laneOffset) - { - output[laneStart + laneOffset] = laneMagnitude; - } - } - - return construct(output); - } #pragma endregion #pragma region Arithmetic Operations From 13aad43e5d4c0fd608dc70aa963c5ba53f6b8b9c Mon Sep 17 00:00:00 2001 From: David Sisco Date: Sat, 12 Sep 2026 21:57:53 -0700 Subject: [PATCH 05/16] fix: PartialRegisterMask UDT build error --- include/SimdLib/PartialRegister.h | 8 +++- include/SimdLib/PartialRegisterMask.h | 68 +++++++++++++++++++++++---- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/include/SimdLib/PartialRegister.h b/include/SimdLib/PartialRegister.h index 07c0f06..173455f 100644 --- a/include/SimdLib/PartialRegister.h +++ b/include/SimdLib/PartialRegister.h @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -40,6 +41,11 @@ class PartialRegister final using native_type = typename api_type::vector_t; using mask_type = PartialRegisterMask; + // Instantiate the associated mask before MSVC fixes the return ABI of the + // comparison members below. Merely naming the specialization does not make + // it complete soon enough to prevent warning C4686 at a chained call site. + static_assert(sizeof(mask_type) > 0, "The associated PartialRegisterMask specialization must be complete."); + constexpr static inline std::size_t register_width = bits; constexpr static inline std::size_t byte_count = api_type::byte_count; constexpr static inline std::size_t native_lane_count = api_type::element_count; @@ -1332,5 +1338,3 @@ class PartialRegister final }; } // namespace SimdLib - -#include diff --git a/include/SimdLib/PartialRegisterMask.h b/include/SimdLib/PartialRegisterMask.h index 205eb3c..584a9ef 100644 --- a/include/SimdLib/PartialRegisterMask.h +++ b/include/SimdLib/PartialRegisterMask.h @@ -7,13 +7,15 @@ #endif #include -#include +#include #include #include #include +#include #include #include +#include namespace SimdLib { @@ -70,28 +72,72 @@ class PartialRegisterMask final [[nodiscard]] constexpr bool SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) any(this PartialRegisterMask value) noexcept requires IApi::MovemaskSlim { - return value.bits() != 0; + if consteval + { + return value.bits() != 0; + } + else + { + using byte_api_type = Api; + const auto bits = api_type::template bit_cast(value.native); + // The inactive suffix is zero, so any set bit in the physical register proves that an active predicate lane is true. + return byte_api_type::testz(bits, bits) == 0; + } } /** @brief Tests whether every active predicate lane is true, ignoring false inactive lanes. */ [[nodiscard]] constexpr bool SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) all(this PartialRegisterMask value) noexcept requires IApi::MovemaskSlim { - return value.bits() == active_bits; + if consteval + { + return value.bits() == active_bits; + } + else + { +#if SIMDLIB_TARGET_X86 + if constexpr (sizeof(element_type) == 2) + { + const auto active_filter = api_type::construct(active_lane_filter); + // Containment by the active-lane filter proves every logical predicate true while ignoring the zero inactive suffix. + if constexpr (register_width == 128) + return _mm_testc_si128(value.native, active_filter) != 0; + else + return _mm256_testc_si256(value.native, active_filter) != 0; + } + else + { + return value.bits() == active_bits; + } +#else + return value.bits() == active_bits; +#endif + } } /** @brief Tests whether every active predicate lane is false. */ [[nodiscard]] constexpr bool SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) none(this PartialRegisterMask value) noexcept requires IApi::MovemaskSlim { - return value.bits() == 0; + if consteval + { + return value.bits() == 0; + } + else + { + using byte_api_type = Api; + const auto bits = api_type::template bit_cast(value.native); + // Testing the complete physical register is valid because every inactive predicate lane is always zero. + return byte_api_type::testz(bits, bits) != 0; + } } /** @brief Returns one compact Boolean bit for each active predicate lane. */ [[nodiscard]] constexpr bits_type SIMD_FLAGS(In, RegisterOnly, ForceInline, Flatten) bits(this PartialRegisterMask value) noexcept requires IApi::MovemaskSlim { - return static_cast(api_type::movemask_slim(value.to_native())) & active_bits; + // The inactive predicate suffix is zero by invariant, so the compact native mask already contains only logical-lane bits. + return static_cast(api_type::movemask_slim(value.to_native())); } /** @@ -100,12 +146,14 @@ class PartialRegisterMask final * @param when_true Value supplying true-selected active lanes. * @param when_false Value supplying false-selected active lanes. * @return A PartialRegister with selected active lanes and a bitwise-zero inactive suffix. + * @tparam selected_register_t Delayed associated value type used to avoid requiring PartialRegister while this mask specialization is incomplete. */ - [[nodiscard]] constexpr register_type SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) - select(this PartialRegisterMask condition, register_type when_true, register_type when_false) noexcept - requires IApi::Select + template + [[nodiscard]] constexpr selected_register_t SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) + select(this PartialRegisterMask condition, selected_register_t when_true, selected_register_t when_false) noexcept + requires std::same_as && IApi::Select { - return register_type{api_type::select(condition.to_native(), when_true.to_native(), when_false.to_native())}; + return selected_register_t{api_type::select(condition.to_native(), when_true.to_native(), when_false.to_native())}; } /** @brief Computes the intersection of two partial predicates. */ @@ -218,3 +266,5 @@ class PartialRegisterMask final }; } // namespace SimdLib + +#include From 55b49ae2880f51fa0d3c86ff362ea71c739fb4e8 Mon Sep 17 00:00:00 2001 From: David Sisco Date: Sat, 12 Sep 2026 23:04:41 -0700 Subject: [PATCH 06/16] perf: improve PartialRegisterMask codegen --- .../PartialRegisterCodegenProfiles.json | 12 ++++---- include/SimdLib/PartialRegisterMask.h | 15 +++++----- tests/RegisterPreconditionFailure.tests.cpp | 30 +++++++++++++++++++ .../codegen/PartialRegisterMaskCodegenRaw.cpp | 23 +++++++------- 4 files changed, 57 insertions(+), 23 deletions(-) diff --git a/cmake/development/PartialRegisterCodegenProfiles.json b/cmake/development/PartialRegisterCodegenProfiles.json index f423cad..4234563 100644 --- a/cmake/development/PartialRegisterCodegenProfiles.json +++ b/cmake/development/PartialRegisterCodegenProfiles.json @@ -68,8 +68,8 @@ "registerWidth": 128, "isaProfile": "SSE42", "reason": "msvc-gs-predicate-composition-cookie", - "wrapperSha256": "40d3d2c14684c7cf4f7ec54e2745a71cc9c96ca77045f8e20e1d2cf2125f5d0f", - "rawSha256": "274480001869ad12ae5da9e9bfc801eb9823aa555ef42c2dd70ca4d37fb61c2a" + "wrapperSha256": "4b0a2ae71fda1c5313ef89b6d675720c07faed6f6bb4a13616a32a0e761ea908", + "rawSha256": "f68da8e931089451ff02324ba929d6f225723ad65d528b3c9a58d99d52bf2d46" }, { "compiler": "msvc", @@ -78,8 +78,8 @@ "registerWidth": 256, "isaProfile": "AVX2", "reason": "msvc-gs-predicate-composition-cookie", - "wrapperSha256": "d080a20d867fa7df30bb9ea1701ec5199900025e9629b4fbc25ee317d9e7a890", - "rawSha256": "c93ce1d1b5f1089deb799e23ed0e17deb3049498e50c10c37d65905296dfa555" + "wrapperSha256": "680664badb890acbe406dfe8d2d9325347c95a189cfcf94e79485094378d2aee", + "rawSha256": "71f62ae0b682328689652fa728398af5e7b87b8e0f48608254a564716025712b" }, { "compiler": "msvc", @@ -89,7 +89,7 @@ "isaProfile": "SSE42", "reason": "msvc-equivalent-import-normalization-gs-and-value-operation-allocation", "wrapperSha256": "e500054cea61f6aabcebf97fadf9264b6de112e5d00519119b9ed093d70239dd", - "rawSha256": "9c085b63a9217f371422e89c09f4984b04cd0b37c3e780d3cb9bc4356f9857d1" + "rawSha256": "7e4902f5e0d7265c27a3460772a31ddd2e34a31a2a2ae1e3b0258c9a67e78c4d" }, { "compiler": "msvc", @@ -99,7 +99,7 @@ "isaProfile": "AVX2", "reason": "msvc-equivalent-import-normalization-gs-and-value-operation-allocation", "wrapperSha256": "c6addf1f03722e3402215409c4f7c076ee16deecabcead203205e8e89d581ad8", - "rawSha256": "94367252b5828ebb560c32fdbcc95014c0991337a8adac50a808404378fa0d5c" + "rawSha256": "0269532845a4a093dc88dff99c2be6660c075d41e7aee860b454926d9a4d36d4" }, { "compiler": "msvc", diff --git a/include/SimdLib/PartialRegisterMask.h b/include/SimdLib/PartialRegisterMask.h index 584a9ef..adf3bdd 100644 --- a/include/SimdLib/PartialRegisterMask.h +++ b/include/SimdLib/PartialRegisterMask.h @@ -79,7 +79,7 @@ class PartialRegisterMask final else { using byte_api_type = Api; - const auto bits = api_type::template bit_cast(value.native); + const auto bits = api_type::template bit_cast(value.to_native()); // The inactive suffix is zero, so any set bit in the physical register proves that an active predicate lane is true. return byte_api_type::testz(bits, bits) == 0; } @@ -101,9 +101,9 @@ class PartialRegisterMask final const auto active_filter = api_type::construct(active_lane_filter); // Containment by the active-lane filter proves every logical predicate true while ignoring the zero inactive suffix. if constexpr (register_width == 128) - return _mm_testc_si128(value.native, active_filter) != 0; + return _mm_testc_si128(value.to_native(), active_filter) != 0; else - return _mm256_testc_si256(value.native, active_filter) != 0; + return _mm256_testc_si256(value.to_native(), active_filter) != 0; } else { @@ -126,7 +126,7 @@ class PartialRegisterMask final else { using byte_api_type = Api; - const auto bits = api_type::template bit_cast(value.native); + const auto bits = api_type::template bit_cast(value.to_native()); // Testing the complete physical register is valid because every inactive predicate lane is always zero. return byte_api_type::testz(bits, bits) != 0; } @@ -180,11 +180,12 @@ class PartialRegisterMask final return PartialRegisterMask{api_type::bitwise_xor(lhs.to_native(), rhs.to_native())}; } - /** @brief Inverts active predicate lanes and explicitly clears the inactive predicate suffix. */ + /** @brief Inverts active predicate lanes while preserving the false inactive predicate suffix. */ [[nodiscard]] constexpr PartialRegisterMask SIMD_FLAGS(InOut, RegisterOnly, ForceInline, Flatten) operator~(this PartialRegisterMask value) noexcept - requires IApi::BitwiseNot && IApi::BitwiseAnd + requires IApi::BitwiseXor { - return PartialRegisterMask{normalize_native(api_type::bitwise_not(value.to_native()))}; + // XOR with the active-lane filter complements the logical predicates without turning the zero inactive suffix into true lanes. + return PartialRegisterMask{api_type::bitwise_xor(value.to_native(), api_type::construct(active_lane_filter))}; } private: diff --git a/tests/RegisterPreconditionFailure.tests.cpp b/tests/RegisterPreconditionFailure.tests.cpp index 98d1038..69c4f3c 100644 --- a/tests/RegisterPreconditionFailure.tests.cpp +++ b/tests/RegisterPreconditionFailure.tests.cpp @@ -132,6 +132,36 @@ TEST_CASE("PartialRegisterMask rejects a true inactive lane from direct aggregat FAIL("PartialRegisterMask accepted a true inactive lane from direct aggregate initialization"); } +TEST_CASE("PartialRegisterMask any rejects a true inactive lane", "[simdlib][partial_register][preconditions]") +{ + using mask_type = SimdLib::PartialRegisterMask; + using api_type = typename mask_type::api_type; + std::array lanes{}; + lanes[3] = 0xffffffffU; + (void)mask_type{api_type::construct(lanes)}.any(); + FAIL("PartialRegisterMask any accepted a true inactive lane"); +} + +TEST_CASE("PartialRegisterMask all rejects a true inactive lane", "[simdlib][partial_register][preconditions]") +{ + using mask_type = SimdLib::PartialRegisterMask; + using api_type = typename mask_type::api_type; + std::array lanes{}; + lanes[7] = 0xffffU; + (void)mask_type{api_type::construct(lanes)}.all(); + FAIL("PartialRegisterMask all accepted a true inactive lane"); +} + +TEST_CASE("PartialRegisterMask none rejects a true inactive lane", "[simdlib][partial_register][preconditions]") +{ + using mask_type = SimdLib::PartialRegisterMask; + using api_type = typename mask_type::api_type; + std::array lanes{}; + lanes[3] = 0xffffffffU; + (void)mask_type{api_type::construct(lanes)}.none(); + FAIL("PartialRegisterMask none accepted a true inactive lane"); +} + TEST_CASE("PartialRegister aligned load rejects a misaligned active source", "[simdlib][partial_register][preconditions]") { using value_type = SimdLib::PartialRegister; diff --git a/tests/codegen/PartialRegisterMaskCodegenRaw.cpp b/tests/codegen/PartialRegisterMaskCodegenRaw.cpp index 0452468..af9f246 100644 --- a/tests/codegen/PartialRegisterMaskCodegenRaw.cpp +++ b/tests/codegen/PartialRegisterMaskCodegenRaw.cpp @@ -13,19 +13,22 @@ constexpr std::size_t partial_codegen_active_lane_count = SIMDLIB_PARTIAL_MASK_C namespace { +using partial_mask_api_t = SimdLib::Api; + +/** @brief Native all-one active prefix used by raw partial-predicate operations. */ +constexpr inline auto partial_mask_active_lane_filter = []() constexpr +{ + std::array lanes{}; + for (std::size_t lane = 0; lane < partial_codegen_active_lane_count; ++lane) + lanes[lane] = 0xffffffffU; + return lanes; +}(); + /** @brief Clears the inactive suffix required by the partial-predicate invariant. */ [[nodiscard]] constexpr typename SimdLib::Api::vector_t normalize_partial_mask_native( typename SimdLib::Api::vector_t native) noexcept { - using api_t = SimdLib::Api; - constexpr auto active_lane_filter = []() constexpr - { - std::array lanes{}; - for (std::size_t lane = 0; lane < partial_codegen_active_lane_count; ++lane) - lanes[lane] = 0xffffffffU; - return lanes; - }(); - return api_t::bitwise_and(native, api_t::construct(active_lane_filter)); + return partial_mask_api_t::bitwise_and(native, partial_mask_api_t::construct(partial_mask_active_lane_filter)); } } // namespace @@ -38,7 +41,7 @@ extern "C" [[nodiscard]] typename SimdLib::Api; const auto normalized_rhs = normalize_partial_mask_native(rhs); const auto normalized_lhs = normalize_partial_mask_native(lhs); - return normalize_partial_mask_native(api_t::bitwise_not(api_t::bitwise_xor(normalized_rhs, normalized_lhs))); + return api_t::bitwise_xor(api_t::bitwise_xor(normalized_rhs, normalized_lhs), api_t::construct(partial_mask_active_lane_filter)); } /** @brief Raw Api mirror for the PartialRegister inactive-lane projection boundary. */ From ccf9c25a6ece9bbd1d90343511c773c8fc6f9f5d Mon Sep 17 00:00:00 2001 From: David Sisco Date: Sat, 12 Sep 2026 23:10:52 -0700 Subject: [PATCH 07/16] dev: fix github CI pipeline to prevent double builds on pushes to PR branches --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2fb683f..26bb34f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,8 @@ name: SimdLib CI on: push: + branches: + - master pull_request: workflow_dispatch: From 503f44ba5341385e0d10d3e8eee28f601a5f96c0 Mon Sep 17 00:00:00 2001 From: David Sisco Date: Sun, 13 Sep 2026 21:55:39 -0700 Subject: [PATCH 08/16] dev: revise Run-COntainerMatrix.ps1 to print compiler warning/error outputs. --- tools/Run-ContainerMatrix.ps1 | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/tools/Run-ContainerMatrix.ps1 b/tools/Run-ContainerMatrix.ps1 index 36ba181..c0d5195 100644 --- a/tools/Run-ContainerMatrix.ps1 +++ b/tools/Run-ContainerMatrix.ps1 @@ -341,7 +341,7 @@ function Start-CellOperation { <# .SYNOPSIS -Completes one child process, writes its logs, and returns its exit code. +Completes one child process, writes its logs, reports failure output, and returns its exit code. .PARAMETER Run Running cell operation to complete. #> @@ -349,9 +349,27 @@ function Complete-CellOperation { param([Parameter(Mandatory)]$Run) if (-not $Run.Process.HasExited) { $Run.Process.WaitForExit() } if (-not $Run.Captured) { - [System.IO.File]::WriteAllText($Run.StandardOutputPath, $Run.StandardOutput.GetAwaiter().GetResult(), $utf8NoBom) - [System.IO.File]::WriteAllText($Run.StandardErrorPath, $Run.StandardError.GetAwaiter().GetResult(), $utf8NoBom) + $stdout = $Run.StandardOutput.GetAwaiter().GetResult() + $stderr = $Run.StandardError.GetAwaiter().GetResult() + [System.IO.File]::WriteAllText($Run.StandardOutputPath, $stdout, $utf8NoBom) + [System.IO.File]::WriteAllText($Run.StandardErrorPath, $stderr, $utf8NoBom) $Run.Captured = $true + + # Replay both streams on failure: build tools can emit diagnostics on either. + # Keep reporting inside the capture guard so final cleanup cannot repeat it. + if ($Run.Process.ExitCode -ne 0) { + $failureLabel = "$($Run.Cell.Id) operation=$($Run.Operation) exit=$($Run.Process.ExitCode)" + [Console]::Error.WriteLine("--- $failureLabel ---") + if (-not [string]::IsNullOrWhiteSpace($stdout)) { + [Console]::Error.WriteLine("stdout ($($Run.StandardOutputPath)):") + [Console]::Error.WriteLine($stdout.TrimEnd()) + } + if (-not [string]::IsNullOrWhiteSpace($stderr)) { + [Console]::Error.WriteLine("stderr ($($Run.StandardErrorPath)):") + [Console]::Error.WriteLine($stderr.TrimEnd()) + } + [Console]::Error.WriteLine("--- End $failureLabel ---") + } } return $Run.Process.ExitCode } From 7f63348144a885346faa57827d0083c3edaf4ff1 Mon Sep 17 00:00:00 2001 From: David Sisco Date: Sun, 13 Sep 2026 22:20:48 -0700 Subject: [PATCH 09/16] dev: improve github CI setup efficiency --- .github/workflows/ci.yml | 38 +++++++++++++++++++++++++++++++------ docs/ContainerValidation.md | 10 ++++++++++ tools/Build.ps1 | 14 +++++++++++--- 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 26bb34f..7823b12 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -129,8 +129,21 @@ jobs: if-no-files-found: error container-compilers: - name: GCC 13, GCC 14, and Clang 22 containers + name: ${{ matrix.name }} container validation runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + include: + - compiler: Gcc13 + service: gcc13 + name: GCC 13 + - compiler: Gcc14 + service: gcc14 + name: GCC 14 + - compiler: Clang22 + service: clang22 + name: Clang 22 steps: - uses: actions/checkout@v4 - name: Install required Docker Compose @@ -152,20 +165,33 @@ jobs: test "${actual_sha256}" = "${expected_sha256}" chmod +x "${plugin_dir}/docker-compose" docker compose version - - name: Build every Linux validation cell + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build cached compiler image + uses: docker/build-push-action@v6 + with: + context: . + file: containers/Dockerfile.${{ matrix.service }} + tags: simdlib/${{ matrix.service }}:local + load: true + provenance: false + # Include intermediate stages so the compiled CMake toolchain is reusable. + cache-from: type=gha,version=2,scope=simdlib-${{ matrix.service }} + cache-to: type=gha,version=2,scope=simdlib-${{ matrix.service }},mode=max,ignore-error=true + - name: Build selected Linux validation cells shell: pwsh - run: tools/Build.ps1 -Scope Containers + run: tools/Build.ps1 -Scope Containers -Compiler ${{ matrix.compiler }} -SkipImageBuild - name: Test the exact Linux build receipt shell: pwsh - run: tools/Run-Tests.ps1 -Scope Containers + run: tools/Run-Tests.ps1 -Scope Containers -Compiler ${{ matrix.compiler }} - name: Build Linux benchmark artifacts explicitly shell: pwsh - run: tools/Build-Benchmarks.ps1 -Scope Containers + run: tools/Build-Benchmarks.ps1 -Scope Containers -Compiler ${{ matrix.compiler }} - name: Upload container evidence if: always() uses: actions/upload-artifact@v4 with: - name: linux-container-evidence + name: linux-${{ matrix.service }}-evidence path: | out/pipeline/linux-*/**/provenance out/pipeline/linux-*/**/reports diff --git a/docs/ContainerValidation.md b/docs/ContainerValidation.md index 1b0c956..45640fe 100644 --- a/docs/ContainerValidation.md +++ b/docs/ContainerValidation.md @@ -39,6 +39,16 @@ package pins remain unchanged. A base-image digest does not freeze package repositories or make subsequent package installation fully reproducible. The entrypoint rejects an unexpected compiler or CMake version before configuring the project. +GitHub CI runs each compiler service in a separate matrix job, with both Clang +configurations sharing the Clang job. Docker Buildx imports and exports a +compiler-specific GitHub Actions layer cache with `mode=max`, including the +intermediate CMake build stage. Cache misses rebuild the pinned image normally; +cache export failures do not invalidate an otherwise successful image build. +The image is loaded into the runner's Docker engine, then +`tools/Build.ps1 -Scope Containers -Compiler -SkipImageBuild` +validates it without another image build. Test and benchmark-build operations +already reuse that image. Each job uploads its own `linux--evidence` +artifact and continues independently when another compiler fails. Building these images requires Docker Compose 2.39.0 or newer so the runner can disable BuildKit provenance without changing the image-identity contract. diff --git a/tools/Build.ps1 b/tools/Build.ps1 index cb5b11e..ecb7b16 100644 --- a/tools/Build.ps1 +++ b/tools/Build.ps1 @@ -6,13 +6,16 @@ Scope must be explicit so a host cannot silently omit required native or container cells. The command builds validation artifacts and records an exact manifest receipt consumed by Run-Tests.ps1. Benchmark compilation is owned exclusively by Build-Benchmarks.ps1. +.PARAMETER SkipImageBuild +Reuses existing Linux compiler images instead of building them before validation. #> [CmdletBinding()] param( [ValidateSet('', 'All', 'Native', 'Containers')] [string]$Scope = '', [ValidateSet('All', 'Msvc', 'ClangCl', 'ClangCoverage', 'Gcc13', 'Gcc14', 'Clang22')] - [string[]]$Compiler = @('All') + [string[]]$Compiler = @('All'), + [switch]$SkipImageBuild ) Set-StrictMode -Version Latest @@ -148,6 +151,9 @@ function Write-BuildReceipt { } $selectedCompilers = @(Resolve-BuildSelection) +if ($SkipImageBuild -and -not @($selectedCompilers | Where-Object { $_ -in (Get-PipelineValidationCompilers -Platform container) }).Count) { + throw '-SkipImageBuild requires at least one Linux container compiler.' +} if ($Scope -in @('All', 'Native') -and -not $IsWindows) { throw 'Native scope requires a Windows x64 host with Visual Studio C++ tools and LLVM 20 or newer.' } $toolingDigest = Get-PipelineToolingDigest -RepositoryRoot $repositoryRoot $pipelineValidationPath = Join-Path $pipelineRoot ( @@ -164,11 +170,13 @@ foreach ($name in @($selectedCompilers | Where-Object { $_ -in (Get-PipelineVali }) } $containerCompilers = @($selectedCompilers | Where-Object { $_ -in (Get-PipelineValidationCompilers -Platform container) }) +$containerImageArguments = @() +if ($SkipImageBuild) { $containerImageArguments += '-SkipImageBuild' } if ($containerCompilers.Count -eq 3) { - $operations.Add([pscustomobject]@{ Id = 'containers'; Script = Join-Path $PSScriptRoot 'Run-ContainerMatrix.ps1'; Arguments = @('-Action', 'Build', '-Compiler', 'All', '-Cell', 'All') }) + $operations.Add([pscustomobject]@{ Id = 'containers'; Script = Join-Path $PSScriptRoot 'Run-ContainerMatrix.ps1'; Arguments = @('-Action', 'Build', '-Compiler', 'All', '-Cell', 'All') + @($containerImageArguments) }) } else { foreach ($name in $containerCompilers) { - $operations.Add([pscustomobject]@{ Id = "container-$($name.ToLowerInvariant())"; Script = Join-Path $PSScriptRoot 'Run-ContainerMatrix.ps1'; Arguments = @('-Action', 'Build', '-Compiler', $name, '-Cell', 'All') }) + $operations.Add([pscustomobject]@{ Id = "container-$($name.ToLowerInvariant())"; Script = Join-Path $PSScriptRoot 'Run-ContainerMatrix.ps1'; Arguments = @('-Action', 'Build', '-Compiler', $name, '-Cell', 'All') + @($containerImageArguments) }) } } $logDirectory = Join-Path $pipelineRoot "logs/$(Get-Date -Format 'yyyyMMdd-HHmmssfff')-build-$PID" From 7f1f39ebd521d9333724a2244989f62307aaaefd Mon Sep 17 00:00:00 2001 From: David Sisco Date: Sun, 13 Sep 2026 22:23:14 -0700 Subject: [PATCH 10/16] dev: remove benchmark builds from CI pipeline as they are unnecessary --- .github/workflows/ci.yml | 13 ------------- docs/BuildPipeline.md | 3 +++ docs/ContainerValidation.md | 5 +++-- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7823b12..25ff5c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,8 +20,6 @@ jobs: run: tools/Build.ps1 -Scope Native -Compiler Msvc - name: Test the exact MSVC build receipt run: tools/Run-Tests.ps1 -Scope Native -Compiler Msvc - - name: Build MSVC benchmark artifacts explicitly - run: tools/Build-Benchmarks.ps1 -Scope Native -Compiler Msvc - name: Upload MSVC evidence if: always() uses: actions/upload-artifact@v4 @@ -31,7 +29,6 @@ jobs: out/pipeline/windows-msvc/**/provenance out/pipeline/windows-msvc/**/reports out/pipeline/windows-msvc/**/validation-build.manifest - out/pipeline/windows-msvc/**/benchmark-build.manifest out/pipeline/windows-msvc/**/build/register-codegen/**/*.json out/pipeline/windows-msvc/**/build/register-codegen/**/*.txt out/pipeline/windows-msvc/**/build/method-flags-codegen/**/*.json @@ -49,8 +46,6 @@ jobs: run: tools/Run-WindowsClang20Container.ps1 -Action Build - name: Test the exact clang-cl 20 build receipt run: tools/Run-WindowsClang20Container.ps1 -Action Test -SkipImageBuild - - name: Build clang-cl 20 benchmark artifacts explicitly - run: tools/Run-WindowsClang20Container.ps1 -Action BuildBenchmarks -SkipImageBuild - name: Upload clang-cl 20 evidence if: always() uses: actions/upload-artifact@v4 @@ -60,7 +55,6 @@ jobs: out/pipeline/windows-clangcl/**/provenance out/pipeline/windows-clangcl/**/reports out/pipeline/windows-clangcl/**/validation-build.manifest - out/pipeline/windows-clangcl/**/benchmark-build.manifest out/pipeline/windows-clangcl/**/build/register-codegen/**/*.json out/pipeline/windows-clangcl/**/build/register-codegen/**/*.txt out/pipeline/windows-clangcl/**/build/partial-register-codegen/**/*.json @@ -101,8 +95,6 @@ jobs: run: tools/Build.ps1 -Scope Native -Compiler ClangCl,ClangCoverage - name: Test the exact Clang 22 build receipt run: tools/Run-Tests.ps1 -Scope Native -Compiler ClangCl,ClangCoverage - - name: Build clang-cl 22 benchmark artifacts explicitly - run: tools/Build-Benchmarks.ps1 -Scope Native -Compiler ClangCl - name: Upload Clang 22 evidence if: always() uses: actions/upload-artifact@v4 @@ -112,7 +104,6 @@ jobs: out/pipeline/windows-clangcl/**/provenance out/pipeline/windows-clangcl/**/reports out/pipeline/windows-clangcl/**/validation-build.manifest - out/pipeline/windows-clangcl/**/benchmark-build.manifest out/pipeline/windows-clangcl/**/build/register-codegen/**/*.json out/pipeline/windows-clangcl/**/build/register-codegen/**/*.txt out/pipeline/windows-clangcl/**/build/partial-register-codegen/**/*.json @@ -184,9 +175,6 @@ jobs: - name: Test the exact Linux build receipt shell: pwsh run: tools/Run-Tests.ps1 -Scope Containers -Compiler ${{ matrix.compiler }} - - name: Build Linux benchmark artifacts explicitly - shell: pwsh - run: tools/Build-Benchmarks.ps1 -Scope Containers -Compiler ${{ matrix.compiler }} - name: Upload container evidence if: always() uses: actions/upload-artifact@v4 @@ -196,7 +184,6 @@ jobs: out/pipeline/linux-*/**/provenance out/pipeline/linux-*/**/reports out/pipeline/linux-*/**/validation-build.manifest - out/pipeline/linux-*/**/benchmark-build.manifest out/pipeline/linux-*/**/build/register-codegen/**/*.json out/pipeline/linux-*/**/build/register-codegen/**/*.txt out/pipeline/linux-*/**/build/method-flags-codegen/**/*.json diff --git a/docs/BuildPipeline.md b/docs/BuildPipeline.md index c8f2f19..012a477 100644 --- a/docs/BuildPipeline.md +++ b/docs/BuildPipeline.md @@ -52,6 +52,9 @@ plus `coverage-provenance.tsv`. The provenance file records the executable identity and profile count used for every independently merged coverage target. Benchmark compilation and execution remain separate: +GitHub CI does not compile or run benchmarks. These commands are available for +explicit local benchmark work: + ```powershell tools/Build-Benchmarks.ps1 -Scope All tools/Run-Benchmarks.ps1 -Scope All diff --git a/docs/ContainerValidation.md b/docs/ContainerValidation.md index 45640fe..2034b7a 100644 --- a/docs/ContainerValidation.md +++ b/docs/ContainerValidation.md @@ -46,8 +46,9 @@ intermediate CMake build stage. Cache misses rebuild the pinned image normally; cache export failures do not invalidate an otherwise successful image build. The image is loaded into the runner's Docker engine, then `tools/Build.ps1 -Scope Containers -Compiler -SkipImageBuild` -validates it without another image build. Test and benchmark-build operations -already reuse that image. Each job uploads its own `linux--evidence` +validates it without another image build. Test operations reuse that image. +CI does not build or run benchmarks; the local benchmark commands remain available. +Each job uploads its own `linux--evidence` artifact and continues independently when another compiler fails. Building these images requires Docker Compose 2.39.0 or newer so the runner can disable BuildKit provenance without changing the image-identity contract. From 100e5d20565d055753fe0798b24a7d8e134ebb3e Mon Sep 17 00:00:00 2001 From: David Sisco Date: Sun, 13 Sep 2026 22:37:14 -0700 Subject: [PATCH 11/16] dev: fix bugs in ci pipeline script error handling --- tools/Pipeline.Common.psm1 | 12 ++++++++---- tools/Run-ContainerMatrix.ps1 | 24 ++++++++++++++++++++---- tools/Run-NativeMatrix.ps1 | 17 +++++++++++++++-- tools/Run-WindowsClang20Container.ps1 | 17 +++++++++++++---- 4 files changed, 56 insertions(+), 14 deletions(-) diff --git a/tools/Pipeline.Common.psm1 b/tools/Pipeline.Common.psm1 index 965c531..99c7953 100644 --- a/tools/Pipeline.Common.psm1 +++ b/tools/Pipeline.Common.psm1 @@ -495,8 +495,10 @@ Imports the installed Visual Studio x64 developer environment. function Initialize-PipelineVisualStudioEnvironment { $vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe' if (-not (Test-Path -LiteralPath $vswhere)) { throw "Visual Studio locator is missing: $vswhere" } - $installation = (& $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath).Trim() - if ($LASTEXITCODE -ne 0 -or -not $installation) { throw 'A Visual Studio installation with the x64 C++ tools is required.' } + $installationOutput = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath + $installationExitCode = $LASTEXITCODE + $installation = ([string]$installationOutput).Trim() + if ($installationExitCode -ne 0 -or -not $installation) { throw 'A Visual Studio installation with the x64 C++ tools is required.' } $developerCommand = Join-Path $installation 'Common7\Tools\VsDevCmd.bat' $environmentLines = & cmd.exe /s /c "`"$developerCommand`" -no_logo -arch=x64 -host_arch=x64 && set" if ($LASTEXITCODE -ne 0) { throw 'Unable to initialize the Visual Studio x64 developer environment.' } @@ -515,8 +517,10 @@ Absolute source-tree path. #> function Get-PipelineRevision { param([Parameter(Mandatory)][string]$RepositoryRoot) - $revision = (& git -C $RepositoryRoot rev-parse HEAD 2>$null).Trim() - if ($LASTEXITCODE -ne 0 -or -not $revision) { return 'unknown' } + $revisionOutput = & git -C $RepositoryRoot rev-parse HEAD 2>$null + $revisionExitCode = $LASTEXITCODE + $revision = ([string]$revisionOutput).Trim() + if ($revisionExitCode -ne 0 -or -not $revision) { return 'unknown' } return $revision } diff --git a/tools/Run-ContainerMatrix.ps1 b/tools/Run-ContainerMatrix.ps1 index c0d5195..5d055f8 100644 --- a/tools/Run-ContainerMatrix.ps1 +++ b/tools/Run-ContainerMatrix.ps1 @@ -38,14 +38,30 @@ if (-not (Get-Command docker -CommandType Application -ErrorAction SilentlyConti } if (-not $env:SIMDLIB_BUILD_REVISION) { - $env:SIMDLIB_BUILD_REVISION = (& git -C $repositoryRoot rev-parse HEAD).Trim() - if ($LASTEXITCODE -ne 0) { + $revisionOutput = & git -C $repositoryRoot rev-parse HEAD + $revisionExitCode = $LASTEXITCODE + $revision = ([string]$revisionOutput).Trim() + if ($revisionExitCode -ne 0 -or $revision -notmatch '^[0-9a-f]{40}$') { throw 'Unable to determine the SimdLib revision for operation provenance.' } + $env:SIMDLIB_BUILD_REVISION = $revision } if ($IsLinux -or $IsMacOS) { - $env:SIMDLIB_HOST_UID = (& id -u).Trim() - $env:SIMDLIB_HOST_GID = (& id -g).Trim() + # Validate host IDs before forwarding them as the container's user identity. + $uidOutput = & id -u + $uidExitCode = $LASTEXITCODE + $hostUid = ([string]$uidOutput).Trim() + if ($uidExitCode -ne 0 -or $hostUid -notmatch '^\d+$') { + throw 'Unable to determine the host user ID for the container.' + } + $gidOutput = & id -g + $gidExitCode = $LASTEXITCODE + $hostGid = ([string]$gidOutput).Trim() + if ($gidExitCode -ne 0 -or $hostGid -notmatch '^\d+$') { + throw 'Unable to determine the host group ID for the container.' + } + $env:SIMDLIB_HOST_UID = $hostUid + $env:SIMDLIB_HOST_GID = $hostGid } <# diff --git a/tools/Run-NativeMatrix.ps1 b/tools/Run-NativeMatrix.ps1 index 6dbe5b8..2786d88 100644 --- a/tools/Run-NativeMatrix.ps1 +++ b/tools/Run-NativeMatrix.ps1 @@ -144,7 +144,13 @@ function Get-NativeCompilerIdentity { $version = if ($BuildCell.Compiler -eq 'msvc') { $command.FileVersionInfo.ProductVersion } else { - (& $command.Source --version | Select-Object -First 1).Trim() + $versionOutput = & $command.Source --version + $versionExitCode = $LASTEXITCODE + $versionLine = ([string]($versionOutput | Select-Object -First 1)).Trim() + if ($versionExitCode -ne 0 -or -not $versionLine) { + throw "Unable to determine the compiler version at $($command.Source) (exit $versionExitCode)." + } + $versionLine } return [ordered]@{ id = $BuildCell.Compiler; path = $command.Source; version = $version } } @@ -158,6 +164,13 @@ Native cell definition. function Initialize-NativeArtifact { param([Parameter(Mandatory)]$BuildCell) $compilerIdentity = Get-NativeCompilerIdentity -BuildCell $BuildCell + # Reject missing version output before recording a toolchain fingerprint. + $cmakeOutput = & $cmake --version + $cmakeExitCode = $LASTEXITCODE + $cmakeVersion = ([string]($cmakeOutput | Select-Object -First 1)).Trim() + if ($cmakeExitCode -ne 0 -or -not $cmakeVersion) { + throw "Unable to determine the CMake version at $cmake (exit $cmakeExitCode)." + } $fingerprint = [ordered]@{ schema = 'simdlib.build-cell-fingerprint.v1' platform = 'windows-x64' @@ -172,7 +185,7 @@ function Initialize-NativeArtifact { cxxStandard = '20-and-23-register' } dependencies = [ordered]@{ - cmakeVersion = (& $cmake --version | Select-Object -First 1).Trim() + cmakeVersion = $cmakeVersion ninjaPath = if ($BuildCell.Generator -eq 'Ninja') { $ninja } else { '' } visualStudio = $visualStudio catch2Commit = '2b60af89e23d28eefc081bc930831ee9d45ea58b' diff --git a/tools/Run-WindowsClang20Container.ps1 b/tools/Run-WindowsClang20Container.ps1 index 6b7acb0..1b29ad0 100644 --- a/tools/Run-WindowsClang20Container.ps1 +++ b/tools/Run-WindowsClang20Container.ps1 @@ -29,8 +29,15 @@ if (-not (Get-Command docker -CommandType Application -ErrorAction SilentlyConti throw 'Docker is required to run the Windows clang-cl 20 container.' } -$serverOs = (& docker version --format '{{.Server.Os}}' 2>&1).Trim() -if ($LASTEXITCODE -ne 0) { throw "Unable to query the Docker engine: $serverOs" } +# Native stderr can contain ErrorRecord objects; preserve the diagnostic as text +# and check the exit code before interpreting the response as an operating system. +$dockerOutput = & docker version --format '{{.Server.Os}}' 2>&1 +$dockerExitCode = $LASTEXITCODE +$dockerText = ($dockerOutput | ForEach-Object { "$_" }) -join "`n" +if ($dockerExitCode -ne 0) { + throw "Unable to query the Docker engine (exit $dockerExitCode):`n$dockerText" +} +$serverOs = $dockerText.Trim() if ($serverOs -ne 'windows') { throw "The Windows clang-cl 20 container requires a Windows Docker engine; selected engine: $serverOs" } @@ -45,8 +52,10 @@ $script = switch ($Action) { 'Test' { 'tools/Run-Tests.ps1' } 'BuildBenchmarks' { 'tools/Build-Benchmarks.ps1' } } -$revision = (& git -C $repositoryRoot rev-parse HEAD).Trim() -if ($LASTEXITCODE -ne 0 -or $revision -notmatch '^[0-9a-f]{40}$') { +$revisionOutput = & git -C $repositoryRoot rev-parse HEAD +$revisionExitCode = $LASTEXITCODE +$revision = ([string]$revisionOutput).Trim() +if ($revisionExitCode -ne 0 -or $revision -notmatch '^[0-9a-f]{40}$') { throw 'Unable to determine the source revision for container provenance.' } From b7a33f6053bbf9dbfb3cfb5210e246376fc4971d Mon Sep 17 00:00:00 2001 From: David Sisco Date: Sun, 13 Sep 2026 23:16:40 -0700 Subject: [PATCH 12/16] fix: GCC build error due to intrinsic method name --- include/SimdLib/Bmi.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/SimdLib/Bmi.h b/include/SimdLib/Bmi.h index 712cedd..7288a70 100644 --- a/include/SimdLib/Bmi.h +++ b/include/SimdLib/Bmi.h @@ -783,13 +783,23 @@ template [[nodiscard]] constexpr int_t SIMD_FLAGS(Neither, #if SIMDLIB_TARGET_X64 if constexpr (sizeof(int_t) == sizeof(std::uint64_t)) { +#if SIMDLIB_COMPILER_GCC + // GCC exposes the packed-control BEXTR intrinsic under the double-underscore name. + return static_cast(__bextr_u64(static_cast(source), static_cast(control))); +#else return static_cast(_bextr2_u64(static_cast(source), static_cast(control))); +#endif } else #endif if constexpr (sizeof(int_t) == sizeof(std::uint32_t)) { +#if SIMDLIB_COMPILER_GCC + // Use the packed control directly, matching the 64-bit intrinsic selection. + return static_cast(__bextr_u32(static_cast(source), control)); +#else return static_cast(_bextr2_u32(static_cast(source), control)); +#endif } } #endif From 1469dbe4171a58d4c6d7c8a1aa258ebe48e06e09 Mon Sep 17 00:00:00 2001 From: David Sisco Date: Sun, 13 Sep 2026 23:28:25 -0700 Subject: [PATCH 13/16] chore: properly implement bextr2 intrinsic via templated method for guaranteed compile-time control masks --- include/SimdLib/Bmi.h | 45 +++++++++++++++++++++++--- tests/constexpr/BmiConstexpr.tests.cpp | 8 +++++ 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/include/SimdLib/Bmi.h b/include/SimdLib/Bmi.h index 7288a70..0a6c81b 100644 --- a/include/SimdLib/Bmi.h +++ b/include/SimdLib/Bmi.h @@ -774,8 +774,11 @@ template * @param source The integer from which to extract bits. * @param control The encoded start and length fields. * @return The extracted bit range. + * @deprecated Use bextr(source) to pack the control at compile time. */ -template [[nodiscard]] constexpr int_t SIMD_FLAGS(Neither, ForceInline) bextr(const int_t source, const std::uint32_t control) noexcept +template +[[deprecated("Use bextr(source) for compile-time control packing.")]] +[[nodiscard]] constexpr int_t SIMD_FLAGS(Neither, ForceInline) bextr(const int_t source, const std::uint32_t control) noexcept { #if SIMDLIB_TARGET_X86 && SIMDLIB_HAS_BMI1 if (!std::is_constant_evaluated()) @@ -841,13 +844,47 @@ template return bextr(source, static_cast(len), start); } -/// @brief Extract contiguous bits from source integer, and return them shifted to the LSB side of the output. Extract the number of bits specified by len, -/// starting at the bit specified by start. +/** + * @brief Extracts bits using a control mask packed at compile time. + * @tparam int_t Source and result integer type. + * @tparam start Starting bit index, from 0 through 255. + * @tparam len Extraction length, from 0 through 255. + * @param source The integer from which to extract bits. + * @return The extracted bits shifted to the least-significant side; zero when + * the start is outside the source width or the length is zero. + */ template [[nodiscard]] constexpr int_t SIMD_FLAGS(Neither, ForceInline) bextr(const int_t source) noexcept { static_assert(start <= 255 && len <= 255, "BMI bit-extract controls must fit the intrinsic control fields"); - return bextr(source, static_cast(len), static_cast(start)); +#if SIMDLIB_TARGET_X86 && SIMDLIB_HAS_BMI1 + if (!std::is_constant_evaluated()) + { + // Pack the template arguments directly into the intrinsic's control operand. + [[maybe_unused]] constexpr std::uint32_t control = static_cast(start) | (static_cast(len) << 8u); +#if SIMDLIB_TARGET_X64 + if constexpr (sizeof(int_t) == sizeof(std::uint64_t)) + { +#if SIMDLIB_COMPILER_GCC + return static_cast(__bextr_u64(static_cast(source), static_cast(control))); +#else + return static_cast(_bextr2_u64(static_cast(source), static_cast(control))); +#endif + } + else +#endif + if constexpr (sizeof(int_t) == sizeof(std::uint32_t)) + { +#if SIMDLIB_COMPILER_GCC + return static_cast(__bextr_u32(static_cast(source), control)); +#else + return static_cast(_bextr2_u32(static_cast(source), control)); +#endif + } + } +#endif + // Constant evaluation and widths without a matching intrinsic use defined shifts. + return Detail::portable_bextr(source, static_cast(start), static_cast(len)); } #pragma endregion diff --git a/tests/constexpr/BmiConstexpr.tests.cpp b/tests/constexpr/BmiConstexpr.tests.cpp index 6baa87c..57f5ba8 100644 --- a/tests/constexpr/BmiConstexpr.tests.cpp +++ b/tests/constexpr/BmiConstexpr.tests.cpp @@ -151,6 +151,14 @@ template [[nodiscard]] consteval bool bmi_width_contract static_assert(blsmsk(0b10100) == 0b00111); static_assert(bextr(0xFEDC'BA98u, 8, 12) == 0xCBu); static_assert(bextr(0xFEDC'BA98u, 12u | (8u << 8u) | 0xFFFF'0000u) == 0xCBu); +static_assert(bextr(0xFEDC'BA98u) == 0xCBu); +static_assert(bextr(0x1234'5678'9ABC'DEF0ULL) == 0x56u); +static_assert(bextr(0xFFFF'FFFFu) == 0u); +static_assert(bextr(0xFFFF'FFFFu) == 0u); +static_assert(bextr(0xFEDC'BA98u) == 0xFu); +static_assert(bextr(0x1234'5678'9ABC'DEF0ULL) == 0x1234'5678'9ABC'DEF0ULL); +static_assert(bextr(0xABu) == 0xAu); +static_assert(bextr(-1) == 15); static_assert(pdep_u64(0b101u, 0b01010100u) == 0b01000100u); static_assert(pext_u64(0b01000100u, 0b01010100u) == 0b101u); static_assert(bmi_width_contract()); From 6ef2f5db21e4fb139ecb8e254fe5a3e5c24120ce Mon Sep 17 00:00:00 2001 From: David Sisco Date: Sun, 13 Sep 2026 23:29:59 -0700 Subject: [PATCH 14/16] dev: correct wording for a few project tasks --- docs/project.todo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/project.todo b/docs/project.todo index 27cc0cd..99d9e44 100644 --- a/docs/project.todo +++ b/docs/project.todo @@ -6,8 +6,8 @@ Code Architecture: ☐ Implement a `SimdLib::ImmMask` class to represent compile-time immediate-mode masks for SIMD intrinsics, providing methods for creating and manipulating masks based on compile-time conditions. This class should be compatible with the `SimdLib::Register` and `SimdLib::Tensor` classes, allowing for efficient lane control in SIMD operations. ☐ Make register types implicitly convertable to their `native_type`. ☐ Make register types implicitly constructable from their `native_type`. - ☐ SimdApi: implement support for 32-bit add, subtract, and multiply operations. - ☐ SimdApi: implement support for 64-bit add, subtract, and multiply operations. + ☐ SimdApi: implement support for 32-bit saturated add, subtract, and multiply operations. + ☐ SimdApi: implement support for 64-bit saturated add, subtract, and multiply operations. ☐ Design a `SimdLib::Tensor` class to represent multi-dimensional arrays (tensors) and provide methods for performing tensor operations in a SIMD context. The Tensor type should support various data types and dimensions, allowing for efficient manipulation of large datasets in parallel. From 75c483da1727d68c3137b7b66e1f55f9532077ac Mon Sep 17 00:00:00 2001 From: David Sisco Date: Mon, 14 Sep 2026 00:03:24 -0700 Subject: [PATCH 15/16] fix: replace calls to deprecated bextr2 wrapper where start/length arent known at compile time --- include/SimdLib/UInt128.h | 8 ++++---- tests/Bmi.tests.cpp | 8 +------- tests/UInt128.tests.cpp | 11 +++++------ tests/constexpr/BmiConstexpr.tests.cpp | 1 - 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/include/SimdLib/UInt128.h b/include/SimdLib/UInt128.h index ae8536f..f7d127b 100644 --- a/include/SimdLib/UInt128.h +++ b/include/SimdLib/UInt128.h @@ -692,19 +692,19 @@ namespace SimdLib::Bmi { // An upper-word range becomes one native 64-bit extraction after rebasing its start field. if (start >= 64) - return uint128_t{Bmi::bextr(value.high(), control - 64u)}; + return uint128_t{Bmi::bextr(value.high(), len, static_cast(start - 64u))}; // A range wholly inside the low word maps directly to one native extraction. if (len <= 64u - start) - return uint128_t{Bmi::bextr(value.low(), control)}; + return uint128_t{Bmi::bextr(value.low(), len, start)}; // Cross-word ranges first align their next 64 result bits with one funnel shift. const std::uint64_t resultLow = Detail::funnel_shift_right(value.low(), value.high(), start); if (len <= 64) - return uint128_t{Bmi::bextr(resultLow, control & 0xFF00u)}; + return uint128_t{Bmi::bextr(resultLow, len, 0)}; // Longer ranges retain the aligned low word and extract only the remaining high result bits. - return uint128_t{resultLow, Bmi::bextr(value.high(), control - (64u << 8u))}; + return uint128_t{resultLow, Bmi::bextr(value.high(), static_cast(len - 64u), 0)}; } #endif // Constant evaluation and targets without native 64-bit BEXTR retain the portable whole-value contract. diff --git a/tests/Bmi.tests.cpp b/tests/Bmi.tests.cpp index 09ee297..f6ceb3c 100644 --- a/tests/Bmi.tests.cpp +++ b/tests/Bmi.tests.cpp @@ -567,8 +567,6 @@ TEST_CASE("BMI exhaustive 8-bit domains match scalar references", "[simdlib][bmi for (unsigned len = 0; len <= 10; ++len) { CHECK(Bmi::bextr(byte, static_cast(len), static_cast(start)) == reference_bextr(byte, start, len)); - const std::uint32_t control = start | (len << 8u); - CHECK(Bmi::bextr(byte, control) == reference_bextr(byte, start, len)); } for (unsigned mask = 0; mask <= 0xFF; ++mask) @@ -605,14 +603,12 @@ TEST_CASE("BMI randomized 32-bit operations match scalar references", "[simdlib] const unsigned index = static_cast(random.next() % 41); const unsigned start = static_cast(random.next() % 41); const unsigned len = static_cast(random.next() % 41); - const std::uint32_t control = start | (len << 8u) | 0xA5A5'0000u; CHECK(Bmi::andn(lhs, rhs) == (rhs & ~lhs)); CHECK(Bmi::bzhi(lhs, index) == reference_bzhi(lhs, index)); CHECK(Bmi::blsi(lhs) == (lhs & (0u - lhs))); CHECK(Bmi::blsr(lhs) == (lhs & (lhs - 1u))); CHECK(Bmi::blsmsk(lhs) == (lhs ^ (lhs - 1u))); CHECK(Bmi::bextr(lhs, static_cast(len), static_cast(start)) == reference_bextr(lhs, start, len)); - CHECK(Bmi::bextr(lhs, control) == reference_bextr(lhs, start, len)); CHECK(Bmi::pdep_u32(lhs, rhs) == reference_pdep(lhs, rhs)); CHECK(Bmi::pext_u32(lhs, rhs) == reference_pext(lhs, rhs)); std::uint32_t high = 0; @@ -633,14 +629,12 @@ TEST_CASE("BMI randomized 64-bit operations match scalar references", "[simdlib] const unsigned index = static_cast(random.next() % 73); const unsigned start = static_cast(random.next() % 73); const unsigned len = static_cast(random.next() % 73); - const std::uint32_t control = start | (len << 8u) | 0x5A5A'0000u; CHECK(Bmi::andn(lhs, rhs) == (rhs & ~lhs)); CHECK(Bmi::bzhi(lhs, index) == reference_bzhi(lhs, index)); CHECK(Bmi::blsi(lhs) == (lhs & (std::uint64_t{0} - lhs))); CHECK(Bmi::blsr(lhs) == (lhs & (lhs - 1))); CHECK(Bmi::blsmsk(lhs) == (lhs ^ (lhs - 1))); CHECK(Bmi::bextr(lhs, static_cast(len), static_cast(start)) == reference_bextr(lhs, start, len)); - CHECK(Bmi::bextr(lhs, control) == reference_bextr(lhs, start, len)); CHECK(Bmi::pdep_u64(lhs, rhs) == reference_pdep(lhs, rhs)); CHECK(Bmi::pext_u64(lhs, rhs) == reference_pext(lhs, rhs)); std::uint64_t high = 0; @@ -682,7 +676,7 @@ TEST_CASE("BMI feature paths produce the scalar-reference result digest", "[simd mix_digest(actual_digest, Bmi::blsi(lhs)); mix_digest(actual_digest, Bmi::blsr(lhs)); mix_digest(actual_digest, Bmi::blsmsk(lhs)); - mix_digest(actual_digest, Bmi::bextr(lhs, static_cast(rhs))); + mix_digest(actual_digest, Bmi::bextr(lhs, static_cast(rhs >> 8u), static_cast(rhs))); mix_digest(actual_digest, Bmi::pdep_u64(lhs, rhs)); mix_digest(actual_digest, Bmi::pdepl_u64(lhs, rhs)); mix_digest(actual_digest, Bmi::pext_u64(lhs, rhs)); diff --git a/tests/UInt128.tests.cpp b/tests/UInt128.tests.cpp index 7b201ef..23071ae 100644 --- a/tests/UInt128.tests.cpp +++ b/tests/UInt128.tests.cpp @@ -533,8 +533,6 @@ TEST_CASE("uint128 deprecated extraction remains compatible with Bmi bextr at bo CAPTURE(length, start); CHECK(deprecated_extract(source, length, start) == test.expected); CHECK(SimdLib::Bmi::bextr(source, length, start) == test.expected); - const volatile std::uint32_t control = static_cast(start) | (static_cast(length) << 8u) | 0xA5A5'0000u; - CHECK(SimdLib::Bmi::bextr(source, control) == test.expected); } } @@ -550,11 +548,10 @@ TEST_CASE("uint128 Bmi bextr control fields match the portable oracle exhaustive { for (unsigned length = 0; length <= 0xFF; ++length) { - const std::uint32_t control = start | (length << 8u) | 0xA5A5'0000u; for (const words128 source : sources) { CAPTURE(start, length, source.low, source.high); - CHECK(words(SimdLib::Bmi::bextr(uint128_t{source.low, source.high}, control)) == bextr_words(source, length, start)); + CHECK(words(SimdLib::Bmi::bextr(uint128_t{source.low, source.high}, static_cast(length), static_cast(start))) == bextr_words(source, length, start)); } } } @@ -677,6 +674,8 @@ TEST_CASE("uint128 optimized operations match the portable two-word oracle", "[s const uint128_t rhs{random.next(), random.next()}; const unsigned shift = static_cast(random.next() % 260); const std::uint32_t control = static_cast(random.next()); + const auto start = static_cast(control); + const auto length = static_cast(control >> 8u); CAPTURE(iteration, shift, control, lhs.low(), lhs.high(), rhs.low(), rhs.high()); CHECK(words(lhs + rhs) == add_words(words(lhs), words(rhs))); @@ -687,7 +686,7 @@ TEST_CASE("uint128 optimized operations match the portable two-word oracle", "[s CHECK((words(~lhs) == words128{~lhs.low(), ~lhs.high()})); CHECK(words(lhs << shift) == shift_left_words(words(lhs), shift)); CHECK(words(lhs >> shift) == shift_right_words(words(lhs), shift)); - CHECK(words(SimdLib::Bmi::bextr(lhs, control)) == + CHECK(words(SimdLib::Bmi::bextr(lhs, length, start)) == bextr_words(words(lhs), static_cast((control >> 8u) & 0xFFu), static_cast(control & 0xFFu))); CHECK((lhs < rhs) == (compare_words(words(lhs), words(rhs)) < 0)); CHECK((lhs == rhs) == (compare_words(words(lhs), words(rhs)) == 0)); @@ -744,7 +743,7 @@ TEST_CASE("uint128 compiler paths produce the portable-oracle result digest", "[ mix_digest(actualDigest, ~lhs); mix_digest(actualDigest, lhs << shift); mix_digest(actualDigest, lhs >> shift); - mix_digest(actualDigest, SimdLib::Bmi::bextr(lhs, control)); + mix_digest(actualDigest, SimdLib::Bmi::bextr(lhs, static_cast(control >> 8u), static_cast(control))); const auto add = add_words(words(lhs), words(rhs)); const auto subtract = subtract_words(words(lhs), words(rhs)); diff --git a/tests/constexpr/BmiConstexpr.tests.cpp b/tests/constexpr/BmiConstexpr.tests.cpp index 57f5ba8..fdce476 100644 --- a/tests/constexpr/BmiConstexpr.tests.cpp +++ b/tests/constexpr/BmiConstexpr.tests.cpp @@ -150,7 +150,6 @@ template [[nodiscard]] consteval bool bmi_width_contract static_assert(blsmsk(0b10100) == 0b00111); static_assert(bextr(0xFEDC'BA98u, 8, 12) == 0xCBu); -static_assert(bextr(0xFEDC'BA98u, 12u | (8u << 8u) | 0xFFFF'0000u) == 0xCBu); static_assert(bextr(0xFEDC'BA98u) == 0xCBu); static_assert(bextr(0x1234'5678'9ABC'DEF0ULL) == 0x56u); static_assert(bextr(0xFFFF'FFFFu) == 0u); From 84b745e5a91eaedbdc77110c323517091979284a Mon Sep 17 00:00:00 2001 From: David Sisco Date: Mon, 14 Sep 2026 00:16:58 -0700 Subject: [PATCH 16/16] feat: implement templated bextr2 specialization for uint128_t --- include/SimdLib/UInt128.h | 45 +++++++++++++++++++++- tests/constexpr/UInt128Constexpr.tests.cpp | 3 ++ tests/headers/PublicSurfaceHeaderProbe.cpp | 1 + 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/include/SimdLib/UInt128.h b/include/SimdLib/UInt128.h index f7d127b..d17b5d5 100644 --- a/include/SimdLib/UInt128.h +++ b/include/SimdLib/UInt128.h @@ -704,7 +704,7 @@ namespace SimdLib::Bmi return uint128_t{Bmi::bextr(resultLow, len, 0)}; // Longer ranges retain the aligned low word and extract only the remaining high result bits. - return uint128_t{resultLow, Bmi::bextr(value.high(), static_cast(len - 64u), 0)}; + return uint128_t{resultLow, Bmi::bextr(value.high(), static_cast(len - 64u), start)}; } #endif // Constant evaluation and targets without native 64-bit BEXTR retain the portable whole-value contract. @@ -728,11 +728,52 @@ template return static_cast(bextr(value, static_cast(len), start)); } +/** + * @brief Extracts a compile-time bit range from a 128-bit value. + * @tparam start Starting bit index, from 0 through 128. + * @tparam len Extraction length, from 0 through 128. + * @param value The 128-bit value from which to extract bits. + * @return The extracted bit range shifted to bit zero. + */ template requires(start <= 128 && len <= 128) [[nodiscard]] constexpr uint128_t bextr(const uint128_t value) noexcept { - return bextr(value, static_cast(len), static_cast(start)); + if constexpr (len == 0 || start >= 128) + { + return {}; + } + else + { +#if SIMDLIB_TARGET_X64 && SIMDLIB_HAS_BMI1 + if (!std::is_constant_evaluated()) + { + if constexpr (start >= 64) + { + return uint128_t{Bmi::bextr(value.high())}; + } + else if constexpr (len <= 64 - start) + { + return uint128_t{Bmi::bextr(value.low())}; + } + else + { + // Align the cross-word range once, then extract each compile-time result segment directly. + const std::uint64_t resultLow = Detail::funnel_shift_right(value.low(), value.high(), start); + if constexpr (len <= 64) + { + return uint128_t{Bmi::bextr(resultLow)}; + } + else + { + return uint128_t{resultLow, Bmi::bextr(value.high())}; + } + } + } +#endif + constexpr std::size_t retained = len < 128 - start ? len : 128 - start; + return (value >> start) & uint128_t::create_mask(static_cast(retained)); + } } } // namespace SimdLib::Bmi diff --git a/tests/constexpr/UInt128Constexpr.tests.cpp b/tests/constexpr/UInt128Constexpr.tests.cpp index 8644ead..9601aca 100644 --- a/tests/constexpr/UInt128Constexpr.tests.cpp +++ b/tests/constexpr/UInt128Constexpr.tests.cpp @@ -38,6 +38,9 @@ static_assert(popcount(std::numeric_limits::max()) == 128); return popcount(lhs) == std::popcount(lhs.low()) + std::popcount(lhs.high()) && countr_zero(uint128_t{}) == 128 && countl_zero(uint128_t{}) == 128 && bit_width(highBit) == 128 && bit_floor(highBit) == highBit && bit_ceil(highBit) == highBit && has_single_bit(highBit) && Bmi::bextr(lhs, 17, 61) == ((lhs >> 61) & uint128_t::create_mask(17)) && + Bmi::bextr<61, 17>(lhs) == ((lhs >> 61) & uint128_t::create_mask(17)) && + Bmi::bextr<68, 12>(lhs) == ((lhs >> 68) & uint128_t::create_mask(12)) && + Bmi::bextr<0, 128>(lhs) == lhs && Bmi::bextr<128, 128>(lhs) == uint128_t{} && Bmi::bextr(lhs, 61u | (17u << 8u) | 0xFFFF'0000u) == ((lhs >> 61) & uint128_t::create_mask(17)); } diff --git a/tests/headers/PublicSurfaceHeaderProbe.cpp b/tests/headers/PublicSurfaceHeaderProbe.cpp index 98cdefb..5c0c3ab 100644 --- a/tests/headers/PublicSurfaceHeaderProbe.cpp +++ b/tests/headers/PublicSurfaceHeaderProbe.cpp @@ -8,6 +8,7 @@ static_assert(std::same_as); static_assert(std::same_as); +static_assert(std::same_as(SimdLib::uint128_t{0xF0})), SimdLib::uint128_t>); static_assert(std::same_as); #if SIMDLIB_HAS_SSE42