ReXGlue SDK
Loading...
Searching...
No Matches
vmx.h
Go to the documentation of this file.
1
10
11/*
12 * Parts of the SIMD patterns are based on XenonRecomp and UnleashedRecomp.
13 */
14
15#pragma once
16
17#include <array>
18#include <climits>
19#include <cmath>
20#include <cstring>
21
22#include <type_traits>
23
24#include <simde/x86/avx.h>
25#include <simde/x86/avx2.h>
26#include <simde/x86/sse.h>
27#include <simde/x86/sse4.1.h>
28
29#include <rex/ppc/context.h>
30#include <rex/types.h>
31
32namespace rex::ppc::simd {
33
39alignas(16) inline constexpr u8 kVectorMaskL[] = {
40 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
41 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
42 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
43 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03,
44 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04,
45 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05,
46 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06,
47 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07,
48 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
49 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09,
50 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A,
51 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B,
52 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C,
53 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D,
54 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E,
55 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F,
56};
57
58alignas(16) inline constexpr u8 kVectorMaskR[] = {
59 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
60 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
61 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
62 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
63 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
64 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
65 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
66 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
67 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
68 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
69 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
70 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
71 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
72 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF,
73 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF,
74 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF,
75};
76
77alignas(16) inline constexpr u8 kVectorShiftTableL[] = {
78 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
79 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
80 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
81 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03,
82 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04,
83 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05,
84 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06,
85 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07,
86 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
87 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09,
88 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A,
89 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B,
90 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C,
91 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D,
92 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E,
93 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F,
94};
95
96alignas(16) inline constexpr u8 kVectorShiftTableR[] = {
97 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
98 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F,
99 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E,
100 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D,
101 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C,
102 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B,
103 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A,
104 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09,
105 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
106 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07,
107 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06,
108 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05,
109 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04,
110 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03,
111 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
112 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
113};
114
115template <typename T>
116using VectorOf = std::conditional_t<std::is_floating_point_v<T>, simde__m128, simde__m128i>;
117
119template <typename T>
120simde__m128i AddSaturate(simde__m128i a, simde__m128i b);
121
123template <>
124inline simde__m128i AddSaturate<u32>(simde__m128i a, simde__m128i b) {
125 return simde_mm_add_epi32(
126 a, simde_mm_min_epu32(simde_mm_xor_si128(a, simde_mm_cmpeq_epi32(a, a)), b));
127}
128
130template <typename T>
131simde__m128i Average(simde__m128i a, simde__m128i b);
132
134template <>
135inline simde__m128i Average<i8>(simde__m128i a, simde__m128i b) {
136 simde__m128i c = simde_mm_set1_epi8(static_cast<char>(128));
137 return simde_mm_xor_si128(c,
138 simde_mm_avg_epu8(simde_mm_xor_si128(c, a), simde_mm_xor_si128(c, b)));
139}
140
142template <>
143inline simde__m128i Average<i16>(simde__m128i a, simde__m128i b) {
144 simde__m128i c = simde_mm_set1_epi16(static_cast<short>(32768));
145 return simde_mm_xor_si128(c,
146 simde_mm_avg_epu16(simde_mm_xor_si128(c, a), simde_mm_xor_si128(c, b)));
147}
148
150template <>
151inline simde__m128i Average<i32>(simde__m128i a, simde__m128i b) {
152 simde__m128i sum = simde_mm_add_epi32(simde_mm_srai_epi32(a, 1), simde_mm_srai_epi32(b, 1));
153 return simde_mm_add_epi32(sum,
154 simde_mm_and_si128(simde_mm_or_si128(a, b), simde_mm_set1_epi32(1)));
155}
156
158template <typename To, typename From>
160
162template <>
163inline simde__m128 Convert<f32, u32>(simde__m128i src1) {
164 simde__m128i xmm1 = simde_mm_add_epi32(src1, simde_mm_set1_epi32(127));
165 simde__m128i xmm0 = simde_mm_slli_epi32(src1, 31 - 8);
166 xmm0 = simde_mm_srli_epi32(xmm0, 31);
167 xmm0 = simde_mm_add_epi32(xmm0, xmm1);
168 xmm0 = simde_mm_srai_epi32(xmm0, 8);
169 xmm0 = simde_mm_add_epi32(xmm0, simde_mm_set1_epi32(0x4F800000));
170 simde__m128 xmm2 = simde_mm_cvtepi32_ps(src1);
171 return simde_mm_blendv_ps(xmm2, simde_mm_castsi128_ps(xmm0), simde_mm_castsi128_ps(src1));
172}
173
174inline simde__m128i PermuteBytes(simde__m128i a, simde__m128i b, simde__m128i c) {
175 simde__m128i d = simde_mm_set1_epi8(0xF);
176 simde__m128i e = simde_mm_sub_epi8(d, simde_mm_and_si128(c, d));
177 return simde_mm_blendv_epi8(simde_mm_shuffle_epi8(a, e), simde_mm_shuffle_epi8(b, e),
178 simde_mm_slli_epi32(c, 3));
179}
180
186template <typename T>
187simde__m128i CompareGreater(simde__m128i a, simde__m128i b);
188
194template <>
195inline simde__m128i CompareGreater<u8>(simde__m128i a, simde__m128i b) {
196 simde__m128i c = simde_mm_set1_epi8(static_cast<char>(128));
197 return simde_mm_cmpgt_epi8(simde_mm_xor_si128(a, c), simde_mm_xor_si128(b, c));
198}
199
205template <>
206inline simde__m128i CompareGreater<u16>(simde__m128i a, simde__m128i b) {
207 simde__m128i c = simde_mm_set1_epi16(static_cast<short>(32768));
208 return simde_mm_cmpgt_epi16(simde_mm_xor_si128(a, c), simde_mm_xor_si128(b, c));
209}
210
212template <typename To, typename From>
214
216template <>
217inline simde__m128i ConvertSaturate<i32, f32>(simde__m128 src1) {
218 simde__m128 xmm2 = simde_mm_cmpunord_ps(src1, src1);
219 simde__m128i xmm0 = simde_mm_cvttps_epi32(src1);
220 simde__m128i xmm1 = simde_mm_cmpeq_epi32(xmm0, simde_mm_set1_epi32(INT_MIN));
221 xmm1 = simde_mm_andnot_si128(simde_mm_castps_si128(src1), xmm1);
222 simde__m128 dest = simde_mm_blendv_ps(simde_mm_castsi128_ps(xmm0),
223 simde_mm_castsi128_ps(simde_mm_set1_epi32(INT_MAX)),
224 simde_mm_castsi128_ps(xmm1));
225 return simde_mm_andnot_si128(simde_mm_castps_si128(xmm2), simde_mm_castps_si128(dest));
226}
227
235template <>
236inline simde__m128i ConvertSaturate<u32, f32>(simde__m128 src1) {
237 simde__m128 nan_mask = simde_mm_cmpunord_ps(src1, src1);
238 simde__m128 neg_mask = simde_mm_cmplt_ps(src1, simde_mm_setzero_ps());
239 simde__m128 max_val = simde_mm_set1_ps(4294967295.0f);
240 simde__m128 overflow_mask = simde_mm_cmpge_ps(src1, max_val);
241
242 simde__m128 clamped = simde_mm_max_ps(src1, simde_mm_setzero_ps());
243 clamped = simde_mm_min_ps(clamped, max_val);
244
245 simde__m128 half_range = simde_mm_set1_ps(2147483648.0f);
246 simde__m128 high_bit_mask = simde_mm_cmpge_ps(clamped, half_range);
247
248 simde__m128 adjusted = simde_mm_sub_ps(clamped, simde_mm_and_ps(high_bit_mask, half_range));
249 simde__m128i low_bits = simde_mm_cvttps_epi32(adjusted);
250 simde__m128i high_bit = simde_mm_and_si128(simde_mm_castps_si128(high_bit_mask),
251 simde_mm_set1_epi32(static_cast<int>(0x80000000u)));
252 simde__m128i result = simde_mm_or_si128(low_bits, high_bit);
253
254 result = simde_mm_andnot_si128(simde_mm_castps_si128(nan_mask), result);
255 result = simde_mm_andnot_si128(simde_mm_castps_si128(neg_mask), result);
256 result = simde_mm_or_si128(
257 simde_mm_andnot_si128(simde_mm_castps_si128(overflow_mask), result),
258 simde_mm_and_si128(simde_mm_castps_si128(overflow_mask), simde_mm_set1_epi32(-1)));
259
260 return result;
261}
262
268inline simde__m128i ShiftRightBits(simde__m128i a, simde__m128i b) {
269 b = simde_mm_srli_epi64(simde_mm_slli_epi64(b, 61), 61);
270 return simde_mm_castps_si128(simde_mm_insert_ps(
271 simde_mm_castsi128_ps(simde_mm_srl_epi64(a, b)),
272 simde_mm_castsi128_ps(simde_mm_srl_epi64(simde_mm_srli_si128(a, 4), b)), 0x10));
273}
274
280inline simde__m128i ShiftLeftBits(simde__m128i a, simde__m128i b) {
281 int shift = simde_mm_extract_epi8(b, 15) & 0x7;
282 if (shift == 0)
283 return a;
284
285 simde__m128i low_shifted = simde_mm_sll_epi64(a, simde_mm_cvtsi32_si128(shift));
286 simde__m128i high_carry = simde_mm_srl_epi64(a, simde_mm_cvtsi32_si128(64 - shift));
287 high_carry = simde_mm_slli_si128(high_carry, 8);
288 return simde_mm_or_si128(low_shifted, high_carry);
289}
290
296inline simde__m128i ShiftLeftBytes(simde__m128i a, simde__m128i b) {
297 int shift_bytes = (simde_mm_extract_epi8(b, 0) >> 3) & 0xF;
298 if (shift_bytes == 0)
299 return a;
300 if (shift_bytes >= 16)
301 return simde_mm_setzero_si128();
302
303 alignas(16) u8 src[16], dst[16];
304 simde_mm_store_si128(reinterpret_cast<simde__m128i*>(src), a);
305 memset(dst, 0, sizeof(dst));
306 memcpy(dst + shift_bytes, src, 16 - shift_bytes);
307 return simde_mm_load_si128(reinterpret_cast<const simde__m128i*>(dst));
308}
309
315inline simde__m128i ShiftRightBytes(simde__m128i a, simde__m128i b) {
316 int shift_bytes = (simde_mm_extract_epi8(b, 0) >> 3) & 0xF;
317 if (shift_bytes == 0)
318 return a;
319 if (shift_bytes >= 16)
320 return simde_mm_setzero_si128();
321
322 alignas(16) u8 src[16], dst[16];
323 simde_mm_store_si128(reinterpret_cast<simde__m128i*>(src), a);
324 memset(dst, 0, sizeof(dst));
325 memcpy(dst, src + shift_bytes, 16 - shift_bytes);
326 return simde_mm_load_si128(reinterpret_cast<const simde__m128i*>(dst));
327}
328
330template <typename T>
331simde__m128i ShiftLeftLanes(simde__m128i a, simde__m128i count);
332
334template <>
335inline simde__m128i ShiftLeftLanes<u16>(simde__m128i a, simde__m128i count) {
336 simde__m128i zero = simde_mm_setzero_si128();
337 simde__m128i a_lo = simde_mm_unpacklo_epi16(a, zero);
338 simde__m128i a_hi = simde_mm_unpackhi_epi16(a, zero);
339 simde__m128i s_lo = simde_mm_unpacklo_epi16(count, zero);
340 simde__m128i s_hi = simde_mm_unpackhi_epi16(count, zero);
341 simde__m128i r_lo = simde_mm_sllv_epi32(a_lo, s_lo);
342 simde__m128i r_hi = simde_mm_sllv_epi32(a_hi, s_hi);
343 simde__m128i mask16 = simde_mm_set1_epi32(0xFFFF);
344 r_lo = simde_mm_and_si128(r_lo, mask16);
345 r_hi = simde_mm_and_si128(r_hi, mask16);
346 return simde_mm_packus_epi32(r_lo, r_hi);
347}
348
352template <typename T>
353simde__m128i ShiftRightLanes(simde__m128i a, simde__m128i count);
354
356template <>
357inline simde__m128i ShiftRightLanes<u16>(simde__m128i a, simde__m128i count) {
358 simde__m128i zero = simde_mm_setzero_si128();
359 simde__m128i a_lo = simde_mm_unpacklo_epi16(a, zero);
360 simde__m128i a_hi = simde_mm_unpackhi_epi16(a, zero);
361 simde__m128i s_lo = simde_mm_unpacklo_epi16(count, zero);
362 simde__m128i s_hi = simde_mm_unpackhi_epi16(count, zero);
363 simde__m128i r_lo = simde_mm_srlv_epi32(a_lo, s_lo);
364 simde__m128i r_hi = simde_mm_srlv_epi32(a_hi, s_hi);
365 return simde_mm_packus_epi32(r_lo, r_hi);
366}
367
369template <>
370inline simde__m128i ShiftRightLanes<i16>(simde__m128i a, simde__m128i count) {
371 simde__m128i zero = simde_mm_setzero_si128();
372 simde__m128i a_lo = simde_mm_srai_epi32(simde_mm_unpacklo_epi16(a, a), 16);
373 simde__m128i a_hi = simde_mm_srai_epi32(simde_mm_unpackhi_epi16(a, a), 16);
374 simde__m128i s_lo = simde_mm_unpacklo_epi16(count, zero);
375 simde__m128i s_hi = simde_mm_unpackhi_epi16(count, zero);
376 simde__m128i r_lo = simde_mm_srav_epi32(a_lo, s_lo);
377 simde__m128i r_hi = simde_mm_srav_epi32(a_hi, s_hi);
378 return simde_mm_packs_epi32(r_lo, r_hi);
379}
380
382template <>
383inline simde__m128i ShiftLeftLanes<u8>(simde__m128i a, simde__m128i count) {
384 simde__m128i zero = simde_mm_setzero_si128();
385 simde__m128i a_lo = simde_mm_unpacklo_epi8(a, zero);
386 simde__m128i a_hi = simde_mm_unpackhi_epi8(a, zero);
387 simde__m128i s_lo = simde_mm_unpacklo_epi8(count, zero);
388 simde__m128i s_hi = simde_mm_unpackhi_epi8(count, zero);
389 simde__m128i r_lo = ShiftLeftLanes<u16>(a_lo, s_lo);
390 simde__m128i r_hi = ShiftLeftLanes<u16>(a_hi, s_hi);
391 simde__m128i mask8 = simde_mm_set1_epi16(0xFF);
392 r_lo = simde_mm_and_si128(r_lo, mask8);
393 r_hi = simde_mm_and_si128(r_hi, mask8);
394 return simde_mm_packus_epi16(r_lo, r_hi);
395}
396
398inline simde__m128i ToGuestByteOrder(simde__m128i v) {
399 return simde_mm_shuffle_epi8(
400 v, simde_mm_load_si128(reinterpret_cast<const simde__m128i*>(kVectorMaskL)));
401}
402
409inline void StoreVectorBytes(u8* host, const u8* bytes, u32 count) {
410 if (count == 16) {
411 simde_mm_storeu_si128(reinterpret_cast<simde__m128i*>(host),
412 simde_mm_loadu_si128(reinterpret_cast<const simde__m128i*>(bytes)));
413 } else if (count >= 8) {
414 std::memcpy(host, bytes, 8);
415 std::memcpy(host + count - 8, bytes + count - 8, 8);
416 } else if (count >= 4) {
417 std::memcpy(host, bytes, 4);
418 std::memcpy(host + count - 4, bytes + count - 4, 4);
419 } else if (count >= 2) {
420 std::memcpy(host, bytes, 2);
421 std::memcpy(host + count - 2, bytes + count - 2, 2);
422 } else if (count == 1) {
423 host[0] = bytes[0];
424 }
425}
426
432inline void StoreVectorLeft(u8* host, u32 ea, simde__m128i v) {
433 alignas(16) u8 bytes[16];
434 simde_mm_store_si128(reinterpret_cast<simde__m128i*>(bytes), ToGuestByteOrder(v));
435 StoreVectorBytes(host, bytes, 16 - (ea & 0xF));
436}
437
444inline void StoreVectorRight(u8* host, u32 ea, simde__m128i v) {
445 alignas(16) u8 bytes[16];
446 simde_mm_store_si128(reinterpret_cast<simde__m128i*>(bytes), ToGuestByteOrder(v));
447 const u32 count = ea & 0xF;
448 StoreVectorBytes(host, bytes + 16 - count, count);
449}
450
459inline void StoreVectorPair(u8* left_host, u32 left_ea, u8* right_host, u32 right_ea,
460 simde__m128i v) {
461 if (right_ea == left_ea + 16 && right_host == left_host + (16 - (left_ea & 0xF))) {
462 simde_mm_storeu_si128(reinterpret_cast<simde__m128i*>(left_host), ToGuestByteOrder(v));
463 return;
464 }
465 StoreVectorLeft(left_host, left_ea, v);
466 StoreVectorRight(right_host, right_ea, v);
467}
468
470
471} // namespace rex::ppc::simd
472
479
480namespace rex::ppc::simd {
481
489template <bool Signed>
490[[nodiscard]] inline bool IsConvertSaturating(simde__m128 scaled) noexcept {
491 const auto above = simde_mm_cmpge_ps(scaled, simde_mm_set1_ps(Signed ? 0x1p31f : 0x1p32f));
492 const auto below = Signed ? simde_mm_cmplt_ps(scaled, simde_mm_set1_ps(-0x1p31f))
493 : simde_mm_cmple_ps(scaled, simde_mm_set1_ps(-1.0f));
494 return simde_mm_movemask_ps(simde_mm_or_ps(above, below)) != 0;
495}
496
497namespace detail {
498
506inline bool AnyLaneDiffers(simde__m128i wrapped, simde__m128i saturated) {
507 return simde_mm_movemask_epi8(simde_mm_cmpeq_epi8(wrapped, saturated)) != 0xFFFF;
508}
509
510} // namespace detail
511
519template <u32 Bits, bool Signed>
520[[nodiscard]] inline bool IsAddSaturating(simde__m128i a, simde__m128i b) noexcept {
521 if constexpr (Bits == 8) {
522 return detail::AnyLaneDiffers(simde_mm_add_epi8(a, b),
523 Signed ? simde_mm_adds_epi8(a, b) : simde_mm_adds_epu8(a, b));
524 } else if constexpr (Bits == 16) {
525 return detail::AnyLaneDiffers(simde_mm_add_epi16(a, b),
526 Signed ? simde_mm_adds_epi16(a, b) : simde_mm_adds_epu16(a, b));
527 } else if constexpr (Signed) {
528 const auto sum = simde_mm_add_epi32(a, b);
529 const auto overflow =
530 simde_mm_and_si128(simde_mm_xor_si128(a, sum), simde_mm_xor_si128(b, sum));
531 return simde_mm_movemask_ps(simde_mm_castsi128_ps(overflow)) != 0;
532 } else {
533 const auto sum = simde_mm_add_epi32(a, b);
534 return simde_mm_movemask_epi8(simde_mm_cmpeq_epi32(simde_mm_max_epu32(a, sum), sum)) != 0xFFFF;
535 }
536}
537
547template <u32 Bits, bool Signed>
548[[nodiscard]] inline bool IsSubSaturating(simde__m128i a, simde__m128i b) noexcept {
549 if constexpr (Bits == 8) {
550 return detail::AnyLaneDiffers(simde_mm_sub_epi8(a, b),
551 Signed ? simde_mm_subs_epi8(a, b) : simde_mm_subs_epu8(a, b));
552 } else if constexpr (Bits == 16) {
553 return detail::AnyLaneDiffers(simde_mm_sub_epi16(a, b),
554 Signed ? simde_mm_subs_epi16(a, b) : simde_mm_subs_epu16(a, b));
555 } else if constexpr (Signed) {
556 const auto difference = simde_mm_sub_epi32(a, b);
557 const auto overflow =
558 simde_mm_and_si128(simde_mm_xor_si128(a, b), simde_mm_xor_si128(a, difference));
559 return simde_mm_movemask_ps(simde_mm_castsi128_ps(overflow)) != 0;
560 } else {
561 return simde_mm_movemask_epi8(simde_mm_cmpeq_epi32(simde_mm_max_epu32(a, b), a)) != 0xFFFF;
562 }
563}
564
572template <u32 SourceBits, bool SignedDest>
573[[nodiscard]] inline bool IsPackSaturating(simde__m128i a, simde__m128i b) noexcept {
574 constexpr u32 kDestBits = SourceBits / 2;
575 constexpr u32 kRange = (1u << kDestBits) - 1;
576 constexpr u32 kLow = SignedDest ? (0u - (1u << (kDestBits - 1))) : 0u;
577 if constexpr (SourceBits == 16) {
578 const auto low = simde_mm_set1_epi16(static_cast<i16>(kLow));
579 const auto range = simde_mm_set1_epi16(static_cast<i16>(kRange));
580 const auto outside = simde_mm_or_si128(simde_mm_subs_epu16(simde_mm_sub_epi16(a, low), range),
581 simde_mm_subs_epu16(simde_mm_sub_epi16(b, low), range));
582 return simde_mm_movemask_epi8(simde_mm_cmpeq_epi8(outside, simde_mm_setzero_si128())) != 0xFFFF;
583 } else {
584 const auto low = simde_mm_set1_epi32(static_cast<i32>(kLow));
585 const auto range = simde_mm_set1_epi32(static_cast<i32>(kRange));
586 const auto first = simde_mm_sub_epi32(a, low);
587 const auto second = simde_mm_sub_epi32(b, low);
588 const auto inside =
589 simde_mm_and_si128(simde_mm_cmpeq_epi32(simde_mm_max_epu32(first, range), range),
590 simde_mm_cmpeq_epi32(simde_mm_max_epu32(second, range), range));
591 return simde_mm_movemask_epi8(inside) != 0xFFFF;
592 }
593}
594
595namespace detail {
596
598struct PackRange {
599 bool checked;
600 f64 low;
601 f64 high;
602};
603
604inline constexpr PackRange kUnchecked{false, 0.0, 0.0};
605inline constexpr PackRange kByteRange{true, 0.0, 255.0};
606inline constexpr PackRange kShortRange{true, -32767.0, 32767.0};
607inline constexpr PackRange kSigned10Range{true, -511.0, 511.0};
608inline constexpr PackRange kSigned20Range{true, -524287.0, 524287.0};
609inline constexpr PackRange kTwoBitRange{true, 0.0, 3.0};
610inline constexpr PackRange kFourBitRange{true, 0.0, 15.0};
611
613inline constexpr std::array<std::array<PackRange, 4>, 7> kPackD3DRanges = {{
614 {kByteRange, kByteRange, kByteRange, kByteRange},
615 {kShortRange, kShortRange, kUnchecked, kUnchecked},
616 {kSigned10Range, kSigned10Range, kSigned10Range, kTwoBitRange},
617 {kUnchecked, kUnchecked, kUnchecked, kUnchecked},
618 {kShortRange, kShortRange, kShortRange, kShortRange},
619 {kUnchecked, kUnchecked, kUnchecked, kUnchecked},
620 {kSigned20Range, kSigned20Range, kSigned20Range, kFourBitRange},
621}};
622
624template <u32 Type, bool High>
625inline constexpr std::array<f32, 4> kPackD3DBounds = [] {
626 std::array<f32, 4> bounds{};
627 for (u32 lane = 0; lane < 4; ++lane) {
628 const PackRange& range = kPackD3DRanges[Type][3 - lane];
629 bounds[lane] =
630 range.checked ? static_cast<f32>(3.0 + (High ? range.high : range.low) / 4194304.0) : 0.0f;
631 }
632 return bounds;
633}();
634
636template <u32 Type>
637inline constexpr std::array<u32, 4> kPackD3DChecked = [] {
638 std::array<u32, 4> mask{};
639 for (u32 lane = 0; lane < 4; ++lane) {
640 mask[lane] = kPackD3DRanges[Type][3 - lane].checked ? 0xFFFFFFFFu : 0u;
641 }
642 return mask;
643}();
644
645} // namespace detail
646
654template <u32 Type>
655[[nodiscard]] inline bool IsPackD3DSaturating(simde__m128 source) noexcept {
656 const auto low = simde_mm_loadu_ps(detail::kPackD3DBounds<Type, false>.data());
657 const auto high = simde_mm_loadu_ps(detail::kPackD3DBounds<Type, true>.data());
658 const auto checked =
659 simde_mm_loadu_ps(reinterpret_cast<const f32*>(detail::kPackD3DChecked<Type>.data()));
660 const auto inside =
661 simde_mm_and_ps(simde_mm_cmpge_ps(source, low), simde_mm_cmple_ps(source, high));
662 const auto outside = simde_mm_andnot_ps(inside, simde_mm_cmpord_ps(source, source));
663 return simde_mm_movemask_ps(simde_mm_and_ps(outside, checked)) != 0;
664}
665
666} // namespace rex::ppc::simd
667
668namespace rex::ppc::simd {
669
670namespace detail {
671
673struct PackField {
674 u8 shift;
675 u8 width;
676 u32 nan_field;
677};
678
680struct PackLayout {
681 std::array<PackField, 4> lanes;
682 u32 count;
683 bool half;
684};
685
686inline constexpr std::array<PackLayout, 7> kPackLayouts = {{
687 {{{{16, 8, 0}, {8, 8, 0}, {0, 8, 0}, {24, 8, 0}}}, 4, false},
688 {{{{16, 16, 0x8000}, {0, 16, 0x8000}, {}, {}}}, 2, false},
689 {{{{0, 10, 0x200}, {10, 10, 0x200}, {20, 10, 0x200}, {30, 2, 0}}}, 4, false},
690 {{{{16, 16, 0}, {0, 16, 0}, {}, {}}}, 2, true},
691 {{{{48, 16, 0x8000}, {32, 16, 0x8000}, {16, 16, 0x8000}, {0, 16, 0x8000}}}, 4, false},
692 {{{{48, 16, 0}, {32, 16, 0}, {16, 16, 0}, {0, 16, 0}}}, 4, true},
693 {{{{0, 20, 0x80000}, {20, 20, 0x80000}, {40, 20, 0x80000}, {60, 4, 0}}}, 4, false},
694}};
695
704inline u64 PackInteger(u32 bits, const PackRange& range, const PackField& lane, u8& saturated) {
705 const u64 mask = (u64{1} << lane.width) - 1;
706 if ((bits & 0x7FFFFFFFu) > 0x7F800000u) {
707 return lane.nan_field;
708 }
709 i64 k = (bits >> 31) != 0 ? -0x100000000LL : static_cast<i64>(bits) - 0x40400000LL;
710 if (range.checked) {
711 const i64 low = static_cast<i64>(range.low);
712 const i64 high = static_cast<i64>(range.high);
713 if (k < low || k > high) {
714 saturated = 1;
715 k = k < low ? low : high;
716 }
717 }
718 return static_cast<u64>(k) & mask;
719}
720
728inline u64 PackHalf(u32 bits) {
729 const u32 sign = (bits >> 16) & 0x8000u;
730 const u32 magnitude = bits & 0x7FFFFFFFu;
731 if (magnitude > 0x477FE000u) {
732 return sign | 0x7FFFu;
733 }
734 const u32 exponent = magnitude >> 23;
735 if (exponent <= 0x70u) {
736 return sign;
737 }
738 return sign | (((exponent - 0x70u) << 10) + ((magnitude & 0x7FE000u) >> 13));
739}
740
748template <u32 Type>
749inline u64 PackElements(const VRegister& source, u8& saturated) {
750 if constexpr (Type == 0) {
751 const auto bits = simde_mm_load_si128(reinterpret_cast<const simde__m128i*>(source.u32));
752 const auto offset = simde_mm_sub_epi32(bits, simde_mm_set1_epi32(0x40400000));
753 const auto negative = simde_mm_srai_epi32(bits, 31);
754 const auto below =
755 simde_mm_or_si128(negative, simde_mm_cmpgt_epi32(simde_mm_setzero_si128(), offset));
756 const auto above =
757 simde_mm_andnot_si128(below, simde_mm_cmpgt_epi32(offset, simde_mm_set1_epi32(255)));
758 const auto not_a_number = simde_mm_cmpgt_epi32(
759 simde_mm_and_si128(bits, simde_mm_set1_epi32(0x7FFFFFFF)), simde_mm_set1_epi32(0x7F800000));
760 const auto outside = simde_mm_andnot_si128(not_a_number, simde_mm_or_si128(below, above));
761 saturated = simde_mm_movemask_epi8(outside) != 0 ? 1 : 0;
762 const auto clamped =
763 simde_mm_andnot_si128(simde_mm_or_si128(below, not_a_number),
764 simde_mm_or_si128(simde_mm_and_si128(above, simde_mm_set1_epi32(255)),
765 simde_mm_andnot_si128(above, offset)));
766 const auto bytes = simde_mm_shuffle_epi8(
767 clamped, simde_mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 12, 8, 4));
768 return static_cast<u32>(simde_mm_cvtsi128_si32(bytes));
769 } else {
770 constexpr const detail::PackLayout& kLayout = detail::kPackLayouts[Type];
771 u64 packed = 0;
772 for (u32 i = 0; i < kLayout.count; ++i) {
773 const u32 bits = source.u32[3 - i];
774 const detail::PackField& lane = kLayout.lanes[i];
775 const u64 field = kLayout.half ? detail::PackHalf(bits)
776 : detail::PackInteger(bits, detail::kPackD3DRanges[Type][i],
777 lane, saturated);
778 packed |= field << lane.shift;
779 }
780 return packed;
781 }
782}
783
784} // namespace detail
785
795template <u32 Type, u32 Mask, u32 Shift>
796inline u8 PackD3D(VRegister& dest, const VRegister& source) {
797 u8 saturated = 0;
798 const u64 packed = detail::PackElements<Type>(source, saturated);
799 const u32 lo = static_cast<u32>(packed);
800 const u32 hi = static_cast<u32>(packed >> 32);
801 if constexpr (Mask == 3 && Shift == 3) {
802 dest.u32[0] = hi;
803 } else {
804 dest.u32[Shift] = lo;
805 if constexpr (Mask >= 2 && Shift < 3) {
806 dest.u32[Shift + 1] = hi;
807 }
808 }
809 return saturated;
810}
811
812} // namespace rex::ppc::simd
Definition fpu.h:71
void StoreVectorRight(u8 *host, u32 ea, simde__m128i v)
Definition vmx.h:444
simde__m128i AddSaturate(simde__m128i a, simde__m128i b)
u8 PackD3D(VRegister &dest, const VRegister &source)
Definition vmx.h:796
bool IsPackD3DSaturating(simde__m128 source) noexcept
Definition vmx.h:655
simde__m128i PermuteBytes(simde__m128i a, simde__m128i b, simde__m128i c)
Definition vmx.h:174
simde__m128i Average(simde__m128i a, simde__m128i b)
constexpr u8 kVectorMaskL[]
Definition vmx.h:39
simde__m128i ToGuestByteOrder(simde__m128i v)
Definition vmx.h:398
simde__m128i CompareGreater(simde__m128i a, simde__m128i b)
constexpr u8 kVectorShiftTableL[]
Definition vmx.h:77
simde__m128i ShiftLeftBits(simde__m128i a, simde__m128i b)
Definition vmx.h:280
simde__m128i ShiftLeftBytes(simde__m128i a, simde__m128i b)
Definition vmx.h:296
simde__m128i ShiftLeftLanes(simde__m128i a, simde__m128i count)
bool IsSubSaturating(simde__m128i a, simde__m128i b) noexcept
Definition vmx.h:548
void StoreVectorPair(u8 *left_host, u32 left_ea, u8 *right_host, u32 right_ea, simde__m128i v)
Definition vmx.h:459
VectorOf< To > Convert(VectorOf< From > src)
simde__m128i ShiftRightLanes(simde__m128i a, simde__m128i count)
constexpr u8 kVectorShiftTableR[]
Definition vmx.h:96
constexpr u8 kVectorMaskR[]
Definition vmx.h:58
bool IsAddSaturating(simde__m128i a, simde__m128i b) noexcept
Definition vmx.h:520
void StoreVectorBytes(u8 *host, const u8 *bytes, u32 count)
Definition vmx.h:409
bool IsPackSaturating(simde__m128i a, simde__m128i b) noexcept
Definition vmx.h:573
void StoreVectorLeft(u8 *host, u32 ea, simde__m128i v)
Definition vmx.h:432
VectorOf< To > ConvertSaturate(VectorOf< From > src)
simde__m128i ShiftRightBits(simde__m128i a, simde__m128i b)
Definition vmx.h:268
bool IsConvertSaturating(simde__m128 scaled) noexcept
Definition vmx.h:490
std::conditional_t< std::is_floating_point_v< T >, simde__m128, simde__m128i > VectorOf
Definition vmx.h:116
simde__m128i ShiftRightBytes(simde__m128i a, simde__m128i b)
Definition vmx.h:315
Integer types, byte order wrappers and size literals.
Definition context.h:219