• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 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 "include/core/SkStream.h"
9 #include "src/base/SkArenaAlloc.h"
10 #include "src/base/SkStringView.h"
11 #include "src/core/SkOpts.h"
12 #include "src/core/SkRasterPipeline.h"
13 #include "src/sksl/codegen/SkSLRasterPipelineBuilder.h"
14 #include "tests/Test.h"
15 
get_program_dump(SkSL::RP::Program & program)16 static sk_sp<SkData> get_program_dump(SkSL::RP::Program& program) {
17     SkDynamicMemoryWStream stream;
18     program.dump(&stream);
19     return stream.detachAsData();
20 }
21 
as_string_view(sk_sp<SkData> dump)22 static std::string_view as_string_view(sk_sp<SkData> dump) {
23     return std::string_view(static_cast<const char*>(dump->data()), dump->size());
24 }
25 
check(skiatest::Reporter * r,SkSL::RP::Program & program,std::string_view expected)26 static void check(skiatest::Reporter* r, SkSL::RP::Program& program, std::string_view expected) {
27     // Verify that the program matches expectations.
28     sk_sp<SkData> dump = get_program_dump(program);
29     REPORTER_ASSERT(r, as_string_view(dump) == expected,
30                     "Output did not match expectation:\n%.*s",
31                     (int)dump->size(), static_cast<const char*>(dump->data()));
32 }
33 
one_slot_at(SkSL::RP::Slot index)34 static SkSL::RP::SlotRange one_slot_at(SkSL::RP::Slot index) {
35     return SkSL::RP::SlotRange{index, 1};
36 }
37 
two_slots_at(SkSL::RP::Slot index)38 static SkSL::RP::SlotRange two_slots_at(SkSL::RP::Slot index) {
39     return SkSL::RP::SlotRange{index, 2};
40 }
41 
three_slots_at(SkSL::RP::Slot index)42 static SkSL::RP::SlotRange three_slots_at(SkSL::RP::Slot index) {
43     return SkSL::RP::SlotRange{index, 3};
44 }
45 
four_slots_at(SkSL::RP::Slot index)46 static SkSL::RP::SlotRange four_slots_at(SkSL::RP::Slot index) {
47     return SkSL::RP::SlotRange{index, 4};
48 }
49 
five_slots_at(SkSL::RP::Slot index)50 static SkSL::RP::SlotRange five_slots_at(SkSL::RP::Slot index) {
51     return SkSL::RP::SlotRange{index, 5};
52 }
53 
DEF_TEST(RasterPipelineBuilder,r)54 DEF_TEST(RasterPipelineBuilder, r) {
55     // Create a very simple nonsense program.
56     SkSL::RP::Builder builder;
57     builder.store_src_rg(two_slots_at(0));
58     builder.store_src(four_slots_at(2));
59     builder.store_dst(four_slots_at(4));
60     builder.store_device_xy01(four_slots_at(6));
61     builder.init_lane_masks();
62     builder.enableExecutionMaskWrites();
63     builder.mask_off_return_mask();
64     builder.mask_off_loop_mask();
65     builder.reenable_loop_mask(one_slot_at(4));
66     builder.disableExecutionMaskWrites();
67     builder.load_src(four_slots_at(1));
68     builder.load_dst(four_slots_at(3));
69     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/10,
70                                                                 /*numUniformSlots=*/0);
71     check(r, *program,
72 R"(    1. store_src_rg                   v0..1 = src.rg
73     2. store_src                      v2..5 = src.rgba
74     3. store_dst                      v4..7 = dst.rgba
75     4. store_device_xy01              v6..9 = DeviceCoords.xy01
76     5. init_lane_masks                CondMask = LoopMask = RetMask = true
77     6. mask_off_return_mask           RetMask &= ~(CondMask & LoopMask & RetMask)
78     7. mask_off_loop_mask             LoopMask &= ~(CondMask & LoopMask & RetMask)
79     8. reenable_loop_mask             LoopMask |= v4
80     9. load_src                       src.rgba = v1..4
81    10. load_dst                       dst.rgba = v3..6
82 )");
83 }
84 
DEF_TEST(RasterPipelineBuilderPushPopMaskRegisters,r)85 DEF_TEST(RasterPipelineBuilderPushPopMaskRegisters, r) {
86     // Create a very simple nonsense program.
87     SkSL::RP::Builder builder;
88 
89     REPORTER_ASSERT(r, !builder.executionMaskWritesAreEnabled());
90     builder.enableExecutionMaskWrites();
91     REPORTER_ASSERT(r, builder.executionMaskWritesAreEnabled());
92 
93     builder.push_condition_mask();         // push into 0
94     builder.push_loop_mask();              // push into 1
95     builder.push_return_mask();            // push into 2
96     builder.merge_condition_mask();        // set the condition-mask to 1 & 2
97     builder.pop_condition_mask();          // pop from 2
98     builder.merge_loop_mask();             // mask off the loop-mask against 1
99     builder.push_condition_mask();         // push into 2
100     builder.pop_condition_mask();          // pop from 2
101     builder.pop_loop_mask();               // pop from 1
102     builder.pop_return_mask();             // pop from 0
103     builder.push_condition_mask();         // push into 0
104     builder.pop_and_reenable_loop_mask();  // pop from 0
105 
106     REPORTER_ASSERT(r, builder.executionMaskWritesAreEnabled());
107     builder.disableExecutionMaskWrites();
108     REPORTER_ASSERT(r, !builder.executionMaskWritesAreEnabled());
109 
110     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
111                                                                 /*numUniformSlots=*/0);
112     check(r, *program,
113 R"(    1. store_condition_mask           $0 = CondMask
114     2. store_loop_mask                $1 = LoopMask
115     3. store_return_mask              $2 = RetMask
116     4. merge_condition_mask           CondMask = $1 & $2
117     5. load_condition_mask            CondMask = $2
118     6. merge_loop_mask                LoopMask &= $1
119     7. store_condition_mask           $2 = CondMask
120     8. load_condition_mask            CondMask = $2
121     9. load_loop_mask                 LoopMask = $1
122    10. load_return_mask               RetMask = $0
123    11. store_condition_mask           $0 = CondMask
124    12. reenable_loop_mask             LoopMask |= $0
125 )");
126 }
127 
128 
DEF_TEST(RasterPipelineBuilderCaseOp,r)129 DEF_TEST(RasterPipelineBuilderCaseOp, r) {
130     // Create a very simple nonsense program.
131     SkSL::RP::Builder builder;
132 
133     builder.push_literal_i(123);    // push a test value
134     builder.push_literal_i(~0);     // push an all-on default mask
135     builder.case_op(123);           // do `case 123:`
136     builder.case_op(124);           // do `case 124:`
137     builder.discard_stack(2);
138 
139     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
140                                                                 /*numUniformSlots=*/0);
141     check(r, *program,
142 R"(    1. copy_constant                  $0 = 0x0000007B (1.723597e-43)
143     2. copy_constant                  $1 = 0xFFFFFFFF
144     3. case_op                        if ($0 == 0x0000007B) { LoopMask = true; $1 = false; }
145     4. case_op                        if ($0 == 0x0000007C) { LoopMask = true; $1 = false; }
146 )");
147 }
148 
DEF_TEST(RasterPipelineBuilderPushPopSrcDst,r)149 DEF_TEST(RasterPipelineBuilderPushPopSrcDst, r) {
150     // Create a very simple nonsense program.
151     SkSL::RP::Builder builder;
152 
153     builder.push_src_rgba();
154     builder.push_dst_rgba();
155     builder.push_src_rgba();
156     builder.pop_src_rgba();
157     builder.pop_dst_rgba();
158     builder.pop_src_rg();
159     builder.pop_src_rg();
160 
161     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
162                                                                 /*numUniformSlots=*/0);
163     check(r, *program,
164 R"(    1. store_src                      $0..3 = src.rgba
165     2. store_dst                      $4..7 = dst.rgba
166     3. store_src                      $8..11 = src.rgba
167     4. load_src                       src.rgba = $8..11
168     5. load_dst                       dst.rgba = $4..7
169     6. load_src_rg                    src.rg = $2..3
170     7. load_src_rg                    src.rg = $0..1
171 )");
172 }
173 
DEF_TEST(RasterPipelineBuilderInvokeChild,r)174 DEF_TEST(RasterPipelineBuilderInvokeChild, r) {
175     // Create a very simple nonsense program.
176     SkSL::RP::Builder builder;
177 
178     builder.invoke_shader(1);
179     builder.invoke_color_filter(2);
180     builder.invoke_blender(3);
181 
182     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
183                                                                 /*numUniformSlots=*/0);
184     check(r, *program,
185 R"(    1. invoke_shader                  invoke_shader 0x00000001
186     2. invoke_color_filter            invoke_color_filter 0x00000002
187     3. invoke_blender                 invoke_blender 0x00000003
188 )");
189 }
190 
DEF_TEST(RasterPipelineBuilderPushPopTempImmediates,r)191 DEF_TEST(RasterPipelineBuilderPushPopTempImmediates, r) {
192     // Create a very simple nonsense program.
193     SkSL::RP::Builder builder;
194     builder.set_current_stack(1);
195     builder.push_literal_i(999);                                          // push into 2
196     builder.set_current_stack(0);
197     builder.push_literal_f(13.5f);                                        // push into 0
198     builder.push_clone_from_stack(/*numSlots=*/1, /*otherStackIndex=*/1); // push into 1 from 2
199     builder.discard_stack();                                              // discard 2
200     builder.push_literal_u(357);                                          // push into 2
201     builder.set_current_stack(1);
202     builder.push_clone_from_stack(/*numSlots=*/1, /*otherStackIndex=*/0,
203                                   /*offsetFromStackTop=*/1);              // push into 3 from 0
204     builder.discard_stack(2);                                             // discard 2 and 3
205     builder.set_current_stack(0);
206     builder.discard_stack(2);                                             // discard 0 and 1
207     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/1,
208                                                                 /*numUniformSlots=*/0);
209     check(r, *program,
210 R"(    1. copy_constant                  $2 = 0x000003E7 (1.399897e-42)
211     2. copy_constant                  $0 = 0x41580000 (13.5)
212     3. copy_constant                  $1 = 0x00000165 (5.002636e-43)
213 )");
214 }
215 
DEF_TEST(RasterPipelineBuilderCopySlotsMasked,r)216 DEF_TEST(RasterPipelineBuilderCopySlotsMasked, r) {
217     // Create a very simple nonsense program.
218     SkSL::RP::Builder builder;
219     builder.copy_slots_masked(two_slots_at(0),  two_slots_at(2));
220     builder.copy_slots_masked(four_slots_at(1), four_slots_at(5));
221     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/9,
222                                                                 /*numUniformSlots=*/0);
223     check(r, *program,
224 R"(    1. copy_2_slots_masked            v0..1 = Mask(v2..3)
225     2. copy_4_slots_masked            v1..4 = Mask(v5..8)
226 )");
227 }
228 
DEF_TEST(RasterPipelineBuilderCopySlotsUnmasked,r)229 DEF_TEST(RasterPipelineBuilderCopySlotsUnmasked, r) {
230     // Create a very simple nonsense program.
231     SkSL::RP::Builder builder;
232     builder.copy_slots_unmasked(three_slots_at(0), three_slots_at(2));
233     builder.copy_slots_unmasked(five_slots_at(1),  five_slots_at(5));
234     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/10,
235                                                                 /*numUniformSlots=*/0);
236     check(r, *program,
237 R"(    1. copy_3_slots_unmasked          v0..2 = v2..4
238     2. copy_4_slots_unmasked          v1..4 = v5..8
239     3. copy_slot_unmasked             v5 = v9
240 )");
241 }
242 
DEF_TEST(RasterPipelineBuilderPushPopSlots,r)243 DEF_TEST(RasterPipelineBuilderPushPopSlots, r) {
244     // Create a very simple nonsense program.
245     SkSL::RP::Builder builder;
246     builder.push_slots(four_slots_at(10));           // push from 10~13 into $0~$3
247     builder.copy_stack_to_slots(one_slot_at(5), 3);  // copy from $1 into 5
248     builder.pop_slots_unmasked(two_slots_at(20));    // pop from $2~$3 into 20~21 (unmasked)
249     builder.enableExecutionMaskWrites();
250     builder.copy_stack_to_slots_unmasked(one_slot_at(4), 2);  // copy from $0 into 4
251     builder.push_slots(three_slots_at(30));          // push from 30~32 into $2~$4
252     builder.pop_slots(five_slots_at(0));             // pop from $0~$4 into 0~4 (masked)
253     builder.disableExecutionMaskWrites();
254 
255     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/50,
256                                                                 /*numUniformSlots=*/0);
257     check(r, *program,
258 R"(    1. copy_4_slots_unmasked          $0..3 = v10..13
259     2. copy_slot_unmasked             v5 = $1
260     3. copy_2_slots_unmasked          v20..21 = $2..3
261     4. copy_slot_unmasked             v4 = $0
262     5. copy_3_slots_unmasked          $2..4 = v30..32
263     6. copy_4_slots_masked            v0..3 = Mask($0..3)
264     7. copy_slot_masked               v4 = Mask($4)
265 )");
266 }
267 
DEF_TEST(RasterPipelineBuilderDuplicateSelectAndSwizzleSlots,r)268 DEF_TEST(RasterPipelineBuilderDuplicateSelectAndSwizzleSlots, r) {
269     // Create a very simple nonsense program.
270     SkSL::RP::Builder builder;
271     builder.push_literal_f(1.0f);           // push into 0
272     builder.push_duplicates(1);             // duplicate into 1
273     builder.push_duplicates(2);             // duplicate into 2~3
274     builder.push_duplicates(3);             // duplicate into 4~6
275     builder.push_duplicates(5);             // duplicate into 7~11
276     builder.select(4);                      // select from 4~7 and 8~11 into 4~7
277     builder.select(3);                      // select from 2~4 and 5~7 into 2~4
278     builder.select(1);                      // select from 3 and 4 into 3
279     builder.swizzle_copy_stack_to_slots(four_slots_at(1), {3, 2, 1, 0}, 4);
280     builder.swizzle_copy_stack_to_slots(four_slots_at(0), {0, 1, 3}, 3);
281     builder.swizzle(4, {3, 2, 1, 0});       // reverse the order of 0~3 (value.wzyx)
282     builder.swizzle(4, {1, 2});             // eliminate elements 0 and 3 (value.yz)
283     builder.swizzle(2, {0});                // eliminate element 1 (value.x)
284     builder.discard_stack(1);               // balance stack
285     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/6,
286                                                                 /*numUniformSlots=*/0);
287     check(r, *program,
288 R"(    1. copy_constant                  $0 = 0x3F800000 (1.0)
289     2. copy_slot_unmasked             $1 = $0
290     3. swizzle_3                      $1..3 = ($1..3).xxx
291     4. swizzle_4                      $3..6 = ($3..6).xxxx
292     5. swizzle_4                      $6..9 = ($6..9).xxxx
293     6. swizzle_3                      $9..11 = ($9..11).xxx
294     7. copy_4_slots_masked            $4..7 = Mask($8..11)
295     8. copy_3_slots_masked            $2..4 = Mask($5..7)
296     9. copy_slot_masked               $3 = Mask($4)
297    10. swizzle_copy_4_slots_masked    (v1..4).wzyx = Mask($0..3)
298    11. swizzle_copy_3_slots_masked    (v0..3).xyw = Mask($1..3)
299    12. swizzle_4                      $0..3 = ($0..3).wzyx
300    13. swizzle_2                      $0..1 = ($0..2).yz
301 )");
302 }
303 
DEF_TEST(RasterPipelineBuilderTransposeMatrix,r)304 DEF_TEST(RasterPipelineBuilderTransposeMatrix, r) {
305     // Create a very simple nonsense program.
306     SkSL::RP::Builder builder;
307     builder.push_literal_f(1.0f);           // push into 0
308     builder.push_duplicates(15);            // duplicate into 1~15
309     builder.transpose(2, 2);                // transpose a 2x2 matrix
310     builder.transpose(3, 3);                // transpose a 3x3 matrix
311     builder.transpose(4, 4);                // transpose a 4x4 matrix
312     builder.transpose(2, 4);                // transpose a 2x4 matrix
313     builder.transpose(4, 3);                // transpose a 4x3 matrix
314     builder.discard_stack(16);              // balance stack
315     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
316                                                                 /*numUniformSlots=*/0);
317     check(r, *program,
318 R"(    1. copy_constant                  $0 = 0x3F800000 (1.0)
319     2. swizzle_4                      $0..3 = ($0..3).xxxx
320     3. copy_4_slots_unmasked          $4..7 = $0..3
321     4. copy_4_slots_unmasked          $8..11 = $4..7
322     5. copy_4_slots_unmasked          $12..15 = $8..11
323     6. swizzle_3                      $13..15 = ($13..15).yxz
324     7. shuffle                        $8..15 = ($8..15)[2 5 0 3 6 1 4 7]
325     8. shuffle                        $1..15 = ($1..15)[3 7 11 0 4 8 12 1 5 9 13 2 6 10 14]
326     9. shuffle                        $9..15 = ($9..15)[3 0 4 1 5 2 6]
327    10. shuffle                        $5..15 = ($5..15)[2 5 8 0 3 6 9 1 4 7 10]
328 )");
329 }
330 
DEF_TEST(RasterPipelineBuilderDiagonalMatrix,r)331 DEF_TEST(RasterPipelineBuilderDiagonalMatrix, r) {
332     // Create a very simple nonsense program.
333     SkSL::RP::Builder builder;
334     builder.push_literal_f(0.0f);           // push into 0
335     builder.push_literal_f(1.0f);           // push into 1
336     builder.diagonal_matrix(2, 2);          // generate a 2x2 diagonal matrix
337     builder.discard_stack(4);               // balance stack
338     builder.push_literal_f(0.0f);           // push into 0
339     builder.push_literal_f(2.0f);           // push into 1
340     builder.diagonal_matrix(4, 4);          // generate a 4x4 diagonal matrix
341     builder.discard_stack(16);              // balance stack
342     builder.push_literal_f(0.0f);           // push into 0
343     builder.push_literal_f(3.0f);           // push into 1
344     builder.diagonal_matrix(2, 3);          // generate a 2x3 diagonal matrix
345     builder.discard_stack(6);               // balance stack
346     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
347                                                                 /*numUniformSlots=*/0);
348     check(r, *program,
349 R"(    1. zero_slot_unmasked             $0 = 0
350     2. copy_constant                  $1 = 0x3F800000 (1.0)
351     3. swizzle_4                      $0..3 = ($0..3).yxxy
352     4. zero_slot_unmasked             $0 = 0
353     5. copy_constant                  $1 = 0x40000000 (2.0)
354     6. shuffle                        $0..15 = ($0..15)[1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1]
355     7. zero_slot_unmasked             $0 = 0
356     8. copy_constant                  $1 = 0x40400000 (3.0)
357     9. shuffle                        $0..5 = ($0..5)[1 0 0 0 1 0]
358 )");
359 }
360 
DEF_TEST(RasterPipelineBuilderMatrixResize,r)361 DEF_TEST(RasterPipelineBuilderMatrixResize, r) {
362     // Create a very simple nonsense program.
363     SkSL::RP::Builder builder;
364     builder.push_literal_f(1.0f);           // synthesize a 2x2 matrix
365     builder.push_literal_f(2.0f);
366     builder.push_literal_f(3.0f);
367     builder.push_literal_f(4.0f);
368     builder.matrix_resize(2, 2, 4, 4);      // resize 2x2 matrix into 4x4
369     builder.matrix_resize(4, 4, 2, 2);      // resize 4x4 matrix back into 2x2
370     builder.matrix_resize(2, 2, 2, 4);      // resize 2x2 matrix into 2x4
371     builder.matrix_resize(2, 4, 4, 2);      // resize 2x4 matrix into 4x2
372     builder.matrix_resize(4, 2, 3, 3);      // resize 4x2 matrix into 3x3
373     builder.discard_stack(9);               // balance stack
374     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
375                                                                 /*numUniformSlots=*/0);
376     check(r, *program,
377 R"(    1. copy_constant                  $0 = 0x3F800000 (1.0)
378     2. copy_constant                  $1 = 0x40000000 (2.0)
379     3. copy_constant                  $2 = 0x40400000 (3.0)
380     4. copy_constant                  $3 = 0x40800000 (4.0)
381     5. zero_slot_unmasked             $4 = 0
382     6. copy_constant                  $5 = 0x3F800000 (1.0)
383     7. shuffle                        $2..15 = ($2..15)[2 2 0 1 2 2 2 2 3 2 2 2 2 3]
384     8. shuffle                        $2..3 = ($2..3)[2 3]
385     9. zero_slot_unmasked             $4 = 0
386    10. shuffle                        $2..7 = ($2..7)[2 2 0 1 2 2]
387    11. zero_slot_unmasked             $8 = 0
388    12. shuffle                        $2..7 = ($2..7)[2 3 6 6 6 6]
389    13. zero_slot_unmasked             $8 = 0
390    14. copy_constant                  $9 = 0x3F800000 (1.0)
391    15. shuffle                        $2..8 = ($2..8)[6 0 1 6 2 3 7]
392 )");
393 }
394 
DEF_TEST(RasterPipelineBuilderBranches,r)395 DEF_TEST(RasterPipelineBuilderBranches, r) {
396 #if SK_HAS_MUSTTAIL
397     // We have guaranteed tail-calling, and don't need to rewind the stack.
398     static constexpr char kExpectationWithKnownExecutionMask[] =
399 R"(    1. jump                           jump +8 (label 3 at #9)
400     2. label                          label 0x00000000
401     3. zero_slot_unmasked             v0 = 0
402     4. label                          label 0x00000001
403     5. zero_slot_unmasked             v1 = 0
404     6. label                          label 0x00000002
405     7. zero_slot_unmasked             v2 = 0
406     8. jump                           jump -6 (label 0 at #2)
407     9. label                          label 0x00000003
408    10. branch_if_no_active_lanes_eq   branch -4 (label 2 at #6) if no lanes of v2 == 0x00000000 (0.0)
409    11. branch_if_no_active_lanes_eq   branch -9 (label 0 at #2) if no lanes of v2 == 0x00000001 (1.401298e-45)
410 )";
411     static constexpr char kExpectationWithExecutionMaskWrites[] =
412 R"(    1. jump                           jump +9 (label 3 at #10)
413     2. label                          label 0x00000000
414     3. zero_slot_unmasked             v0 = 0
415     4. label                          label 0x00000001
416     5. zero_slot_unmasked             v1 = 0
417     6. branch_if_no_active_lanes      branch_if_no_active_lanes -2 (label 1 at #4)
418     7. label                          label 0x00000002
419     8. zero_slot_unmasked             v2 = 0
420     9. branch_if_any_active_lanes     branch_if_any_active_lanes -7 (label 0 at #2)
421    10. label                          label 0x00000003
422    11. branch_if_no_active_lanes_eq   branch -4 (label 2 at #7) if no lanes of v2 == 0x00000000 (0.0)
423    12. branch_if_no_active_lanes_eq   branch -10 (label 0 at #2) if no lanes of v2 == 0x00000001 (1.401298e-45)
424 )";
425 #else
426     // We don't have guaranteed tail-calling, so we rewind the stack immediately before any backward
427     // branches.
428     static constexpr char kExpectationWithKnownExecutionMask[] =
429 R"(    1. jump                           jump +9 (label 3 at #10)
430     2. label                          label 0x00000000
431     3. zero_slot_unmasked             v0 = 0
432     4. label                          label 0x00000001
433     5. zero_slot_unmasked             v1 = 0
434     6. label                          label 0x00000002
435     7. zero_slot_unmasked             v2 = 0
436     8. stack_rewind
437     9. jump                           jump -7 (label 0 at #2)
438    10. label                          label 0x00000003
439    11. stack_rewind
440    12. branch_if_no_active_lanes_eq   branch -6 (label 2 at #6) if no lanes of v2 == 0x00000000 (0.0)
441    13. stack_rewind
442    14. branch_if_no_active_lanes_eq   branch -12 (label 0 at #2) if no lanes of v2 == 0x00000001 (1.401298e-45)
443 )";
444     static constexpr char kExpectationWithExecutionMaskWrites[] =
445 R"(    1. jump                           jump +11 (label 3 at #12)
446     2. label                          label 0x00000000
447     3. zero_slot_unmasked             v0 = 0
448     4. label                          label 0x00000001
449     5. zero_slot_unmasked             v1 = 0
450     6. stack_rewind
451     7. branch_if_no_active_lanes      branch_if_no_active_lanes -3 (label 1 at #4)
452     8. label                          label 0x00000002
453     9. zero_slot_unmasked             v2 = 0
454    10. stack_rewind
455    11. branch_if_any_active_lanes     branch_if_any_active_lanes -9 (label 0 at #2)
456    12. label                          label 0x00000003
457    13. stack_rewind
458    14. branch_if_no_active_lanes_eq   branch -6 (label 2 at #8) if no lanes of v2 == 0x00000000 (0.0)
459    15. stack_rewind
460    16. branch_if_no_active_lanes_eq   branch -14 (label 0 at #2) if no lanes of v2 == 0x00000001 (1.401298e-45)
461 )";
462 #endif
463 
464     for (bool enableExecutionMaskWrites : {false, true}) {
465         // Create a very simple nonsense program.
466         SkSL::RP::Builder builder;
467         int label1 = builder.nextLabelID();
468         int label2 = builder.nextLabelID();
469         int label3 = builder.nextLabelID();
470         int label4 = builder.nextLabelID();
471 
472         if (enableExecutionMaskWrites) {
473             builder.enableExecutionMaskWrites();
474         }
475 
476         builder.jump(label4);
477         builder.label(label1);
478         builder.zero_slots_unmasked(one_slot_at(0));
479         builder.label(label2);
480         builder.zero_slots_unmasked(one_slot_at(1));
481         builder.branch_if_no_active_lanes(label2);
482         builder.branch_if_no_active_lanes(label3);
483         builder.label(label3);
484         builder.zero_slots_unmasked(one_slot_at(2));
485         builder.branch_if_any_active_lanes(label1);
486         builder.branch_if_any_active_lanes(label1);
487         builder.label(label4);
488         builder.branch_if_no_active_lanes_on_stack_top_equal(0, label3);
489         builder.branch_if_no_active_lanes_on_stack_top_equal(0, label2);
490         builder.branch_if_no_active_lanes_on_stack_top_equal(1, label1);
491         builder.branch_if_no_active_lanes_on_stack_top_equal(1, label4);
492 
493         if (enableExecutionMaskWrites) {
494             builder.disableExecutionMaskWrites();
495         }
496 
497         std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/3,
498                                                                     /*numUniformSlots=*/0);
499 
500         check(r, *program, enableExecutionMaskWrites ? kExpectationWithExecutionMaskWrites
501                                                      : kExpectationWithKnownExecutionMask);
502     }
503 }
504 
DEF_TEST(RasterPipelineBuilderBinaryFloatOps,r)505 DEF_TEST(RasterPipelineBuilderBinaryFloatOps, r) {
506     using BuilderOp = SkSL::RP::BuilderOp;
507 
508     SkSL::RP::Builder builder;
509     builder.push_literal_f(10.0f);
510     builder.push_duplicates(30);
511     builder.binary_op(BuilderOp::add_n_floats, 1);
512     builder.binary_op(BuilderOp::sub_n_floats, 2);
513     builder.binary_op(BuilderOp::mul_n_floats, 3);
514     builder.binary_op(BuilderOp::div_n_floats, 4);
515     builder.binary_op(BuilderOp::max_n_floats, 3);
516     builder.binary_op(BuilderOp::min_n_floats, 2);
517     builder.binary_op(BuilderOp::cmplt_n_floats, 5);
518     builder.binary_op(BuilderOp::cmple_n_floats, 4);
519     builder.binary_op(BuilderOp::cmpeq_n_floats, 3);
520     builder.binary_op(BuilderOp::cmpne_n_floats, 2);
521     builder.discard_stack(2);
522     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
523                                                                 /*numUniformSlots=*/0);
524     check(r, *program,
525 R"(    1. copy_constant                  $0 = 0x41200000 (10.0)
526     2. swizzle_4                      $0..3 = ($0..3).xxxx
527     3. copy_4_slots_unmasked          $4..7 = $0..3
528     4. copy_4_slots_unmasked          $8..11 = $4..7
529     5. copy_4_slots_unmasked          $12..15 = $8..11
530     6. copy_4_slots_unmasked          $16..19 = $12..15
531     7. copy_4_slots_unmasked          $20..23 = $16..19
532     8. copy_4_slots_unmasked          $24..27 = $20..23
533     9. swizzle_4                      $27..30 = ($27..30).xxxx
534    10. add_float                      $29 += $30
535    11. sub_2_floats                   $26..27 -= $28..29
536    12. mul_3_floats                   $22..24 *= $25..27
537    13. div_4_floats                   $17..20 /= $21..24
538    14. max_3_floats                   $15..17 = max($15..17, $18..20)
539    15. min_2_floats                   $14..15 = min($14..15, $16..17)
540    16. cmplt_n_floats                 $6..10 = lessThan($6..10, $11..15)
541    17. cmple_4_floats                 $3..6 = lessThanEqual($3..6, $7..10)
542    18. cmpeq_3_floats                 $1..3 = equal($1..3, $4..6)
543    19. cmpne_2_floats                 $0..1 = notEqual($0..1, $2..3)
544 )");
545 }
546 
DEF_TEST(RasterPipelineBuilderBinaryIntOps,r)547 DEF_TEST(RasterPipelineBuilderBinaryIntOps, r) {
548     using BuilderOp = SkSL::RP::BuilderOp;
549 
550     SkSL::RP::Builder builder;
551     builder.push_literal_i(123);
552     builder.push_duplicates(40);
553     builder.binary_op(BuilderOp::bitwise_and_n_ints, 1);
554     builder.binary_op(BuilderOp::bitwise_xor_n_ints, 2);
555     builder.binary_op(BuilderOp::bitwise_or_n_ints, 3);
556     builder.binary_op(BuilderOp::add_n_ints, 2);
557     builder.binary_op(BuilderOp::sub_n_ints, 3);
558     builder.binary_op(BuilderOp::mul_n_ints, 4);
559     builder.binary_op(BuilderOp::div_n_ints, 5);
560     builder.binary_op(BuilderOp::max_n_ints, 4);
561     builder.binary_op(BuilderOp::min_n_ints, 3);
562     builder.binary_op(BuilderOp::cmplt_n_ints, 1);
563     builder.binary_op(BuilderOp::cmple_n_ints, 2);
564     builder.binary_op(BuilderOp::cmpeq_n_ints, 3);
565     builder.binary_op(BuilderOp::cmpne_n_ints, 4);
566     builder.discard_stack(4);
567     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
568                                                                 /*numUniformSlots=*/0);
569     check(r, *program,
570 R"(    1. copy_constant                  $0 = 0x0000007B (1.723597e-43)
571     2. swizzle_4                      $0..3 = ($0..3).xxxx
572     3. copy_4_slots_unmasked          $4..7 = $0..3
573     4. copy_4_slots_unmasked          $8..11 = $4..7
574     5. copy_4_slots_unmasked          $12..15 = $8..11
575     6. copy_4_slots_unmasked          $16..19 = $12..15
576     7. copy_4_slots_unmasked          $20..23 = $16..19
577     8. copy_4_slots_unmasked          $24..27 = $20..23
578     9. copy_4_slots_unmasked          $28..31 = $24..27
579    10. copy_4_slots_unmasked          $32..35 = $28..31
580    11. copy_4_slots_unmasked          $36..39 = $32..35
581    12. copy_slot_unmasked             $40 = $39
582    13. bitwise_and_int                $39 &= $40
583    14. bitwise_xor_2_ints             $36..37 ^= $38..39
584    15. bitwise_or_3_ints              $32..34 |= $35..37
585    16. add_2_ints                     $31..32 += $33..34
586    17. sub_3_ints                     $27..29 -= $30..32
587    18. mul_4_ints                     $22..25 *= $26..29
588    19. div_n_ints                     $16..20 /= $21..25
589    20. max_4_ints                     $13..16 = max($13..16, $17..20)
590    21. min_3_ints                     $11..13 = min($11..13, $14..16)
591    22. cmplt_int                      $12 = lessThan($12, $13)
592    23. cmple_2_ints                   $9..10 = lessThanEqual($9..10, $11..12)
593    24. cmpeq_3_ints                   $5..7 = equal($5..7, $8..10)
594    25. cmpne_4_ints                   $0..3 = notEqual($0..3, $4..7)
595 )");
596 }
597 
DEF_TEST(RasterPipelineBuilderBinaryUIntOps,r)598 DEF_TEST(RasterPipelineBuilderBinaryUIntOps, r) {
599     using BuilderOp = SkSL::RP::BuilderOp;
600 
601     SkSL::RP::Builder builder;
602     builder.push_literal_u(456);
603     builder.push_duplicates(21);
604     builder.binary_op(BuilderOp::div_n_uints, 6);
605     builder.binary_op(BuilderOp::cmplt_n_uints, 5);
606     builder.binary_op(BuilderOp::cmple_n_uints, 4);
607     builder.binary_op(BuilderOp::max_n_uints, 3);
608     builder.binary_op(BuilderOp::min_n_uints, 2);
609     builder.discard_stack(2);
610     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
611                                                                 /*numUniformSlots=*/0);
612     check(r, *program,
613 R"(    1. copy_constant                  $0 = 0x000001C8 (6.389921e-43)
614     2. swizzle_4                      $0..3 = ($0..3).xxxx
615     3. copy_4_slots_unmasked          $4..7 = $0..3
616     4. copy_4_slots_unmasked          $8..11 = $4..7
617     5. copy_4_slots_unmasked          $12..15 = $8..11
618     6. copy_4_slots_unmasked          $16..19 = $12..15
619     7. swizzle_3                      $19..21 = ($19..21).xxx
620     8. div_n_uints                    $10..15 /= $16..21
621     9. cmplt_n_uints                  $6..10 = lessThan($6..10, $11..15)
622    10. cmple_4_uints                  $3..6 = lessThanEqual($3..6, $7..10)
623    11. max_3_uints                    $1..3 = max($1..3, $4..6)
624    12. min_2_uints                    $0..1 = min($0..1, $2..3)
625 )");
626 }
627 
DEF_TEST(RasterPipelineBuilderUnaryOps,r)628 DEF_TEST(RasterPipelineBuilderUnaryOps, r) {
629     using BuilderOp = SkSL::RP::BuilderOp;
630 
631     SkSL::RP::Builder builder;
632     builder.push_literal_i(456);
633     builder.push_duplicates(4);
634     builder.unary_op(BuilderOp::cast_to_float_from_int, 1);
635     builder.unary_op(BuilderOp::cast_to_float_from_uint, 2);
636     builder.unary_op(BuilderOp::cast_to_int_from_float, 3);
637     builder.unary_op(BuilderOp::cast_to_uint_from_float, 4);
638     builder.unary_op(BuilderOp::bitwise_not_int, 5);
639     builder.unary_op(BuilderOp::cos_float, 4);
640     builder.unary_op(BuilderOp::tan_float, 3);
641     builder.unary_op(BuilderOp::sin_float, 2);
642     builder.unary_op(BuilderOp::sqrt_float, 1);
643     builder.unary_op(BuilderOp::abs_float, 2);
644     builder.unary_op(BuilderOp::abs_int, 3);
645     builder.unary_op(BuilderOp::floor_float, 4);
646     builder.unary_op(BuilderOp::ceil_float, 5);
647     builder.discard_stack(5);
648     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
649                                                                 /*numUniformSlots=*/0);
650     check(r, *program,
651 R"(    1. copy_constant                  $0 = 0x000001C8 (6.389921e-43)
652     2. swizzle_4                      $0..3 = ($0..3).xxxx
653     3. copy_slot_unmasked             $4 = $3
654     4. cast_to_float_from_int         $4 = IntToFloat($4)
655     5. cast_to_float_from_2_uints     $3..4 = UintToFloat($3..4)
656     6. cast_to_int_from_3_floats      $2..4 = FloatToInt($2..4)
657     7. cast_to_uint_from_4_floats     $1..4 = FloatToUint($1..4)
658     8. bitwise_not_4_ints             $0..3 = ~$0..3
659     9. bitwise_not_int                $4 = ~$4
660    10. cos_float                      $1 = cos($1)
661    11. cos_float                      $2 = cos($2)
662    12. cos_float                      $3 = cos($3)
663    13. cos_float                      $4 = cos($4)
664    14. tan_float                      $2 = tan($2)
665    15. tan_float                      $3 = tan($3)
666    16. tan_float                      $4 = tan($4)
667    17. sin_float                      $3 = sin($3)
668    18. sin_float                      $4 = sin($4)
669    19. sqrt_float                     $4 = sqrt($4)
670    20. abs_2_floats                   $3..4 = abs($3..4)
671    21. abs_3_ints                     $2..4 = abs($2..4)
672    22. floor_4_floats                 $1..4 = floor($1..4)
673    23. ceil_4_floats                  $0..3 = ceil($0..3)
674    24. ceil_float                     $4 = ceil($4)
675 )");
676 }
677 
DEF_TEST(RasterPipelineBuilderUniforms,r)678 DEF_TEST(RasterPipelineBuilderUniforms, r) {
679     using BuilderOp = SkSL::RP::BuilderOp;
680 
681     // Create a very simple nonsense program.
682     SkSL::RP::Builder builder;
683     builder.push_uniform(one_slot_at(0));        // push into 0
684     builder.push_uniform(two_slots_at(1));       // push into 1~2
685     builder.push_uniform(three_slots_at(3));     // push into 3~5
686     builder.push_uniform(four_slots_at(6));      // push into 6~9
687     builder.push_uniform(five_slots_at(0));      // push into 10~14
688     builder.unary_op(BuilderOp::abs_int, 1);     // perform work so the program isn't eliminated
689     builder.discard_stack(15);                   // balance stack
690     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
691                                                                 /*numUniformSlots=*/10);
692     check(r, *program,
693 R"(    1. copy_4_constants               $0..3 = u0..3
694     2. copy_4_constants               $4..7 = u4..7
695     3. copy_2_constants               $8..9 = u8..9
696     4. copy_4_constants               $10..13 = u0..3
697     5. copy_constant                  $14 = u4
698     6. abs_int                        $14 = abs($14)
699 )");
700 }
701 
DEF_TEST(RasterPipelineBuilderPushZeros,r)702 DEF_TEST(RasterPipelineBuilderPushZeros, r) {
703     using BuilderOp = SkSL::RP::BuilderOp;
704 
705     // Create a very simple nonsense program.
706     SkSL::RP::Builder builder;
707     builder.push_zeros(1);                    // push into 0
708     builder.push_zeros(2);                    // push into 1~2
709     builder.push_zeros(3);                    // push into 3~5
710     builder.push_zeros(4);                    // push into 6~9
711     builder.push_zeros(5);                    // push into 10~14
712     builder.unary_op(BuilderOp::abs_int, 1);  // perform work so the program isn't eliminated
713     builder.discard_stack(15);                // balance stack
714     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
715                                                                 /*numUniformSlots=*/10);
716     check(r, *program,
717 R"(    1. zero_4_slots_unmasked          $0..3 = 0
718     2. zero_4_slots_unmasked          $4..7 = 0
719     3. zero_4_slots_unmasked          $8..11 = 0
720     4. zero_3_slots_unmasked          $12..14 = 0
721     5. abs_int                        $14 = abs($14)
722 )");
723 }
724 
DEF_TEST(RasterPipelineBuilderTernaryFloatOps,r)725 DEF_TEST(RasterPipelineBuilderTernaryFloatOps, r) {
726     using BuilderOp = SkSL::RP::BuilderOp;
727 
728     SkSL::RP::Builder builder;
729     builder.push_literal_f(0.75f);
730     builder.push_duplicates(8);
731     builder.ternary_op(BuilderOp::mix_n_floats, 3);
732     builder.discard_stack(3);
733     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
734                                                                 /*numUniformSlots=*/0);
735     check(r, *program,
736 R"(    1. copy_constant                  $0 = 0x3F400000 (0.75)
737     2. swizzle_4                      $0..3 = ($0..3).xxxx
738     3. copy_4_slots_unmasked          $4..7 = $0..3
739     4. copy_slot_unmasked             $8 = $7
740     5. mix_3_floats                   $0..2 = mix($3..5, $6..8, $0..2)
741 )");
742 }
743 
DEF_TEST(RasterPipelineBuilderAutomaticStackRewinding,r)744 DEF_TEST(RasterPipelineBuilderAutomaticStackRewinding, r) {
745     using BuilderOp = SkSL::RP::BuilderOp;
746 
747     SkSL::RP::Builder builder;
748     builder.push_literal_i(1);
749     builder.push_duplicates(2000);
750     builder.unary_op(BuilderOp::abs_int, 1);  // perform work so the program isn't eliminated
751     builder.discard_stack(2001);
752     std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
753                                                                 /*numUniformSlots=*/0);
754     sk_sp<SkData> dump = get_program_dump(*program);
755 
756 #if SK_HAS_MUSTTAIL
757     // We have guaranteed tail-calling, so we never use `stack_rewind`.
758     REPORTER_ASSERT(r, !skstd::contains(as_string_view(dump), "stack_rewind"));
759 #else
760     // We can't guarantee tail-calling, so we should automatically insert `stack_rewind` stages into
761     // long programs.
762     REPORTER_ASSERT(r, skstd::contains(as_string_view(dump), "stack_rewind"));
763 #endif
764 }
765