SuperDex Physics C++ API
Loading...
Searching...
No Matches
x64_simd_float_4_inl.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include "x64_simd_inl.h" // for IntelliSense
20
21#if MOCHI_USE_SIMD && MOCHI_ARCH_X64_AVX2
22
23namespace superdex {
24
25/***********************************************************************************************
26 Simd<float, 4>
27*/
28template <>
29class Simd<float, 4> {
30 public:
31 MOCHI_NATIVE_SIMD_IMPL_BOILERPLATE(float, 4, __m128);
32 Simd(float a, float b, float c = 0.0f, float d = 0.0f) : raw(_mm_set_ps(d, c, b, a)) {} // SSE
33 template <class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
34 Simd(U a) : raw(_mm_set_ps1(a)) {} // SSE
35
36 template <int i>
37 [[nodiscard]] static MOCHI_FORCE_INLINE float Get(Simd v) {
38 static_assert(i >= 0 && i < 4, "Index out of range");
39 if constexpr (i == 0) {
40 return _mm_cvtss_f32(v.raw); // SSE
41 } else if constexpr (i == 1) {
42 return _mm_cvtss_f32(_mm_shuffle_ps(v.raw, v.raw, _MM_SHUFFLE(1, 1, 1, 1))); // SSE, SSE
43 } else if constexpr (i == 2) {
44 return _mm_cvtss_f32(_mm_shuffle_ps(v.raw, v.raw, _MM_SHUFFLE(2, 2, 2, 2))); // SSE, SSE
45 } else if constexpr (i == 3) {
46 return _mm_cvtss_f32(_mm_shuffle_ps(v.raw, v.raw, _MM_SHUFFLE(3, 3, 3, 3))); // SSE, SSE
47 }
48 }
49
50 [[nodiscard]] static MOCHI_FORCE_INLINE float Get(Simd v, int i) {
51 MOCHI_ASSERT_VERBOSE(i >= 0 && i < kSize, "Index out of range");
52#if MOCHI_COMPILER_MSVC
53 return v.raw.m128_f32[i];
54#else
55 switch (i) { // clang-format off
56 case 0: return Get<0>(v);
57 case 1: return Get<1>(v);
58 case 2: return Get<2>(v);
59 case 3: return Get<3>(v);
60 MOCHI_UNLIKELY default: return 0.0f;
61 } // clang-format on
62#endif
63 }
64
65 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Set(Simd v, int i, Scalar value) {
66 MOCHI_ASSERT_VERBOSE(i >= 0 && i < kSize, "Index out of range");
67#if MOCHI_COMPILER_MSVC
68 auto result = v;
69 result.raw.m128_f32[i] = value;
70 return result;
71#else
72 static constexpr __m128i kMasks[] = {
73 // clang-format off
74 {static_cast<long long>(0x00000000FFFFFFFFLL), static_cast<long long>(0x0000000000000000LL)},
75 {static_cast<long long>(0xFFFFFFFF00000000LL), static_cast<long long>(0x0000000000000000LL)},
76 {static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0x00000000FFFFFFFFLL)},
77 {static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0xFFFFFFFF00000000LL)}
78 }; // clang-format on
79 return _mm_blendv_ps(v.raw, _mm_set1_ps(value), _mm_castsi128_ps(kMasks[i])); // SSE4.1
80#endif
81 }
82
83 template <int i>
84 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Set(Simd v, Scalar value) {
85 return _mm_insert_ps(v.raw, _mm_set1_ps(value), i << 4); // SSE4.1
86 }
87
88 [[nodiscard]] static MOCHI_FORCE_INLINE Simd AsPoint(Simd a) {
89 // Replace the 3rd component with an integer that has the same bits as 1.0f.
90 auto araw = _mm_castps_si128(a.raw); // SSE2
91 auto v = _mm_insert_epi32(araw, 0x3f800000, 3); // SSE4.1
92 return _mm_castsi128_ps(v); // SSE2
93 }
94
95 [[nodiscard]] static MOCHI_FORCE_INLINE Simd AsDirection(Simd a) {
96 // Replace the 3rd component with an integer that has the same bits as 0.0f.
97 auto araw = _mm_castps_si128(a.raw); // SSE2
98 auto v = _mm_insert_epi32(araw, 0x00000000, 3); // SSE4.1
99 return _mm_castsi128_ps(v); // SSE2
100 }
101
102 template <int x, int y, int z, int w>
103 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Blend(Simd a, Simd b) {
104 static_assert(
105 x >= 0 && x < 2 && y >= 0 && y < 2 && z >= 0 && z < 2 && w >= 0 && w < 2,
106 "invalid blend index");
107 if constexpr (x == 0 && y == 0 && z == 0 && w == 0) {
108 return a;
109 } else if constexpr (x == 1 && y == 1 && z == 1 && w == 1) {
110 return b;
111 } else {
112 return _mm_blend_ps(a.raw, b.raw, x | (y << 1) | (z << 2) | (w << 3)); // SSE4.1
113 }
114 }
115
116 template <int N>
117 [[nodiscard]] static MOCHI_FORCE_INLINE bool AllTrue(Simd v) {
118 static_assert(N >= 1 && N <= kSize, "Unsupported N");
119 int mask = GetMSBitMask(v); // One bit for each byte in the vector
120 if constexpr (N == kSize) {
121 return mask == 0x0000FFFF;
122 } else {
123 int constexpr kNumBits = N * sizeof(Scalar);
124 auto constexpr kMustBeSet = (1UL << kNumBits) - 1;
125 return (mask & kMustBeSet) == kMustBeSet;
126 }
127 }
128
129 template <int N>
130 [[nodiscard]] static MOCHI_FORCE_INLINE bool AnyTrue(Simd v) {
131 static_assert(N >= 1 && N <= kSize, "Unsupported N");
132 int mask = GetMSBitMask(v); // One bit for each byte in the vector
133 if constexpr (N == kSize) {
134 return mask != 0;
135 } else {
136 int constexpr kNumBits = N * sizeof(Scalar);
137 auto constexpr kMayBeSet = (1UL << kNumBits) - 1;
138 return (mask & kMayBeSet) != 0;
139 }
140 }
141
142 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Broadcast(Scalar const* p) {
143 return _mm_broadcast_ss(p); // AVX
144 }
145
146 template <int i>
147 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Broadcast(Simd v) {
148 return Shuffle<i, i, i, i>(v);
149 }
150
151 template <int N = kSize>
152 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Load([[maybe_unused]] Scalar const* ptr) {
153 static_assert(N >= 0 && N <= 4);
154 if constexpr (N == 0) {
155 return Simd::Zero();
156 } else if constexpr (N == 1) {
157 return Simd{*ptr, 0.0f};
158 } else if constexpr (N == 2) {
159 __m128i mask = _mm_set_epi32(0, 0, -1, -1); // SSE2
160 return _mm_maskload_ps(ptr, mask); // AVX
161 } else if constexpr (N == 3) {
162 __m128i mask = _mm_set_epi32(0, -1, -1, -1); // SSE2
163 return _mm_maskload_ps(ptr, mask); // AVX
164 } else if constexpr (N == 4) {
165 return _mm_loadu_ps(ptr); // SSE
166 }
167 }
168
169 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Load(Scalar const* ptr, int n) {
170 MOCHI_ASSERT_VERBOSE(n >= 0 && n <= kSize, "Invalid size parameter");
171 return _mm_maskload_ps(ptr, x64_simd::kLoadMasksS4[n]); // AVX
172 }
173
174 [[nodiscard]] static Simd LoadIndexed(Scalar const* ptr, Simd<int, 4> const& indices) {
175 return _mm_i32gather_ps(ptr, indices.raw, sizeof(float)); // AVX2
176 }
177
178 template <int kTupleCount = kSize>
179 MOCHI_FORCE_INLINE static void
180 LoadTransposed(Scalar const* ptr, Simd& out0, Simd& out1, Simd& out2) {
181 static_assert(kTupleCount >= 1 && kTupleCount <= kSize, "Invalid kTupleCount");
182 constexpr int kCount0 = Clamp(kTupleCount * 3 - 0, 0, 4);
183 constexpr int kCount1 = Clamp(kTupleCount * 3 - 4, 0, 4);
184 constexpr int kCount2 = Clamp(kTupleCount * 3 - 8, 0, 4);
185 auto a = Simd::Load<kCount0>(ptr).raw; // [0,1,2,3]
186 auto b = Simd::Load<kCount1>(kCount1 == 0 ? ptr : ptr + 4).raw; // [4,5,6,7]
187 auto c = Simd::Load<kCount2>(kCount2 == 0 ? ptr : ptr + 8).raw; // [8,9,10,11]
188
189 auto t0 = _mm_blend_ps(a, b, 0b0100); // [0,_,6,3]
190 auto t1 = _mm_blend_ps(t0, c, 0b0010); // [0,9,6,3]
191 out0 = _mm_shuffle_ps(t1, t1, _MM_SHUFFLE(1, 2, 3, 0)); // [0,3,6,9]
192
193 t0 = _mm_blend_ps(a, b, 0b1001); // [4,1,_,7]
194 t1 = _mm_blend_ps(t0, c, 0b0100); // [4,1,10,7]
195 out1 = _mm_shuffle_ps(t1, t1, _MM_SHUFFLE(2, 3, 0, 1)); // [1,4,7,10]
196
197 t0 = _mm_blend_ps(a, b, 0b0010); // [_,5,2,_]
198 t1 = _mm_blend_ps(c, t0, 0b0110); // [8,5,2,11]
199 out2 = _mm_shuffle_ps(t1, t1, _MM_SHUFFLE(3, 0, 1, 2)); // [2,5,8,11]
200 }
201
202 template <int i>
203 [[nodiscard]] static MOCHI_FORCE_INLINE Simd SetBasisVector() {
204 static_assert(i >= 0 && i <= 3, "Invalid component index");
205 auto zero = _mm_setzero_si128(); // SSE2
206 auto v = _mm_insert_epi32(zero, 0x3f800000, i); // SSE4.1
207 return _mm_castsi128_ps(v); // SSE2
208 }
209
210 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Select(Simd mask, Simd a, Simd b) {
211 return _mm_blendv_ps(b.raw, a.raw, mask.raw); // SSE4.1
212 }
213
214 // return Simd{v[x], v[y], v[z], v[w]}
215 template <int x = 0, int y = 1, int z = 2, int w = 3>
216 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Shuffle(Simd v) {
217 static_assert(
218 x >= 0 && x < 4 && y >= 0 && y < 4 && z >= 0 && z < 4 && w >= 0 && w < 4, "Invalid index");
219 if constexpr (x == 0 && y == 1 && z == 2 && w == 3) {
220 return v;
221 } else {
222 return _mm_shuffle_ps(v.raw, v.raw, x | (y << 2) | (z << 4) | (w << 6)); // SSE
223 }
224 }
225 template <int x = 0, int y = 1, int z = 2, int w = 3>
226 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Shuffle(Simd a, Simd b) {
227 static_assert(
228 x >= 0 && x < 4 && y >= 0 && y < 4 && z >= 0 && z < 4 && w >= 0 && w < 4, "Invalid index");
229 return _mm_shuffle_ps(a.raw, b.raw, x | (y << 2) | (z << 4) | (w << 6)); // SSE
230 }
231
232 template <int N = kSize>
233 static MOCHI_FORCE_INLINE void Store([[maybe_unused]] Scalar* ptr, [[maybe_unused]] Simd v) {
234 static_assert(N >= 0 && N <= kSize);
235 if constexpr (N == 0) {
236 } else if constexpr (N < kSize) {
237 // About 3X faster than a masked store on AMD. About the same on Intel.
238 memcpy(ptr, &v, sizeof(Scalar) * N);
239 } else {
240 _mm_storeu_ps(ptr, v.raw); // SSE
241 }
242 }
243
244 static MOCHI_FORCE_INLINE void Store(Scalar* ptr, Simd v, int n) {
245 MOCHI_ASSERT_VERBOSE(n >= 0 && n <= kSize, "Invalid size parameter");
246 // Faster than masked store on AMD.
247 switch (n) { // clang-format off
248 case 1: Store<1>(ptr, v); break;
249 case 2: Store<2>(ptr, v); break;
250 case 3: Store<3>(ptr, v); break;
251 case 4: Store<4>(ptr, v); break;
252 MOCHI_UNLIKELY default: break;
253 } // clang-format on
254 }
255
256 MOCHI_FORCE_INLINE static int StoreSelected(Scalar* ptr, Simd condition, Simd values) {
257 auto mask = _mm_movemask_ps(condition.raw);
258 // Load the shuffle pattern from a lookup table.
259 auto const* tableRow =
260 reinterpret_cast<__m128i const*>(x64_simd::kStoreSelectedShuffleTableS4[mask]);
261 auto pattern = _mm_load_si128(tableRow);
262 auto packed = _mm_permutevar_ps(values.raw, pattern);
263 _mm_storeu_ps(ptr, packed);
264 return _mm_popcnt_u32(mask);
265 }
266
267 template <int kTupleCount = kSize>
268 MOCHI_FORCE_INLINE static void StoreTransposed(Scalar* ptr, Simd a, Simd b, Simd c) {
269 static_assert(kTupleCount >= 1 && kTupleCount <= kSize, "Invalid kTupleCount");
270 // a = [0,3,6,9], b = [1,4,7,10], c = [2,5,8,11]
271 auto d = _mm_shuffle_ps(a.raw, a.raw, _MM_SHUFFLE(1, 2, 3, 0)); // [0,9,6,3]
272 auto e = _mm_shuffle_ps(b.raw, b.raw, _MM_SHUFFLE(2, 3, 0, 1)); // [4,1,10,7]
273 auto f = _mm_shuffle_ps(c.raw, c.raw, _MM_SHUFFLE(3, 0, 1, 2)); // [8,5,2,11]
274 constexpr int kCount0 = Clamp(kTupleCount * 3 - 0, 0, 4);
275 constexpr int kCount1 = Clamp(kTupleCount * 3 - 4, 0, 4);
276 constexpr int kCount2 = Clamp(kTupleCount * 3 - 8, 0, 4);
277 Simd::Store<kCount0>(ptr, _mm_blend_ps(_mm_blend_ps(d, e, 0b0010), f, 0b0100)); // [0,1,2,3]
278 if constexpr (kCount1 > 0) {
279 Simd::Store<kCount1>(
280 ptr + 4, _mm_blend_ps(_mm_blend_ps(d, e, 0b1001), f, 0b0010)); // [4,5,6,7]
281 }
282 if constexpr (kCount2 > 0) {
283 Simd::Store<kCount2>(
284 ptr + 8, _mm_blend_ps(_mm_blend_ps(d, e, 0b0100), f, 0b1001)); // [8,9,10,11]
285 }
286 }
287
288 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Sqrt(Simd v) {
289 return _mm_sqrt_ps(v.raw); // SSE
290 }
291
292 [[nodiscard]] static MOCHI_FORCE_INLINE Simd RcpApprox(Simd v) {
293 return _mm_rcp_ps(v.raw); // SSE
294 }
295
296 [[nodiscard]] static MOCHI_FORCE_INLINE Simd RcpSqrtApprox(Simd v) {
297 return _mm_rsqrt_ps(v.raw); // SSE
298 }
299
300 // Broadcast the value -0.0. Use this in bitwise operations to affect just the sign bit.
301 [[nodiscard]] static MOCHI_FORCE_INLINE Simd SignBitMask() {
302 return _mm_castsi128_ps(_mm_set1_epi32(0x80000000)); // SSE2, SSE2
303 }
304
305 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Abs(Simd v) {
306 return _mm_andnot_ps(SignBitMask().raw, v.raw); // SSE
307 }
308
309 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Min(Simd a, Simd b) {
310 return _mm_min_ps(a.raw, b.raw); // SSE
311 }
312
313 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Max(Simd a, Simd b) {
314 return _mm_max_ps(a.raw, b.raw); // SSE
315 }
316
317 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Floor(Simd a) {
318 return _mm_floor_ps(a.raw); // SSE4.1
319 }
320
321 [[nodiscard]] static MOCHI_FORCE_INLINE Simd FastRound(Simd v) {
322 return _mm_round_ps(v.raw, _MM_FROUND_TO_NEAREST_INT); // SSE4.1
323 }
324
325#if MOCHI_ARCH_X64_SVML
326 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Cos(Simd a) {
327 return _mm_cos_ps(a.raw); // SSE
328 }
329
330 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Sin(Simd a) {
331 return _mm_sin_ps(a.raw); // SSE
332 }
333
334 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Tan(Simd a) {
335 return _mm_tan_ps(a.raw); // SSE
336 }
337
338 [[nodiscard]] static MOCHI_FORCE_INLINE Simd ACos(Simd a) {
339 return _mm_acos_ps(a.raw); // SSE
340 }
341
342 [[nodiscard]] static MOCHI_FORCE_INLINE Simd ASin(Simd a) {
343 return _mm_asin_ps(a.raw); // SSE
344 }
345
346 [[nodiscard]] static MOCHI_FORCE_INLINE Simd ATan(Simd a) {
347 return _mm_atan_ps(a.raw); // SSE
348 }
349
350 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Exp(Simd a) {
351 return _mm_exp_ps(a.raw); // SSE
352 }
353
354 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Ln(Simd a) {
355 return _mm_log_ps(a.raw); // SSE
356 }
357
358 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Tanh(Simd a) {
359 return _mm_tanh_ps(a.raw); // SSE
360 }
361#endif // MOCHI_ARCH_X64_SVML
362
363 [[nodiscard]] static MOCHI_FORCE_INLINE Simd MulAdd(Simd a, Simd b, Simd c) {
364#if MOCHI_ARCH_X64_FMA
365 return {_mm_fmadd_ps(a.raw, b.raw, c.raw)}; // FMA
366#else
367 return (a * b) + c;
368#endif
369 }
370
371 [[nodiscard]] static MOCHI_FORCE_INLINE Simd MulSub(Simd a, Simd b, Simd c) {
372#if MOCHI_ARCH_X64_FMA
373 return _mm_fmsub_ps(a.raw, b.raw, c.raw); // FMA
374#else
375 return (a * b) - c;
376#endif
377 }
378
379 [[nodiscard]] static MOCHI_FORCE_INLINE Simd NegMulAdd(Simd a, Simd b, Simd c) {
380#if MOCHI_ARCH_X64_FMA
381 return _mm_fnmadd_ps(a.raw, b.raw, c.raw); // FMA
382#else
383 return -(a * b) + c;
384#endif
385 }
386
387 [[nodiscard]] static MOCHI_FORCE_INLINE Simd NegMulSub(Simd a, Simd b, Simd c) {
388#if MOCHI_ARCH_X64_FMA
389 return _mm_fnmsub_ps(a.raw, b.raw, c.raw); // FMA
390#else
391 return -(a * b) - c;
392#endif
393 }
394
395 template <int N = 4>
396 [[nodiscard]] static MOCHI_FORCE_INLINE Scalar HMin(Simd a) {
397 static_assert(N >= 2 && N <= 4, "Unsupported N");
398 if constexpr (N == 2) {
399 return Get<0>(Min(a, Broadcast<1>(a)));
400 } else if constexpr (N == 3) {
401 return Get<0>(Min(Min(a, Broadcast<1>(a)), Broadcast<2>(a)));
402 } else {
403 auto tmp = Min(a, Shuffle<1, 2, 3, 0>(a));
404 return Get<0>(Min(tmp, Simd::Shuffle<2, 3, 0, 1>(tmp)));
405 }
406 }
407
408 template <int N = 4>
409 [[nodiscard]] static MOCHI_FORCE_INLINE Scalar HMax(Simd a) {
410 static_assert(N >= 2 && N <= 4, "Unsupported N");
411 if constexpr (N == 2) {
412 return Get<0>(Max(a, Broadcast<1>(a)));
413 } else if constexpr (N == 3) {
414 return Get<0>(Max(Max(a, Broadcast<1>(a)), Broadcast<2>(a)));
415 } else {
416 auto tmp = Max(a, Shuffle<1, 2, 3, 0>(a));
417 return Get<0>(Max(tmp, Shuffle<2, 3, 0, 1>(tmp)));
418 }
419 }
420
421 template <int N>
422 [[nodiscard]] static MOCHI_FORCE_INLINE Scalar HSum(Simd a) {
423 static_assert(N >= 2 && N <= 4, "Unsupported N");
424 // Terms are added in the same order as Simd<double, 4>::HSum<N> for consistency.
425 if constexpr (N == 2) {
426 return Get<0>(a) + Get<1>(a); // a[0] + a[1]
427 } else if constexpr (N == 3) {
428 return (Get<0>(a) + Get<2>(a)) + Get<1>(a); // (a[0] + a[2]) + a[1]
429 } else {
430 // PERF NOTE: Alternatively _mm_dp_ps could be used to compute the dot product with
431 // Simd{1.0f}. In comparison, this implementation takes 2 extra instructions, but it had ~50%
432 // higher throughput and ~45% lower latency, when benchmarked on an AMD CPU.
433 return (Get<0>(a) + Get<2>(a)) + (Get<1>(a) + Get<3>(a)); // (a[0] + a[2]) + (a[1] + a[3])
434 }
435 }
436
437 template <int N>
438 [[nodiscard]] static MOCHI_FORCE_INLINE Scalar HProd(Simd a) {
439 static_assert(N >= 2 && N <= 4, "Unsupported N");
440 alignas(alignof(Simd)) Scalar buf[4];
441 Store(buf, a);
442 if constexpr (N == 2) {
443 return buf[0] * buf[1];
444 } else if constexpr (N == 3) {
445 return buf[0] * buf[1] * buf[2];
446 } else if constexpr (N == 4) {
447 return buf[0] * buf[1] * buf[2] * buf[3];
448 }
449 }
450
451 template <int N>
452 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Dot(Simd a, Simd b) {
453 static_assert(N >= 2 && N <= 4, "Unsupported N");
454 if constexpr (N == 2) {
455 return _mm_dp_ps(a.raw, b.raw, 0x3F); // SSE4.1
456 } else if constexpr (N == 3) {
457 return _mm_dp_ps(a.raw, b.raw, 0x7F); // SSE4.1
458 } else if constexpr (N == 4) {
459#if MOCHI_COMPILER_CLANG
460 return _mm_dp_ps(a.raw, b.raw, -1); // SSE4.1
461#else
462 return _mm_dp_ps(a.raw, b.raw, 0xFF); // SSE4.1
463#endif
464 }
465 }
466
467 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator<(Simd rhs) const {
468 return _mm_cmplt_ps(this->raw, rhs.raw); // SSE
469 }
470
471 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator>(Simd rhs) const {
472 return _mm_cmpgt_ps(this->raw, rhs.raw); // SSE
473 }
474
475 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator<=(Simd rhs) const {
476 return _mm_cmple_ps(this->raw, rhs.raw); // SSE
477 }
478
479 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator>=(Simd rhs) const {
480 return _mm_cmpge_ps(this->raw, rhs.raw); // SSE
481 }
482
483 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Equal(Simd a, Simd b) {
484 return _mm_cmpeq_ps(a.raw, b.raw); // SSE
485 }
486
487 [[nodiscard]] static MOCHI_FORCE_INLINE Simd NotEqual(Simd a, Simd b) {
488 return _mm_cmpneq_ps(a.raw, b.raw); // SSE
489 }
490
491 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Zero() {
492 return _mm_setzero_ps(); // SSE
493 }
494
495 [[nodiscard]] MOCHI_FORCE_INLINE bool operator==(Simd rhs) const {
496 auto mask = GetMSBitMask(Equal(*this, rhs));
497 return mask == 0x0000FFFF; // All values equal
498 }
499
500 [[nodiscard]] MOCHI_FORCE_INLINE bool operator!=(Simd rhs) const {
501 auto mask = GetMSBitMask(NotEqual(*this, rhs));
502 return mask != 0; // All values equal
503 }
504
505 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator~() const {
506 // _mm_cmpeq_epi32 appears to be the fastest way to fill an SSE register with ones.
507 __m128i dummy{};
508 __m128 ones = _mm_castsi128_ps(_mm_cmpeq_epi32(dummy, dummy)); // SSE2, SSE2
509 return _mm_xor_ps(raw, ones); // SSE
510 }
511
512 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator-() const {
513 return _mm_xor_ps(raw, SignBitMask().raw); // SSE, SSE
514 }
515
516 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator+(Simd rhs) const {
517 return _mm_add_ps(raw, rhs.raw); // SSE
518 }
519
520 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator-(Simd rhs) const {
521 return _mm_sub_ps(raw, rhs.raw); // SSE
522 }
523
524 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator*(Simd rhs) const {
525 return _mm_mul_ps(raw, rhs.raw); // SSE
526 }
527
528 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator/(Simd rhs) const {
529 return _mm_div_ps(raw, rhs.raw); // SSE
530 }
531
532 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator&(Simd rhs) const {
533 return _mm_and_ps(raw, rhs.raw); // SSE
534 }
535
536 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator|(Simd rhs) const {
537 return _mm_or_ps(raw, rhs.raw); // SSE
538 }
539
540 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator^(Simd rhs) const {
541 return _mm_xor_ps(raw, rhs.raw); // SSE
542 }
543
544 private:
545 // Integer mask with the most significant bit of each byte in the vector
546 [[nodiscard]] static MOCHI_FORCE_INLINE int GetMSBitMask(Simd a) {
547 return _mm_movemask_epi8(_mm_castps_si128(a.raw)); // SSE2, SSE2
548 }
549};
550
551} // namespace superdex
552
553#endif // MOCHI_USE_SIMD && MOCHI_ARCH_X64_AVX2
Simd operator&(Simd rhs) const
bool operator==(Simd rhs) const
NativeType raw
Definition simd.h:174
Simd operator>(Simd rhs) const
Simd operator*(Simd rhs) const
Simd operator^(Simd rhs) const
Simd operator-() const
Simd operator>=(Simd rhs) const
Simd operator<(Simd rhs) const
bool operator!=(Simd rhs) const
Simd operator|(Simd rhs) const
static constexpr int kSize
Definition simd.h:96
Simd operator+(Simd rhs) const
Simd operator~() const
Simd operator/(Simd rhs) const
Simd operator<=(Simd rhs) const
#define MOCHI_ASSERT_VERBOSE(condition_without_side_effects,...)
Definition debug.h:102
#define MOCHI_UNLIKELY
#define MOCHI_FORCE_INLINE
T Dot(Simd< T, N > a, Simd< T, N > b)
Definition simd.h:666
constexpr T ACos(T a)
Simd< T, 2 > Shuffle(Simd< T, 2 > a)
Definition simd_inl.h:270
V LoadIndexed(typename V::Scalar const *ptr, Simd< I, V::kSize > indices)
Definition simd_inl.h:200
constexpr T const & Min(T const &a, T const &b)
constexpr auto Equal(T const &a, T const &b)
constexpr T Sin(T a)
T HSum(Simd< T, N > a)
Definition simd_inl.h:379
T HMin(Simd< T, N > a)
Definition simd_inl.h:391
bool AllTrue(T const &a)
Definition basic_utils.h:60
constexpr auto MulAdd(A a, B b, C c)
Simd< T, N > Tanh(Simd< T, N > a)
Definition simd_inl.h:688
Simd< T, N > Set(Simd< T, N > a, T value)
Definition simd_inl.h:315
constexpr auto NotEqual(T const &a, T const &b)
constexpr T Exp(T a)
constexpr T Cos(T a)
T HMax(Simd< T, N > a)
Definition simd_inl.h:397
V Broadcast(typename V::Scalar a)
Definition simd_inl.h:115
constexpr T Abs(T a)
Definition basic_utils.h:50
constexpr auto MulSub(A a, B b, C c)
Simd< T, N > Blend(Simd< T, N > a, Simd< T, N > b)
Definition simd_inl.h:285
constexpr T Tan(T a)
bool AnyTrue(T const &a)
Definition basic_utils.h:66
constexpr T Select(bool condition, T a, T b)
constexpr T Sqrt(T a)
constexpr T ATan(T a)
Simd< T, N > Ln(Simd< T, N > a)
Definition simd_inl.h:678
constexpr auto NegMulAdd(A a, B b, C c)
T Get(Simd< T, N > v)
Definition simd_inl.h:300
constexpr T Floor(T a)
constexpr T ASin(T a)
constexpr ValT Clamp(ValT value, MinT min, MaxT max)
T HProd(Simd< T, N > a)
Definition simd_inl.h:385
constexpr T const & Max(T const &a, T const &b)
void LoadTransposed(T const *ptr, Simd< T, N > &out0, Simd< T, N > &out1, Simd< T, N > &out2)
Definition simd_inl.h:207
Simd< T, N > FastRound(Simd< T, N > a)
Definition simd_inl.h:416
void StoreTransposed(T *ptr, Simd< T, N > a, Simd< T, N > b, Simd< T, N > c)
Definition simd_inl.h:245
void Store(T *ptr, Simd< T, N > a)
Definition simd_inl.h:213
Simd< T, N > RcpSqrtApprox(Simd< T, N > a)
Definition simd_inl.h:359
constexpr T RcpApprox(T a)
constexpr auto NegMulSub(A a, B b, C c)
int StoreSelected(T *ptr, Simd< MaskT, N > condition, Simd< T, N > values)
Definition simd_inl.h:225
V Load(typename V::Scalar const *ptr)
Definition simd_inl.h:184
#define MOCHI_NATIVE_SIMD_IMPL_BOILERPLATE(T, N, NativeT)