| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | //================================================================================================== | ||
| 2 | /* | ||
| 3 | SPY - C++ Informations Broker | ||
| 4 | Copyright : SPY Project Contributors | ||
| 5 | SPDX-License-Identifier: BSL-1.0 | ||
| 6 | */ | ||
| 7 | //================================================================================================== | ||
| 8 | #pragma once | ||
| 9 | |||
| 10 | #include <spy/simd/arm.hpp> | ||
| 11 | #include <spy/simd/ppc.hpp> | ||
| 12 | #include <spy/simd/riscv.hpp> | ||
| 13 | #include <spy/simd/wasm.hpp> | ||
| 14 | #include <spy/simd/x86.hpp> | ||
| 15 | |||
| 16 | namespace spy::_ | ||
| 17 | { | ||
| 18 | enum class simd_isa | ||
| 19 | { | ||
| 20 | undefined_ = -1, | ||
| 21 | x86_ = 1000, | ||
| 22 | ppc_ = 2000, | ||
| 23 | arm_ = 3000, | ||
| 24 | arm_sve_ = 3500, | ||
| 25 | wasm_ = 4000, | ||
| 26 | riscv_ = 5000 | ||
| 27 | }; | ||
| 28 | |||
| 29 | enum class simd_version | ||
| 30 | { | ||
| 31 | undefined_ = -1, | ||
| 32 | sse1_ = 1110, | ||
| 33 | sse2_ = 1120, | ||
| 34 | sse3_ = 1130, | ||
| 35 | ssse3_ = 1131, | ||
| 36 | sse41_ = 1141, | ||
| 37 | sse42_ = 1142, | ||
| 38 | avx_ = 1201, | ||
| 39 | avx2_ = 1202, | ||
| 40 | avx512_ = 1300, | ||
| 41 | vmx_ = 2000, | ||
| 42 | vmx_2_03_ = 2203, | ||
| 43 | vmx_2_05_ = 2205, | ||
| 44 | vmx_2_06_ = 2206, | ||
| 45 | vmx_2_07_ = 2207, | ||
| 46 | vmx_3_00_ = 2300, | ||
| 47 | vmx_3_01_ = 2301, | ||
| 48 | vsx_ = 3000, | ||
| 49 | vsx_2_06_ = 3206, | ||
| 50 | vsx_2_07_ = 3207, | ||
| 51 | vsx_3_00_ = 3300, | ||
| 52 | vsx_3_01_ = 3301, | ||
| 53 | neon_ = 4001, | ||
| 54 | asimd_ = 4002, | ||
| 55 | sve_ = 5000, | ||
| 56 | fixed_sve_ = 5100, | ||
| 57 | sve2_ = 5500, | ||
| 58 | fixed_sve2_ = 5600, | ||
| 59 | simd128_ = 6000, | ||
| 60 | rvv_ = 7000, | ||
| 61 | fixed_rvv_ = 7500 | ||
| 62 | }; | ||
| 63 | |||
| 64 | template<simd_isa InsSetArch = simd_isa::undefined_, | ||
| 65 | simd_version Version = simd_version::undefined_> | ||
| 66 | struct simd_info | ||
| 67 | { | ||
| 68 | static constexpr auto isa = InsSetArch; | ||
| 69 | static constexpr auto version = Version; | ||
| 70 | |||
| 71 | static constexpr int width = []() | ||
| 72 | { | ||
| 73 | if constexpr(Version == simd_version::simd128_ || | ||
| 74 | (Version >= simd_version::sse1_ && Version <= simd_version::sse42_) || | ||
| 75 | Version == simd_version::neon_ || Version == simd_version::asimd_ || | ||
| 76 | (Version >= simd_version::vmx_2_03_ && Version <= simd_version::vsx_3_01_)) | ||
| 77 | return 128; | ||
| 78 | else if constexpr(Version == simd_version::avx_ || Version == simd_version::avx2_) return 256; | ||
| 79 | else if constexpr(Version == simd_version::avx512_) return 512; | ||
| 80 | else if constexpr(Version == simd_version::rvv_) return -1; | ||
| 81 | else if constexpr(Version == simd_version::fixed_rvv_) | ||
| 82 | { | ||
| 83 | #if defined(__riscv_v_fixed_vlen) | ||
| 84 | return __riscv_v_fixed_vlen; | ||
| 85 | #else | ||
| 86 | return -1; | ||
| 87 | #endif | ||
| 88 | } | ||
| 89 | else if constexpr(Version == simd_version::sve_ || Version == simd_version::sve2_) | ||
| 90 | { | ||
| 91 | return -1; | ||
| 92 | } | ||
| 93 | else if constexpr(Version == simd_version::fixed_sve_ || Version == simd_version::fixed_sve2_) | ||
| 94 | { | ||
| 95 | #if defined(__ARM_FEATURE_SVE_BITS) | ||
| 96 | return __ARM_FEATURE_SVE_BITS; | ||
| 97 | #else | ||
| 98 | return -1; | ||
| 99 | #endif | ||
| 100 | } | ||
| 101 | else return -1; | ||
| 102 | }(); | ||
| 103 | |||
| 104 | static constexpr bool has_fixed_cardinal() { return width != -1; } | ||
| 105 | |||
| 106 | 2 | template<_::stream OS> friend auto& operator<<(OS& os, simd_info const&) | |
| 107 | { | ||
| 108 | if constexpr(Version == simd_version::simd128_) os << "WASM SIMD128"; | ||
| 109 | else if constexpr(Version == simd_version::sse1_) os << "X86 SSE"; | ||
| 110 | 2 | else if constexpr(Version == simd_version::sse2_) os << "X86 SSE2"; | |
| 111 | else if constexpr(Version == simd_version::sse3_) os << "X86 SSE3"; | ||
| 112 | else if constexpr(Version == simd_version::ssse3_) os << "X86 SSSE3"; | ||
| 113 | else if constexpr(Version == simd_version::sse41_) os << "X86 SSE4.1"; | ||
| 114 | else if constexpr(Version == simd_version::sse42_) os << "X86 SSE4.2"; | ||
| 115 | else if constexpr(Version == simd_version::avx_) os << "X86 AVX"; | ||
| 116 | else if constexpr(Version == simd_version::avx2_) os << "X86 AVX2"; | ||
| 117 | else if constexpr(Version == simd_version::avx512_) os << "X86 AVX512"; | ||
| 118 | else if constexpr(Version >= simd_version::vmx_2_03_ && Version <= simd_version::vmx_3_01_) | ||
| 119 | { | ||
| 120 | constexpr auto v = static_cast<int>(Version); | ||
| 121 | os << "PPC VMX with ISA v" << ((v - 2000) / 100.); | ||
| 122 | } | ||
| 123 | else if constexpr(Version >= simd_version::vsx_2_06_ && Version <= simd_version::vsx_3_01_) | ||
| 124 | { | ||
| 125 | constexpr auto v = static_cast<int>(Version); | ||
| 126 | os << "PPC VSX with ISA v" << ((v - 3000) / 100.); | ||
| 127 | } | ||
| 128 | else if constexpr(Version == simd_version::neon_) os << "ARM NEON"; | ||
| 129 | else if constexpr(Version == simd_version::asimd_) os << "ARM ASIMD"; | ||
| 130 | else if constexpr(Version == simd_version::sve_) os << "ARM SVE (dyn.)"; | ||
| 131 | else if constexpr(Version == simd_version::fixed_sve_) | ||
| 132 | os << "ARM SVE (" << simd_info::width << " bits)"; | ||
| 133 | else if constexpr(Version == simd_version::sve2_) os << "ARM SVE2 (dyn.)"; | ||
| 134 | else if constexpr(Version == simd_version::fixed_sve2_) | ||
| 135 | os << "ARM SVE2 (" << simd_info::width << " bits)"; | ||
| 136 | else if constexpr(Version == simd_version::rvv_) os << "RISC-V RVV (dyn.)"; | ||
| 137 | else if constexpr(Version == simd_version::fixed_rvv_) | ||
| 138 | os << "RISC-V RVV (" << simd_info::width << " bits)"; | ||
| 139 | else return os << "Undefined SIMD instructions set"; | ||
| 140 | |||
| 141 | if constexpr(spy::supports::fma_) os << " (with FMA3 support)"; | ||
| 142 | if constexpr(spy::supports::fma4_) os << " (with FMA4 support)"; | ||
| 143 | if constexpr(spy::supports::xop_) os << " (with XOP support)"; | ||
| 144 | |||
| 145 | 2 | return os; | |
| 146 | } | ||
| 147 | }; | ||
| 148 | |||
| 149 | template<simd_isa I1, simd_version V1, simd_isa I2, simd_version V2> | ||
| 150 | 18 | constexpr bool operator==(simd_info<I1, V1>, simd_info<I2, V2>) noexcept | |
| 151 | { | ||
| 152 | if constexpr(V1 != simd_version::undefined_ && V2 != simd_version::undefined_) | ||
| 153 | 8 | return (I1 == I2) && (V1 == V2); | |
| 154 | 10 | else return I1 == I2; | |
| 155 | } | ||
| 156 | |||
| 157 | template<simd_isa I1, simd_version V1, simd_isa I2, simd_version V2> | ||
| 158 | 25 | constexpr std::partial_ordering operator<=>(simd_info<I1, V1>, simd_info<I2, V2>) noexcept | |
| 159 | { | ||
| 160 | 17 | if constexpr(I1 != I2) return std::partial_ordering::unordered; | |
| 161 | 8 | else return static_cast<int>(V1) <=> static_cast<int>(V2); | |
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | namespace spy | ||
| 166 | { | ||
| 167 | // clang-format off | ||
| 168 | //================================================================================================ | ||
| 169 | //! @ingroup spy_isa | ||
| 170 | //! @brief SIMD extensions set reporting value | ||
| 171 | //! | ||
| 172 | //! @groupheader{SIMD Instructions Sets} | ||
| 173 | //! SIMD extensions set detection is made so that one can ask if the current SIMD extension is | ||
| 174 | //! exactly, below or above a given reference instruction set. Detectable instructions sets depends | ||
| 175 | //! on SIMD hardware vendor | ||
| 176 | //! | ||
| 177 | //! | Architecture | Supported SIMD instructions sets | | ||
| 178 | //! | ----------------- | --------------------------------------------------------------------------------------- | | ||
| 179 | //! | **X86 SSE** | `spy::sse1_`, `spy::sse2_`, `spy::sse3_`, `spy::ssse3_`, `spy::sse41_`, `spy::sse42_` | | ||
| 180 | //! | **X86 AVX** | `spy::avx_`, `spy::avx2_`, `spy::avx512_` | | ||
| 181 | //! | **Power PC VMX** | `spy::vmx_`, `spy::vmx_2_03_`, `spy::vmx_2_05_`, `spy::vmx_2_06_`, `spy::vmx_2_07_`, `spy::vmx_3_00_`, `spy::vmx_3_01_` | | ||
| 182 | //! | **Power PC VSX** | `spy::vsx_`, `spy::vsx_2_06_`, `spy::vsx_2_07_`, `spy::vsx_3_00_`, `spy::vsx_3_01_` | | ||
| 183 | //! | **ARM NEON** | `spy::neon_`, `spy::asimd_` | | ||
| 184 | //! | **ARM SVE** | `spy::sve_`, `spy::sve128_`, `spy::sve256_`, `spy::sve512_`, `spy::sve1024_` | | ||
| 185 | //! | **WASM** | `spy::simd128_` | | ||
| 186 | //! | **RISC-V** | `spy::rvv_` | | ||
| 187 | //! | ||
| 188 | //! Complete set of comparison operators is provided for those sets. Order of instructions sets | ||
| 189 | //! are built so that if an instructions set supersedes another, it is considered greater than. For | ||
| 190 | //! example, `spy::avx_` is greater than `spy::sse41_` as the former is a super-set of the later. | ||
| 191 | //! Comparing SIMD descriptors across architecture is undefined behavior. | ||
| 192 | //! | ||
| 193 | //! Moreover, the `spy::simd_instruction_set` object exposes a constexpr `width` static field that | ||
| 194 | //! contains the size in bits of the current SIMD instructions set registers. | ||
| 195 | //! | ||
| 196 | //! @subgroupheader{Example - SIMD Instructions Sets} | ||
| 197 | //! @godbolt{samples/simd.cpp} | ||
| 198 | //! | ||
| 199 | //! @groupheader{SIMD Architectures} | ||
| 200 | //! | ||
| 201 | //! One can also simply asks if a given family of instructions set is available. | ||
| 202 | //! | ||
| 203 | //! | Architecture | Generic indicator | | ||
| 204 | //! | ------------- | --------------------| | ||
| 205 | //! | **X86** | `spy::x86_simd_` | | ||
| 206 | //! | **Power PC** | `spy::ppc_simd_` | | ||
| 207 | //! | **ARM** | `spy::arm_simd_` | | ||
| 208 | //! | **WASM** | `spy::wasm_simd_` | | ||
| 209 | //! | **RISC-V** | `spy::riscv_simd_` | | ||
| 210 | //! | ||
| 211 | //! @subgroupheader{Example - SIMD Architectures} | ||
| 212 | //! @godbolt{samples/simd-arch.cpp} | ||
| 213 | //! | ||
| 214 | //! @groupheader{Supplemental Instructions sets} | ||
| 215 | //! | ||
| 216 | //! Some SIMD instructions sets provide supplemental instructions on top of an existing one. The | ||
| 217 | //! `spy::supports` namespace carries one indicator per supplemental set, `spy::supports::fma_`, | ||
| 218 | //! `spy::supports::fma4_`, `spy::supports::xop_` and `spy::supports::f16c_` for the AVX family, and | ||
| 219 | //! the `spy::supports::avx512` namespace for the AVX-512 subsets. Each of them is documented on its | ||
| 220 | //! own in this group. | ||
| 221 | //! | ||
| 222 | //! @subgroupheader{Example - Supplemental Instructions Sets} | ||
| 223 | //! @godbolt{samples/simd-additional.cpp} | ||
| 224 | //! | ||
| 225 | //================================================================================================ | ||
| 226 | // clang-format off | ||
| 227 | #if defined(SPY_SIMD_DETECTED) | ||
| 228 | constexpr inline auto simd_instruction_set = _::simd_info<SPY_SIMD_VENDOR, SPY_SIMD_DETECTED> {}; | ||
| 229 | #else | ||
| 230 | constexpr inline auto simd_instruction_set = _::simd_info<> {}; | ||
| 231 | #endif | ||
| 232 | |||
| 233 | //================================================================================================ | ||
| 234 | // Available SIMD instructions set | ||
| 235 | //================================================================================================ | ||
| 236 | constexpr inline auto undefined_simd_ = _::simd_info<> {}; | ||
| 237 | |||
| 238 | template<_::simd_version V = _::simd_version::undefined_> | ||
| 239 | using wasm_simd_info = _::simd_info<_::simd_isa::wasm_, V>; | ||
| 240 | |||
| 241 | constexpr inline auto wasm_simd_ = wasm_simd_info<> {}; | ||
| 242 | constexpr inline auto simd128_ = wasm_simd_info<_::simd_version::simd128_> {}; | ||
| 243 | |||
| 244 | template<_::simd_version V = _::simd_version::undefined_> | ||
| 245 | using x86_simd_info = _::simd_info<_::simd_isa::x86_, V>; | ||
| 246 | |||
| 247 | constexpr inline auto x86_simd_ = x86_simd_info<> {}; | ||
| 248 | constexpr inline auto sse1_ = x86_simd_info<_::simd_version::sse1_> {}; | ||
| 249 | constexpr inline auto sse2_ = x86_simd_info<_::simd_version::sse2_> {}; | ||
| 250 | constexpr inline auto sse3_ = x86_simd_info<_::simd_version::sse3_> {}; | ||
| 251 | constexpr inline auto ssse3_ = x86_simd_info<_::simd_version::ssse3_> {}; | ||
| 252 | constexpr inline auto sse41_ = x86_simd_info<_::simd_version::sse41_> {}; | ||
| 253 | constexpr inline auto sse42_ = x86_simd_info<_::simd_version::sse42_> {}; | ||
| 254 | constexpr inline auto avx_ = x86_simd_info<_::simd_version::avx_> {}; | ||
| 255 | constexpr inline auto avx2_ = x86_simd_info<_::simd_version::avx2_> {}; | ||
| 256 | constexpr inline auto avx512_ = x86_simd_info<_::simd_version::avx512_> {}; | ||
| 257 | |||
| 258 | template<_::simd_version V = _::simd_version::undefined_> | ||
| 259 | using ppc_simd_info = _::simd_info<_::simd_isa::ppc_, V>; | ||
| 260 | |||
| 261 | constexpr inline auto ppc_simd_ = ppc_simd_info<> {}; | ||
| 262 | constexpr inline auto vmx_ = ppc_simd_info<_::simd_version::vmx_> {}; | ||
| 263 | constexpr inline auto vmx_2_03_ = ppc_simd_info<_::simd_version::vmx_2_03_> {}; | ||
| 264 | constexpr inline auto vmx_2_05_ = ppc_simd_info<_::simd_version::vmx_2_05_> {}; | ||
| 265 | constexpr inline auto vmx_2_06_ = ppc_simd_info<_::simd_version::vmx_2_06_> {}; | ||
| 266 | constexpr inline auto vmx_2_07_ = ppc_simd_info<_::simd_version::vmx_2_07_> {}; | ||
| 267 | constexpr inline auto vmx_3_00_ = ppc_simd_info<_::simd_version::vmx_3_00_> {}; | ||
| 268 | constexpr inline auto vmx_3_01_ = ppc_simd_info<_::simd_version::vmx_3_01_> {}; | ||
| 269 | |||
| 270 | constexpr inline auto vsx_ = ppc_simd_info<_::simd_version::vsx_> {}; | ||
| 271 | constexpr inline auto vsx_2_06_ = ppc_simd_info<_::simd_version::vsx_2_06_> {}; | ||
| 272 | constexpr inline auto vsx_2_07_ = ppc_simd_info<_::simd_version::vsx_2_07_> {}; | ||
| 273 | constexpr inline auto vsx_3_00_ = ppc_simd_info<_::simd_version::vsx_3_00_> {}; | ||
| 274 | constexpr inline auto vsx_3_01_ = ppc_simd_info<_::simd_version::vsx_3_01_> {}; | ||
| 275 | |||
| 276 | template<_::simd_version V = _::simd_version::undefined_> | ||
| 277 | using arm_simd_info = _::simd_info<_::simd_isa::arm_, V>; | ||
| 278 | |||
| 279 | template<_::simd_version V = _::simd_version::undefined_> | ||
| 280 | using sve_simd_info = _::simd_info<_::simd_isa::arm_sve_, V>; | ||
| 281 | |||
| 282 | constexpr inline auto arm_simd_ = arm_simd_info<> {}; | ||
| 283 | constexpr inline auto neon_ = arm_simd_info<_::simd_version::neon_> {}; | ||
| 284 | constexpr inline auto asimd_ = arm_simd_info<_::simd_version::asimd_> {}; | ||
| 285 | constexpr inline auto sve_ = sve_simd_info<_::simd_version::sve_> {}; | ||
| 286 | constexpr inline auto fixed_sve_ = sve_simd_info<_::simd_version::fixed_sve_> {}; | ||
| 287 | constexpr inline auto sve2_ = sve_simd_info<_::simd_version::sve2_> {}; | ||
| 288 | constexpr inline auto fixed_sve2_ = sve_simd_info<_::simd_version::fixed_sve2_> {}; | ||
| 289 | |||
| 290 | template<_::simd_version V = _::simd_version::undefined_> | ||
| 291 | using riscv_simd_info = _::simd_info<_::simd_isa::riscv_, V>; | ||
| 292 | constexpr inline auto riscv_simd_ = riscv_simd_info<> {}; | ||
| 293 | constexpr inline auto rvv_ = riscv_simd_info<_::simd_version::rvv_> {}; | ||
| 294 | constexpr inline auto fixed_rvv_ = riscv_simd_info<_::simd_version::fixed_rvv_> {}; | ||
| 295 | } | ||
| 296 |