GCC Code Coverage Report


Directory: include/spy/
File: include/spy/simd.hpp
Date: 2026-10-08 16:24:54
Exec Total Coverage
Lines: 9 9 100.0%
Functions: 10 10 100.0%
Branches: 0 0 -%

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