blob: 6a0fc3cdb7fd9e9d1bf731eca7cc45e2a3fcffe3 [file] [log] [blame]
Mike Kleina7080262017-01-09 10:20:13 -05001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkCpu.h"
9#include "SkOpts.h"
10#include "SkRasterPipeline.h"
11#include "SkStream.h"
Mike Klein09326e72017-01-11 13:41:30 -050012#if defined(_MSC_VER)
13 #include <windows.h>
14#else
15 #include <sys/mman.h>
16#endif
Mike Kleina7080262017-01-09 10:20:13 -050017
18#include "SkSplicer_generated.h"
19#include "SkSplicer_shared.h"
20
Mike Klein13ccda42017-01-10 14:09:24 -050021// Uncomment to dump output JIT'd pipeline.
22//#define DUMP "/tmp/dump.bin"
Mike Kleinf7200982017-01-15 18:14:07 -050023//#define DUMP "/data/local/tmp/dump.bin"
Mike Klein13ccda42017-01-10 14:09:24 -050024//
25// On x86, we'll include IACA markers too.
26// https://software.intel.com/en-us/articles/intel-architecture-code-analyzer
27// Running IACA will disassemble, and more.
28// $ ./iaca.sh -arch HSW -64 -mark 0 /tmp/dump.bin | less
29//
30// To disassemble an aarch64 dump,
Mike Kleinf7200982017-01-15 18:14:07 -050031// $ adb pull /data/local/tmp/dump.bin; gobjdump -b binary -D dump.bin -m aarch64 | less
Mike Klein4ef8cb32017-01-12 11:36:46 -050032//
33// To disassemble an armv7 dump,
Mike Kleinf7200982017-01-15 18:14:07 -050034// $ adb pull /data/local/tmp/dump.bin; gobjdump -b binary -D dump.bin -m arm | less
Mike Kleina7080262017-01-09 10:20:13 -050035
36namespace {
37
38 // Stages expect these constants to be set to these values.
39 // It's fine to rearrange and add new ones if you update SkSplicer_constants.
40 static const SkSplicer_constants kConstants = {
Mike Klein8f297c92017-01-20 19:16:10 -050041 1.0f, 255.0f, 1/255.0f, 0x000000ff,
Mike Kleina7080262017-01-09 10:20:13 -050042 0.0025f, 0.6975f, 0.3000f, 1/12.92f, 0.055f, // from_srgb
43 12.46f, 0.411192f, 0.689206f, -0.0988f, 0.0043f, // to_srgb
44 };
45
Mike Kleina7080262017-01-09 10:20:13 -050046 // We do this a lot, so it's nice to infer the correct size. Works fine with arrays.
47 template <typename T>
Mike Klein8e619a22017-01-09 17:21:32 -050048 static void splice(SkWStream* buf, const T& val) {
Mike Kleina13b2482017-02-07 10:11:52 -050049 buf->write(&val, sizeof(val));
Mike Kleina7080262017-01-09 10:20:13 -050050 }
51
Mike Kleina9600952017-02-07 14:32:25 -050052 // Splice up to (but not including) the final return instruction in code.
53 template <typename T, size_t N>
54 static void splice_until_ret(SkWStream* buf, const T (&code)[N]) {
55 // On all platforms we splice today, return is a single T (byte on x86, u32 on ARM).
56 buf->write(&code, sizeof(T) * (N-1));
57 }
58
Mike Klein8e619a22017-01-09 17:21:32 -050059#if defined(__aarch64__)
60 static constexpr int kStride = 4;
Mike Klein8e619a22017-01-09 17:21:32 -050061 static void set_ctx(SkWStream* buf, void* ctx) {
62 uint16_t parts[4];
63 memcpy(parts, &ctx, 8);
64 splice(buf, 0xd2f00000 | (parts[3] << 5) | 0x2); // move 16-bit intermediate << 48 into x2
65 splice(buf, 0xf2c00000 | (parts[2] << 5) | 0x2); // merge 16-bit intermediate << 32 into x2
66 splice(buf, 0xf2a00000 | (parts[1] << 5) | 0x2); // merge 16-bit intermediate << 16 into x2
67 splice(buf, 0xf2800000 | (parts[0] << 5) | 0x2); // merge 16-bit intermediate << 0 into x2
68 }
69 static void loop(SkWStream* buf, int loop_start) {
Mike Klein8e619a22017-01-09 17:21:32 -050070 splice(buf, 0xeb01001f); // cmp x0, x1
Mike Klein13ccda42017-01-10 14:09:24 -050071 int off = loop_start - (int)buf->bytesWritten();
Mike Klein8e619a22017-01-09 17:21:32 -050072 off /= 4; // bytes -> instructions, still signed
73 off = (off & 0x7ffff) << 5; // 19 bit maximum range (+- 256K instructions)
74 splice(buf, 0x54000003 | off); // b.cc loop_start (cc == "carry clear", unsigned less than)
75 }
76 static void ret(SkWStream* buf) {
77 splice(buf, 0xd65f03c0); // ret
78 }
Mike Klein4ef8cb32017-01-12 11:36:46 -050079#elif defined(__ARM_NEON__)
80 static constexpr int kStride = 2;
81 static void set_ctx(SkWStream* buf, void* ctx) {
82 uint16_t parts[2];
83 auto encode = [](uint16_t part) -> uint32_t {
84 return (part & 0xf000) << 4 | (part & 0xfff);
85 };
86 memcpy(parts, &ctx, 4);
87 splice(buf, 0xe3002000 | encode(parts[0])); // mov r2, <bottom 16 bits>
88 splice(buf, 0xe3402000 | encode(parts[1])); // movt r2, <top 16 bits>
89 }
90 static void loop(SkWStream* buf, int loop_start) {
Mike Klein4ef8cb32017-01-12 11:36:46 -050091 splice(buf, 0xe1500001); // cmp r0, r1
92 int off = loop_start - ((int)buf->bytesWritten() + 8 /*ARM is weird*/);
93 off /= 4; // bytes -> instructions, still signed
94 off = (off & 0x00ffffff);
95 splice(buf, 0x3a000000 | off); // bcc loop_start
96 }
97 static void ret(SkWStream* buf) {
98 splice(buf, 0xe12fff1e); // bx lr
99 }
Mike Klein8e619a22017-01-09 17:21:32 -0500100#else
101 static constexpr int kStride = 8;
Mike Klein8e619a22017-01-09 17:21:32 -0500102 static void set_ctx(SkWStream* buf, void* ctx) {
103 static const uint8_t movabsq_rdx[] = { 0x48, 0xba };
104 splice(buf, movabsq_rdx); // movabsq <next 8 bytes>, %rdx
105 splice(buf, ctx);
106 }
107 static void loop(SkWStream* buf, int loop_start) {
Mike Klein8e619a22017-01-09 17:21:32 -0500108 static const uint8_t cmp_rsi_rdi[] = { 0x48, 0x39, 0xf7 };
109 static const uint8_t jb_near[] = { 0x0f, 0x8c };
Mike Klein8e619a22017-01-09 17:21:32 -0500110 splice(buf, cmp_rsi_rdi); // cmp %rsi, %rdi
111 splice(buf, jb_near); // jb <next 4 bytes> (b == "before", unsigned less than)
112 splice(buf, loop_start - (int)(buf->bytesWritten() + 4));
113 }
114 static void ret(SkWStream* buf) {
115 static const uint8_t vzeroupper[] = { 0xc5, 0xf8, 0x77 };
116 static const uint8_t ret[] = { 0xc3 };
117 splice(buf, vzeroupper);
118 splice(buf, ret);
119 }
120#endif
121
Mike Klein09326e72017-01-11 13:41:30 -0500122#if defined(_MSC_VER)
123 // Adapt from MS ABI to System V ABI used by stages.
124 static void before_loop(SkWStream* buf) {
125 static const uint8_t ms_to_system_v[] = {
126 0x56, // push %rsi
127 0x57, // push %rdi
128 0x48,0x81,0xec,0xa8,0x00,0x00,0x00, // sub $0xa8,%rsp
129 0xc5,0x78,0x29,0xbc,0x24,0x90,0x00,0x00,0x00, // vmovaps %xmm15,0x90(%rsp)
130 0xc5,0x78,0x29,0xb4,0x24,0x80,0x00,0x00,0x00, // vmovaps %xmm14,0x80(%rsp)
131 0xc5,0x78,0x29,0x6c,0x24,0x70, // vmovaps %xmm13,0x70(%rsp)
132 0xc5,0x78,0x29,0x64,0x24,0x60, // vmovaps %xmm12,0x60(%rsp)
133 0xc5,0x78,0x29,0x5c,0x24,0x50, // vmovaps %xmm11,0x50(%rsp)
134 0xc5,0x78,0x29,0x54,0x24,0x40, // vmovaps %xmm10,0x40(%rsp)
135 0xc5,0x78,0x29,0x4c,0x24,0x30, // vmovaps %xmm9,0x30(%rsp)
136 0xc5,0x78,0x29,0x44,0x24,0x20, // vmovaps %xmm8,0x20(%rsp)
137 0xc5,0xf8,0x29,0x7c,0x24,0x10, // vmovaps %xmm7,0x10(%rsp)
138 0xc5,0xf8,0x29,0x34,0x24, // vmovaps %xmm6,(%rsp)
139 0x48,0x89,0xcf, // mov %rcx,%rdi
140 0x48,0x89,0xd6, // mov %rdx,%rsi
141 0x4c,0x89,0xc2, // mov %r8,%rdx
142 0x4c,0x89,0xc9, // mov %r9,%rcx
143 };
144 splice(buf, ms_to_system_v);
145 }
146 static void after_loop(SkWStream* buf) {
147 static const uint8_t system_v_to_ms[] = {
148 0xc5,0xf8,0x28,0x34,0x24, // vmovaps (%rsp),%xmm6
149 0xc5,0xf8,0x28,0x7c,0x24,0x10, // vmovaps 0x10(%rsp),%xmm7
150 0xc5,0x78,0x28,0x44,0x24,0x20, // vmovaps 0x20(%rsp),%xmm8
151 0xc5,0x78,0x28,0x4c,0x24,0x30, // vmovaps 0x30(%rsp),%xmm9
152 0xc5,0x78,0x28,0x54,0x24,0x40, // vmovaps 0x40(%rsp),%xmm10
153 0xc5,0x78,0x28,0x5c,0x24,0x50, // vmovaps 0x50(%rsp),%xmm11
154 0xc5,0x78,0x28,0x64,0x24,0x60, // vmovaps 0x60(%rsp),%xmm12
155 0xc5,0x78,0x28,0x6c,0x24,0x70, // vmovaps 0x70(%rsp),%xmm13
156 0xc5,0x78,0x28,0xb4,0x24,0x80,0x00,0x00,0x00, // vmovaps 0x80(%rsp),%xmm14
157 0xc5,0x78,0x28,0xbc,0x24,0x90,0x00,0x00,0x00, // vmovaps 0x90(%rsp),%xmm15
158 0x48,0x81,0xc4,0xa8,0x00,0x00,0x00, // add $0xa8,%rsp
159 0x5f, // pop %rdi
160 0x5e, // pop %rsi
161 };
162 splice(buf, system_v_to_ms);
163 }
Mike Klein4ef8cb32017-01-12 11:36:46 -0500164#elif !defined(__aarch64__) && !defined(__ARM_NEON__) && defined(DUMP)
Mike Klein09326e72017-01-11 13:41:30 -0500165 // IACA start and end markers.
Mike Kleina7080262017-01-09 10:20:13 -0500166 static const uint8_t ud2[] = { 0x0f, 0x0b }; // undefined... crashes when run
167 static const uint8_t nop3[] = { 0x64, 0x67, 0x90 }; // 3 byte no-op
168 static const uint8_t movl_ebx[] = { 0xbb }; // move next 4 bytes into ebx
169
Mike Klein09326e72017-01-11 13:41:30 -0500170 static void before_loop(SkWStream* buf) {
Mike Klein8e619a22017-01-09 17:21:32 -0500171 splice(buf, ud2);
172 splice(buf, movl_ebx);
173 splice(buf, 111);
174 splice(buf, nop3);
Mike Kleina7080262017-01-09 10:20:13 -0500175 }
Mike Klein09326e72017-01-11 13:41:30 -0500176 static void after_loop(SkWStream* buf) {
Mike Klein8e619a22017-01-09 17:21:32 -0500177 splice(buf, movl_ebx);
178 splice(buf, 222);
179 splice(buf, nop3);
180 splice(buf, ud2);
Mike Kleina7080262017-01-09 10:20:13 -0500181 }
182#else
Mike Klein09326e72017-01-11 13:41:30 -0500183 static void before_loop(SkWStream*) {}
184 static void after_loop (SkWStream*) {}
Mike Kleina7080262017-01-09 10:20:13 -0500185#endif
186
Mike Klein09326e72017-01-11 13:41:30 -0500187 // We can only mprotect / VirtualProtect at 4K page granularity.
188 static size_t round_up_to_full_pages(size_t len) {
189 size_t size = 0;
190 while (size < len) {
191 size += 4096;
Mike Kleina7080262017-01-09 10:20:13 -0500192 }
Mike Klein09326e72017-01-11 13:41:30 -0500193 return size;
194 }
195
196#if defined(_MSC_VER)
197 // Copy len bytes from src to memory that's executable. cleanup with cleanup_executable_mem().
198 static void* copy_to_executable_mem(const void* src, size_t* len) {
199 if (!src || !*len) {
200 return nullptr;
201 }
202
203 size_t alloc = round_up_to_full_pages(*len);
204
205 auto fn = VirtualAlloc(nullptr, alloc, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
206 memcpy(fn, src, *len);
207
208 DWORD dont_care;
209 VirtualProtect(fn, alloc, PAGE_EXECUTE_READ, &dont_care);
210
211 *len = alloc;
212 return fn;
213 }
214 static void cleanup_executable_mem(void* fn, size_t len) {
215 if (fn) {
216 VirtualFree(fn, 0, MEM_RELEASE);
217 }
218 }
219#else
220 static void* copy_to_executable_mem(const void* src, size_t* len) {
221 if (!src || !*len) {
222 return nullptr;
223 }
224
225 size_t alloc = round_up_to_full_pages(*len);
226
227 auto fn = mmap(nullptr, alloc, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
228 memcpy(fn, src, *len);
229
230 mprotect(fn, alloc, PROT_READ|PROT_EXEC);
231 __builtin___clear_cache((char*)fn, (char*)fn + *len); // Essential on ARM; no-op on x86.
232
233 *len = alloc;
234 return fn;
Mike Kleina7080262017-01-09 10:20:13 -0500235 }
236 static void cleanup_executable_mem(void* fn, size_t len) {
237 if (fn) {
238 munmap(fn, len);
239 }
240 }
Mike Klein09326e72017-01-11 13:41:30 -0500241#endif
Mike Kleina7080262017-01-09 10:20:13 -0500242
Mike Kleina13b2482017-02-07 10:11:52 -0500243 static bool splice(SkWStream* buf, SkRasterPipeline::StockStage st) {
Mike Kleinf7200982017-01-15 18:14:07 -0500244 switch (st) {
245 default: return false;
Mike Kleina9600952017-02-07 14:32:25 -0500246 #define CASE(st) case SkRasterPipeline::st: splice_until_ret(buf, kSplice_##st); break
Mike Kleinf7200982017-01-15 18:14:07 -0500247 CASE(clear);
248 CASE(plus_);
249 CASE(srcover);
250 CASE(dstover);
251 CASE(clamp_0);
252 CASE(clamp_1);
253 CASE(clamp_a);
254 CASE(swap);
255 CASE(move_src_dst);
256 CASE(move_dst_src);
257 CASE(premul);
258 CASE(unpremul);
259 CASE(from_srgb);
260 CASE(to_srgb);
261 CASE(scale_u8);
262 CASE(load_tables);
263 CASE(load_8888);
264 CASE(store_8888);
265 CASE(load_f16);
266 CASE(store_f16);
267 CASE(matrix_3x4);
268 #undef CASE
269 }
270 return true;
271 }
272
Mike Kleina7080262017-01-09 10:20:13 -0500273 struct Spliced {
274
275 Spliced(const SkRasterPipeline::Stage* stages, int nstages) {
276 // We always create a backup interpreter pipeline,
277 // - to handle any program we can't, and
Mike Kleinf7200982017-01-15 18:14:07 -0500278 // - to handle the n < stride tails.
Mike Kleina7080262017-01-09 10:20:13 -0500279 fBackup = SkOpts::compile_pipeline(stages, nstages);
280 fSplicedLen = 0;
281 fSpliced = nullptr;
282 // If we return early anywhere in here, !fSpliced means we'll use fBackup instead.
283
Mike Klein4ef8cb32017-01-12 11:36:46 -0500284 #if defined(__aarch64__)
285 #elif defined(__ARM_NEON__)
286 // Late generation ARMv7, e.g. Cortex A15 or Krait.
287 if (!SkCpu::Supports(SkCpu::NEON|SkCpu::NEON_FMA|SkCpu::VFP_FP16)) {
288 return;
289 }
290 #else
291 // To keep things simple, only one x86 target supported: Haswell+ x86-64.
Mike Kleina7080262017-01-09 10:20:13 -0500292 if (!SkCpu::Supports(SkCpu::HSW) || sizeof(void*) != 8) {
293 return;
294 }
Mike Klein8e619a22017-01-09 17:21:32 -0500295 #endif
Mike Kleina7080262017-01-09 10:20:13 -0500296
297 SkDynamicMemoryWStream buf;
298
Mike Klein7ba89a12017-01-10 13:42:51 -0500299 // Our loop is the equivalent of this C++ code:
300 // do {
301 // ... run spliced stages...
Mike Kleinf7200982017-01-15 18:14:07 -0500302 // x += stride;
Mike Klein7ba89a12017-01-10 13:42:51 -0500303 // } while(x < limit);
Mike Klein09326e72017-01-11 13:41:30 -0500304 before_loop(&buf);
Mike Kleina7080262017-01-09 10:20:13 -0500305 auto loop_start = buf.bytesWritten(); // Think of this like a label, loop_start:
306
307 for (int i = 0; i < nstages; i++) {
Mike Klein8e619a22017-01-09 17:21:32 -0500308 // If a stage has a context pointer, load it into rdx/x2, Stage argument 3 "ctx".
Mike Kleina7080262017-01-09 10:20:13 -0500309 if (stages[i].ctx) {
Mike Klein8e619a22017-01-09 17:21:32 -0500310 set_ctx(&buf, stages[i].ctx);
Mike Kleina7080262017-01-09 10:20:13 -0500311 }
312
313 // Splice in the code for the Stages, generated offline into SkSplicer_generated.h.
Mike Kleina13b2482017-02-07 10:11:52 -0500314 if (!splice(&buf, stages[i].stage)) {
Mike Kleinf7200982017-01-15 18:14:07 -0500315 //SkDebugf("SkSplicer can't yet handle stage %d.\n", stages[i].stage);
316 return;
Mike Kleina7080262017-01-09 10:20:13 -0500317 }
318 }
319
Mike Kleina9600952017-02-07 14:32:25 -0500320 splice_until_ret(&buf, kSplice_inc_x);
Mike Klein8e619a22017-01-09 17:21:32 -0500321 loop(&buf, loop_start); // Loop back to handle more pixels if not done.
Mike Klein09326e72017-01-11 13:41:30 -0500322 after_loop(&buf);
Mike Klein8e619a22017-01-09 17:21:32 -0500323 ret(&buf); // We're done.
Mike Kleina7080262017-01-09 10:20:13 -0500324
325 auto data = buf.detachAsData();
326 fSplicedLen = data->size();
Mike Klein09326e72017-01-11 13:41:30 -0500327 fSpliced = copy_to_executable_mem(data->data(), &fSplicedLen);
Mike Kleina7080262017-01-09 10:20:13 -0500328
Mike Klein13ccda42017-01-10 14:09:24 -0500329 #if defined(DUMP)
330 SkFILEWStream(DUMP).write(data->data(), data->size());
Mike Kleina7080262017-01-09 10:20:13 -0500331 #endif
332 }
333
334 // Spliced is stored in a std::function, so it needs to be copyable.
335 Spliced(const Spliced& o) : fBackup (o.fBackup)
336 , fSplicedLen(o.fSplicedLen)
Mike Kleina13b2482017-02-07 10:11:52 -0500337 , fSpliced (copy_to_executable_mem(o.fSpliced, &fSplicedLen)) {}
Mike Kleina7080262017-01-09 10:20:13 -0500338
339 ~Spliced() {
340 cleanup_executable_mem(fSpliced, fSplicedLen);
341 }
342
343 // Here's where we call fSpliced if we created it, fBackup if not.
Mike Klein319ba3d2017-01-20 15:11:54 -0500344 void operator()(size_t x, size_t n) const {
Mike Kleina13b2482017-02-07 10:11:52 -0500345 size_t body = n/kStride*kStride; // Largest multiple of kStride (2, 4, 8, or 16) <= n.
Mike Kleinf7200982017-01-15 18:14:07 -0500346 if (fSpliced && body) { // Can we run fSpliced for at least one stride?
Mike Kleinf7200982017-01-15 18:14:07 -0500347 using Fn = void(size_t x, size_t limit, void* ctx, const void* k);
Mike Kleina13b2482017-02-07 10:11:52 -0500348 ((Fn*)fSpliced)(x, x+body, nullptr, &kConstants);
Mike Kleina7080262017-01-09 10:20:13 -0500349
Mike Kleinf7200982017-01-15 18:14:07 -0500350 // Fall through to fBackup for any n<stride last pixels.
Mike Kleina7080262017-01-09 10:20:13 -0500351 x += body;
352 n -= body;
353 }
Mike Klein319ba3d2017-01-20 15:11:54 -0500354 fBackup(x,n);
Mike Kleina7080262017-01-09 10:20:13 -0500355 }
356
Mike Klein319ba3d2017-01-20 15:11:54 -0500357 std::function<void(size_t, size_t)> fBackup;
358 size_t fSplicedLen;
359 void* fSpliced;
Mike Kleina7080262017-01-09 10:20:13 -0500360 };
361
362}
363
Mike Klein319ba3d2017-01-20 15:11:54 -0500364std::function<void(size_t, size_t)> SkRasterPipeline::jit() const {
Mike Kleina7080262017-01-09 10:20:13 -0500365 return Spliced(fStages.data(), SkToInt(fStages.size()));
366}