blob: 8f74ab9008b7de625e80fa341e3a8ac258dbbe4c [file] [log] [blame]
Mike Kleinec370972020-03-05 10:15:35 -06001// Copyright 2020 Google LLC.
Mike Kleina67d1ae2020-03-09 17:36:00 -05002// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
Mike Kleinec370972020-03-05 10:15:35 -06003
4#ifndef SkVM_opts_DEFINED
5#define SkVM_opts_DEFINED
6
7#include "include/private/SkVx.h"
8#include "src/core/SkVM.h"
9
Mike Klein2e69a132020-09-18 08:02:46 -050010template <int N>
11static inline skvx::Vec<N,int> gather32(const int* ptr, const skvx::Vec<N,int>& ix) {
12#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_AVX2
13 if constexpr (N == 8) {
14 return skvx::bit_pun<skvx::Vec<N,int>>(
15 _mm256_i32gather_epi32(ptr, skvx::bit_pun<__m256i>(ix), 4));
16 }
17#endif
18 // Try to recurse on specializations, falling back on standard scalar map()-based impl.
19 if constexpr (N > 8) {
20 return join(gather32(ptr, ix.lo),
21 gather32(ptr, ix.hi));
22 }
Mike Klein840e8ea2020-10-12 12:38:10 -050023 return map([&](int i) { return ptr[i]; }, ix);
Mike Klein2e69a132020-09-18 08:02:46 -050024}
25
Mike Kleinec370972020-03-05 10:15:35 -060026namespace SK_OPTS_NS {
27
John Stiles9a6f3992021-08-16 11:37:20 -040028namespace SkVMInterpreterTypes {
29#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_AVX2
30 constexpr inline int K = 32; // 1024-bit: 4 ymm or 2 zmm at a time
31#else
32 constexpr inline int K = 8; // 256-bit: 2 xmm, 2 v-registers, etc.
33#endif
34 using I32 = skvx::Vec<K, int>;
35 using I16 = skvx::Vec<K, int16_t>;
36 using F32 = skvx::Vec<K, float>;
37 using U64 = skvx::Vec<K, uint64_t>;
38 using U32 = skvx::Vec<K, uint32_t>;
39 using U16 = skvx::Vec<K, uint16_t>;
40 using U8 = skvx::Vec<K, uint8_t>;
41 union Slot {
42 F32 f32;
43 I32 i32;
44 U32 u32;
45 I16 i16;
46 U16 u16;
47 };
48} // namespace SkVMInterpreterTypes
49
Mike Kleinec370972020-03-05 10:15:35 -060050 inline void interpret_skvm(const skvm::InterpreterInstruction insts[], const int ninsts,
51 const int nregs, const int loop,
52 const int strides[], const int nargs,
53 int n, void* args[]) {
54 using namespace skvm;
55
John Stiles9a6f3992021-08-16 11:37:20 -040056 using SkVMInterpreterTypes::K;
57 using SkVMInterpreterTypes::I32;
58 using SkVMInterpreterTypes::I16;
59 using SkVMInterpreterTypes::F32;
60 using SkVMInterpreterTypes::U64;
61 using SkVMInterpreterTypes::U32;
62 using SkVMInterpreterTypes::U16;
63 using SkVMInterpreterTypes::U8;
64 using SkVMInterpreterTypes::Slot;
65
Mike Kleinec370972020-03-05 10:15:35 -060066 // We'll operate in SIMT style, knocking off K-size chunks from n while possible.
Mike Kleinec370972020-03-05 10:15:35 -060067
68 Slot few_regs[16];
69 std::unique_ptr<char[]> many_regs;
70
Mike Klein4284f752020-07-10 15:16:17 -050071 Slot* r = few_regs;
Mike Kleinec370972020-03-05 10:15:35 -060072
73 if (nregs > (int)SK_ARRAY_COUNT(few_regs)) {
74 // Annoyingly we can't trust that malloc() or new will work with Slot because
75 // the skvx::Vec types may have alignment greater than what they provide.
76 // We'll overallocate one extra register so we can align manually.
77 many_regs.reset(new char[ sizeof(Slot) * (nregs + 1) ]);
78
79 uintptr_t addr = (uintptr_t)many_regs.get();
80 addr += alignof(Slot) -
81 (addr & (alignof(Slot) - 1));
82 SkASSERT((addr & (alignof(Slot) - 1)) == 0);
Mike Klein4284f752020-07-10 15:16:17 -050083 r = (Slot*)addr;
Mike Kleinec370972020-03-05 10:15:35 -060084 }
85
86
Mike Kleinec370972020-03-05 10:15:35 -060087 // Step each argument pointer ahead by its stride a number of times.
88 auto step_args = [&](int times) {
89 for (int i = 0; i < nargs; i++) {
90 args[i] = (void*)( (char*)args[i] + times * strides[i] );
91 }
92 };
93
94 int start = 0,
95 stride;
96 for ( ; n > 0; start = loop, n -= stride, step_args(stride)) {
97 stride = n >= K ? K : 1;
98
John Stiles68f56062021-08-03 12:31:56 -040099 for (int instIdx = start; instIdx < ninsts; instIdx++) {
100 InterpreterInstruction inst = insts[instIdx];
Mike Kleinec370972020-03-05 10:15:35 -0600101
Mike Kleinf3087d82021-01-19 11:36:25 -0600102 // d = op(x,y,z,w, immA,immB)
Mike Kleinec370972020-03-05 10:15:35 -0600103 Reg d = inst.d,
104 x = inst.x,
105 y = inst.y,
Mike Kleinf3087d82021-01-19 11:36:25 -0600106 z = inst.z,
107 w = inst.w;
Mike Kleinaad97192021-01-19 11:04:37 -0600108 int immA = inst.immA,
Herb Derbya37001e2021-07-22 17:34:21 -0400109 immB = inst.immB,
110 immC = inst.immC;
Mike Kleinec370972020-03-05 10:15:35 -0600111
112 // Ops that interact with memory need to know whether we're stride=1 or K,
113 // but all non-memory ops can run the same code no matter the stride.
114 switch (2*(int)inst.op + (stride == K ? 1 : 0)) {
115 default: SkUNREACHABLE;
116
117 #define STRIDE_1(op) case 2*(int)op
118 #define STRIDE_K(op) case 2*(int)op + 1
Mike Kleinaad97192021-01-19 11:04:37 -0600119 STRIDE_1(Op::store8 ): memcpy(args[immA], &r[x].i32, 1); break;
120 STRIDE_1(Op::store16): memcpy(args[immA], &r[x].i32, 2); break;
121 STRIDE_1(Op::store32): memcpy(args[immA], &r[x].i32, 4); break;
122 STRIDE_1(Op::store64): memcpy((char*)args[immA]+0, &r[x].i32, 4);
123 memcpy((char*)args[immA]+4, &r[y].i32, 4); break;
Mike Kleinec370972020-03-05 10:15:35 -0600124
Mike Kleinaad97192021-01-19 11:04:37 -0600125 STRIDE_K(Op::store8 ): skvx::cast<uint8_t> (r[x].i32).store(args[immA]); break;
126 STRIDE_K(Op::store16): skvx::cast<uint16_t>(r[x].i32).store(args[immA]); break;
127 STRIDE_K(Op::store32): (r[x].i32).store(args[immA]); break;
Mike Klein6732da02020-07-16 13:03:18 -0500128 STRIDE_K(Op::store64): (skvx::cast<uint64_t>(r[x].u32) << 0 |
Mike Kleinaad97192021-01-19 11:04:37 -0600129 skvx::cast<uint64_t>(r[y].u32) << 32).store(args[immA]);
Mike Klein6732da02020-07-16 13:03:18 -0500130 break;
Mike Kleinec370972020-03-05 10:15:35 -0600131
Mike Kleinaad97192021-01-19 11:04:37 -0600132 STRIDE_1(Op::load8 ): r[d].i32 = 0; memcpy(&r[d].i32, args[immA], 1); break;
133 STRIDE_1(Op::load16): r[d].i32 = 0; memcpy(&r[d].i32, args[immA], 2); break;
134 STRIDE_1(Op::load32): r[d].i32 = 0; memcpy(&r[d].i32, args[immA], 4); break;
Mike Klein31367892020-07-30 08:19:12 -0500135 STRIDE_1(Op::load64):
Mike Kleinaad97192021-01-19 11:04:37 -0600136 r[d].i32 = 0; memcpy(&r[d].i32, (char*)args[immA] + 4*immB, 4); break;
Mike Kleinec370972020-03-05 10:15:35 -0600137
Mike Kleinaad97192021-01-19 11:04:37 -0600138 STRIDE_K(Op::load8 ): r[d].i32= skvx::cast<int>(U8 ::Load(args[immA])); break;
139 STRIDE_K(Op::load16): r[d].i32= skvx::cast<int>(U16::Load(args[immA])); break;
140 STRIDE_K(Op::load32): r[d].i32= I32::Load(args[immA]) ; break;
Mike Klein31367892020-07-30 08:19:12 -0500141 STRIDE_K(Op::load64):
Mike Kleinaad97192021-01-19 11:04:37 -0600142 // Low 32 bits if immB=0, or high 32 bits if immB=1.
143 r[d].i32 = skvx::cast<int>(U64::Load(args[immA]) >> (32*immB)); break;
Mike Kleinec370972020-03-05 10:15:35 -0600144
145 // The pointer we base our gather on is loaded indirectly from a uniform:
Mike Kleinaad97192021-01-19 11:04:37 -0600146 // - args[immA] is the uniform holding our gather base pointer somewhere;
147 // - (const uint8_t*)args[immA] + immB points to the gather base pointer;
Mike Kleinec370972020-03-05 10:15:35 -0600148 // - memcpy() loads the gather base and into a pointer of the right type.
149 // After all that we have an ordinary (uniform) pointer `ptr` to load from,
Mike Klein4284f752020-07-10 15:16:17 -0500150 // and we then gather from it using the varying indices in r[x].
Mike Kleinfeb4d102020-09-17 08:54:08 -0500151 STRIDE_1(Op::gather8): {
152 const uint8_t* ptr;
Mike Kleinaad97192021-01-19 11:04:37 -0600153 memcpy(&ptr, (const uint8_t*)args[immA] + immB, sizeof(ptr));
Mike Kleinfeb4d102020-09-17 08:54:08 -0500154 r[d].i32 = ptr[ r[x].i32[0] ];
155 } break;
156 STRIDE_1(Op::gather16): {
157 const uint16_t* ptr;
Mike Kleinaad97192021-01-19 11:04:37 -0600158 memcpy(&ptr, (const uint8_t*)args[immA] + immB, sizeof(ptr));
Mike Kleinfeb4d102020-09-17 08:54:08 -0500159 r[d].i32 = ptr[ r[x].i32[0] ];
160 } break;
161 STRIDE_1(Op::gather32): {
162 const int* ptr;
Mike Kleinaad97192021-01-19 11:04:37 -0600163 memcpy(&ptr, (const uint8_t*)args[immA] + immB, sizeof(ptr));
Mike Kleinfeb4d102020-09-17 08:54:08 -0500164 r[d].i32 = ptr[ r[x].i32[0] ];
165 } break;
Mike Kleinec370972020-03-05 10:15:35 -0600166
Mike Kleinfeb4d102020-09-17 08:54:08 -0500167 STRIDE_K(Op::gather8): {
168 const uint8_t* ptr;
Mike Kleinaad97192021-01-19 11:04:37 -0600169 memcpy(&ptr, (const uint8_t*)args[immA] + immB, sizeof(ptr));
Mike Klein840e8ea2020-10-12 12:38:10 -0500170 r[d].i32 = map([&](int ix) { return (int)ptr[ix]; }, r[x].i32);
Mike Kleinfeb4d102020-09-17 08:54:08 -0500171 } break;
172 STRIDE_K(Op::gather16): {
173 const uint16_t* ptr;
Mike Kleinaad97192021-01-19 11:04:37 -0600174 memcpy(&ptr, (const uint8_t*)args[immA] + immB, sizeof(ptr));
Mike Klein840e8ea2020-10-12 12:38:10 -0500175 r[d].i32 = map([&](int ix) { return (int)ptr[ix]; }, r[x].i32);
Mike Kleinfeb4d102020-09-17 08:54:08 -0500176 } break;
177 STRIDE_K(Op::gather32): {
178 const int* ptr;
Mike Kleinaad97192021-01-19 11:04:37 -0600179 memcpy(&ptr, (const uint8_t*)args[immA] + immB, sizeof(ptr));
Mike Klein2e69a132020-09-18 08:02:46 -0500180 r[d].i32 = gather32(ptr, r[x].i32);
Mike Kleinfeb4d102020-09-17 08:54:08 -0500181 } break;
Mike Kleinec370972020-03-05 10:15:35 -0600182
183 #undef STRIDE_1
184 #undef STRIDE_K
185
186 // Ops that don't interact with memory should never care about the stride.
187 #define CASE(op) case 2*(int)op: /*fallthrough*/ case 2*(int)op+1
188
Mike Klein89b3c1f2020-07-29 16:45:05 -0500189 // These 128-bit ops are implemented serially for simplicity.
Mike Klein31367892020-07-30 08:19:12 -0500190 CASE(Op::store128): {
Mike Kleine0fe62a2021-01-19 12:16:18 -0600191 U64 lo = (skvx::cast<uint64_t>(r[x].u32) << 0 |
192 skvx::cast<uint64_t>(r[y].u32) << 32),
193 hi = (skvx::cast<uint64_t>(r[z].u32) << 0 |
194 skvx::cast<uint64_t>(r[w].u32) << 32);
Mike Klein89b3c1f2020-07-29 16:45:05 -0500195 for (int i = 0; i < stride; i++) {
Mike Kleine0fe62a2021-01-19 12:16:18 -0600196 memcpy((char*)args[immA] + 16*i + 0, &lo[i], 8);
197 memcpy((char*)args[immA] + 16*i + 8, &hi[i], 8);
Mike Klein89b3c1f2020-07-29 16:45:05 -0500198 }
199 } break;
200
Mike Klein31367892020-07-30 08:19:12 -0500201 CASE(Op::load128):
Mike Klein89b3c1f2020-07-29 16:45:05 -0500202 r[d].i32 = 0;
203 for (int i = 0; i < stride; i++) {
Mike Kleinaad97192021-01-19 11:04:37 -0600204 memcpy(&r[d].i32[i], (const char*)args[immA] + 16*i+ 4*immB, 4);
Mike Klein89b3c1f2020-07-29 16:45:05 -0500205 } break;
206
Mike Kleinec370972020-03-05 10:15:35 -0600207 CASE(Op::assert_true):
208 #ifdef SK_DEBUG
Mike Klein4284f752020-07-10 15:16:17 -0500209 if (!all(r[x].i32)) {
John Stiles68f56062021-08-03 12:31:56 -0400210 SkDebugf("inst %d, register %d\n", instIdx, y);
Mike Kleinec370972020-03-05 10:15:35 -0600211 for (int i = 0; i < K; i++) {
John Stiles68f56062021-08-03 12:31:56 -0400212 SkDebugf("\t%2d: %08x (%g)\n",
213 instIdx, r[y].i32[instIdx], r[y].f32[instIdx]);
Mike Kleinec370972020-03-05 10:15:35 -0600214 }
Mike Klein51a7f952020-09-16 16:00:33 -0500215 SkASSERT(false);
Mike Kleinec370972020-03-05 10:15:35 -0600216 }
Mike Kleinec370972020-03-05 10:15:35 -0600217 #endif
218 break;
219
220 CASE(Op::index): {
221 const int iota[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
Mike Klein394a6d52020-09-18 14:04:19 -0500222 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
223 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
224 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63 };
Mike Kleinec370972020-03-05 10:15:35 -0600225 static_assert(K <= SK_ARRAY_COUNT(iota), "");
226
Mike Klein4284f752020-07-10 15:16:17 -0500227 r[d].i32 = n - I32::Load(iota);
Mike Kleinec370972020-03-05 10:15:35 -0600228 } break;
229
Mike Kleinec370972020-03-05 10:15:35 -0600230 CASE(Op::uniform32):
Mike Kleinaad97192021-01-19 11:04:37 -0600231 r[d].i32 = *(const int*)( (const char*)args[immA] + immB );
Mike Kleinec370972020-03-05 10:15:35 -0600232 break;
233
Herb Derbya37001e2021-07-22 17:34:21 -0400234 CASE(Op::array32):
235 const int* ptr;
236 memcpy(&ptr, (const uint8_t*)args[immA] + immB, sizeof(ptr));
237 r[d].i32 = ptr[immC/sizeof(int)];
238 break;
239
Mike Kleinaad97192021-01-19 11:04:37 -0600240 CASE(Op::splat): r[d].i32 = immA; break;
Mike Kleinec370972020-03-05 10:15:35 -0600241
Mike Klein4284f752020-07-10 15:16:17 -0500242 CASE(Op::add_f32): r[d].f32 = r[x].f32 + r[y].f32; break;
243 CASE(Op::sub_f32): r[d].f32 = r[x].f32 - r[y].f32; break;
244 CASE(Op::mul_f32): r[d].f32 = r[x].f32 * r[y].f32; break;
245 CASE(Op::div_f32): r[d].f32 = r[x].f32 / r[y].f32; break;
246 CASE(Op::min_f32): r[d].f32 = min(r[x].f32, r[y].f32); break;
247 CASE(Op::max_f32): r[d].f32 = max(r[x].f32, r[y].f32); break;
Mike Kleinec370972020-03-05 10:15:35 -0600248
Mike Klein4284f752020-07-10 15:16:17 -0500249 CASE(Op::fma_f32): r[d].f32 = fma( r[x].f32, r[y].f32, r[z].f32); break;
250 CASE(Op::fms_f32): r[d].f32 = fma( r[x].f32, r[y].f32, -r[z].f32); break;
251 CASE(Op::fnma_f32): r[d].f32 = fma(-r[x].f32, r[y].f32, r[z].f32); break;
Mike Kleinec370972020-03-05 10:15:35 -0600252
Mike Klein4284f752020-07-10 15:16:17 -0500253 CASE(Op::sqrt_f32): r[d].f32 = sqrt(r[x].f32); break;
Mike Kleinec370972020-03-05 10:15:35 -0600254
Mike Klein4284f752020-07-10 15:16:17 -0500255 CASE(Op::add_i32): r[d].i32 = r[x].i32 + r[y].i32; break;
256 CASE(Op::sub_i32): r[d].i32 = r[x].i32 - r[y].i32; break;
257 CASE(Op::mul_i32): r[d].i32 = r[x].i32 * r[y].i32; break;
Mike Kleinec370972020-03-05 10:15:35 -0600258
Mike Kleinaad97192021-01-19 11:04:37 -0600259 CASE(Op::shl_i32): r[d].i32 = r[x].i32 << immA; break;
260 CASE(Op::sra_i32): r[d].i32 = r[x].i32 >> immA; break;
261 CASE(Op::shr_i32): r[d].u32 = r[x].u32 >> immA; break;
Mike Kleinec370972020-03-05 10:15:35 -0600262
Mike Klein4284f752020-07-10 15:16:17 -0500263 CASE(Op:: eq_f32): r[d].i32 = r[x].f32 == r[y].f32; break;
264 CASE(Op::neq_f32): r[d].i32 = r[x].f32 != r[y].f32; break;
265 CASE(Op:: gt_f32): r[d].i32 = r[x].f32 > r[y].f32; break;
266 CASE(Op::gte_f32): r[d].i32 = r[x].f32 >= r[y].f32; break;
Mike Kleinec370972020-03-05 10:15:35 -0600267
Mike Klein4284f752020-07-10 15:16:17 -0500268 CASE(Op:: eq_i32): r[d].i32 = r[x].i32 == r[y].i32; break;
269 CASE(Op:: gt_i32): r[d].i32 = r[x].i32 > r[y].i32; break;
Mike Kleinec370972020-03-05 10:15:35 -0600270
Mike Klein4284f752020-07-10 15:16:17 -0500271 CASE(Op::bit_and ): r[d].i32 = r[x].i32 & r[y].i32; break;
272 CASE(Op::bit_or ): r[d].i32 = r[x].i32 | r[y].i32; break;
273 CASE(Op::bit_xor ): r[d].i32 = r[x].i32 ^ r[y].i32; break;
274 CASE(Op::bit_clear): r[d].i32 = r[x].i32 & ~r[y].i32; break;
Mike Kleinec370972020-03-05 10:15:35 -0600275
Mike Klein4284f752020-07-10 15:16:17 -0500276 CASE(Op::select): r[d].i32 = skvx::if_then_else(r[x].i32, r[y].i32, r[z].i32);
Mike Kleinec370972020-03-05 10:15:35 -0600277 break;
278
Mike Klein4284f752020-07-10 15:16:17 -0500279 CASE(Op::ceil): r[d].f32 = skvx::ceil(r[x].f32) ; break;
280 CASE(Op::floor): r[d].f32 = skvx::floor(r[x].f32) ; break;
281 CASE(Op::to_f32): r[d].f32 = skvx::cast<float>( r[x].i32 ); break;
282 CASE(Op::trunc): r[d].i32 = skvx::cast<int> ( r[x].f32 ); break;
283 CASE(Op::round): r[d].i32 = skvx::cast<int> (skvx::lrint(r[x].f32)); break;
Mike Klein4d680cd2020-07-15 09:58:51 -0500284
Mike Klein42d67a62020-12-01 10:14:55 -0600285 CASE(Op::to_fp16):
Mike Klein4d680cd2020-07-15 09:58:51 -0500286 r[d].i32 = skvx::cast<int>(skvx::to_half(r[x].f32));
287 break;
Mike Klein42d67a62020-12-01 10:14:55 -0600288 CASE(Op::from_fp16):
Mike Klein4d680cd2020-07-15 09:58:51 -0500289 r[d].f32 = skvx::from_half(skvx::cast<uint16_t>(r[x].i32));
290 break;
Mike Klein98c512c2020-09-15 10:00:27 -0500291
Mike Kleinec370972020-03-05 10:15:35 -0600292 #undef CASE
293 }
294 }
295 }
296 }
297
John Stilesa6841be2020-08-06 14:11:56 -0400298} // namespace SK_OPTS_NS
Mike Kleinec370972020-03-05 10:15:35 -0600299
300#endif//SkVM_opts_DEFINED