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/SkRasterPipeline.h"
12 #include "src/sksl/codegen/SkSLRasterPipelineBuilder.h"
13 #include "src/sksl/tracing/SkSLDebugTracePriv.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(const sk_sp<SkData> & dump)22 static std::string_view as_string_view(const 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
ten_slots_at(SkSL::RP::Slot index)54 static SkSL::RP::SlotRange ten_slots_at(SkSL::RP::Slot index) {
55 return SkSL::RP::SlotRange{index, 10};
56 }
57
DEF_TEST(RasterPipelineBuilder,r)58 DEF_TEST(RasterPipelineBuilder, r) {
59 // Create a very simple nonsense program.
60 SkSL::RP::Builder builder;
61 builder.store_src_rg(two_slots_at(0));
62 builder.store_src(four_slots_at(2));
63 builder.store_dst(four_slots_at(4));
64 builder.store_device_xy01(four_slots_at(6));
65 builder.init_lane_masks();
66 builder.enableExecutionMaskWrites();
67 builder.mask_off_return_mask();
68 builder.mask_off_loop_mask();
69 builder.reenable_loop_mask(one_slot_at(4));
70 builder.disableExecutionMaskWrites();
71 builder.load_src(four_slots_at(1));
72 builder.load_dst(four_slots_at(3));
73 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/10,
74 /*numUniformSlots=*/0,
75 /*numImmutableSlots=*/0);
76 check(r, *program,
77 R"(store_src_rg v0..1 = src.rg
78 store_src v2..5 = src.rgba
79 store_dst v4..7 = dst.rgba
80 store_device_xy01 v6..9 = DeviceCoords.xy01
81 init_lane_masks CondMask = LoopMask = RetMask = true
82 mask_off_return_mask RetMask &= ~(CondMask & LoopMask & RetMask)
83 mask_off_loop_mask LoopMask &= ~(CondMask & LoopMask & RetMask)
84 reenable_loop_mask LoopMask |= v4
85 load_src src.rgba = v1..4
86 load_dst dst.rgba = v3..6
87 )");
88 }
89
DEF_TEST(RasterPipelineBuilderPushPopMaskRegisters,r)90 DEF_TEST(RasterPipelineBuilderPushPopMaskRegisters, r) {
91 // Create a very simple nonsense program.
92 SkSL::RP::Builder builder;
93
94 REPORTER_ASSERT(r, !builder.executionMaskWritesAreEnabled());
95 builder.enableExecutionMaskWrites();
96 REPORTER_ASSERT(r, builder.executionMaskWritesAreEnabled());
97
98 builder.push_condition_mask(); // push into 0
99 builder.push_loop_mask(); // push into 1
100 builder.push_return_mask(); // push into 2
101 builder.merge_condition_mask(); // set the condition-mask to 1 & 2
102 builder.merge_inv_condition_mask(); // set the condition-mask to 1 & ~2
103 builder.pop_condition_mask(); // pop from 2
104 builder.merge_loop_mask(); // mask off the loop-mask against 1
105 builder.push_condition_mask(); // push into 2
106 builder.pop_condition_mask(); // pop from 2
107 builder.pop_loop_mask(); // pop from 1
108 builder.pop_return_mask(); // pop from 0
109 builder.push_condition_mask(); // push into 0
110 builder.pop_and_reenable_loop_mask(); // pop from 0
111
112 REPORTER_ASSERT(r, builder.executionMaskWritesAreEnabled());
113 builder.disableExecutionMaskWrites();
114 REPORTER_ASSERT(r, !builder.executionMaskWritesAreEnabled());
115
116 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
117 /*numUniformSlots=*/0,
118 /*numImmutableSlots=*/0);
119 check(r, *program,
120 R"(store_condition_mask $0 = CondMask
121 store_loop_mask $1 = LoopMask
122 store_return_mask $2 = RetMask
123 merge_condition_mask CondMask = $1 & $2
124 merge_inv_condition_mask CondMask = $1 & ~$2
125 load_condition_mask CondMask = $2
126 merge_loop_mask LoopMask &= $1
127 store_condition_mask $2 = CondMask
128 load_condition_mask CondMask = $2
129 load_loop_mask LoopMask = $1
130 load_return_mask RetMask = $0
131 store_condition_mask $0 = CondMask
132 reenable_loop_mask LoopMask |= $0
133 )");
134 }
135
136
DEF_TEST(RasterPipelineBuilderCaseOp,r)137 DEF_TEST(RasterPipelineBuilderCaseOp, r) {
138 // Create a very simple nonsense program.
139 SkSL::RP::Builder builder;
140
141 builder.push_constant_i(123); // push a test value
142 builder.push_constant_i(~0); // push an all-on default mask
143 builder.case_op(123); // do `case 123:`
144 builder.case_op(124); // do `case 124:`
145 builder.discard_stack(2);
146
147 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
148 /*numUniformSlots=*/0,
149 /*numImmutableSlots=*/0);
150 check(r, *program,
151 R"(copy_constant $0 = 0x0000007B (1.723597e-43)
152 copy_constant $1 = 0xFFFFFFFF
153 case_op if ($0 == 0x0000007B) { LoopMask = true; $1 = false; }
154 case_op if ($0 == 0x0000007C) { LoopMask = true; $1 = false; }
155 )");
156 }
157
DEF_TEST(RasterPipelineBuilderPushPopSrcDst,r)158 DEF_TEST(RasterPipelineBuilderPushPopSrcDst, r) {
159 // Create a very simple nonsense program.
160 SkSL::RP::Builder builder;
161
162 builder.push_src_rgba();
163 builder.push_dst_rgba();
164 builder.pop_src_rgba();
165 builder.exchange_src();
166 builder.exchange_src();
167 builder.exchange_src();
168 builder.pop_dst_rgba();
169
170 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
171 /*numUniformSlots=*/0,
172 /*numImmutableSlots=*/0);
173 check(r, *program,
174 R"(store_src $0..3 = src.rgba
175 store_dst $4..7 = dst.rgba
176 load_src src.rgba = $4..7
177 exchange_src swap(src.rgba, $0..3)
178 load_dst dst.rgba = $0..3
179 )");
180 }
181
DEF_TEST(RasterPipelineBuilderInvokeChild,r)182 DEF_TEST(RasterPipelineBuilderInvokeChild, r) {
183 // Create a very simple nonsense program.
184 SkSL::RP::Builder builder;
185
186 builder.invoke_shader(1);
187 builder.invoke_color_filter(2);
188 builder.invoke_blender(3);
189
190 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
191 /*numUniformSlots=*/0,
192 /*numImmutableSlots=*/0);
193 check(r, *program,
194 R"(invoke_shader invoke_shader 0x00000001
195 invoke_color_filter invoke_color_filter 0x00000002
196 invoke_blender invoke_blender 0x00000003
197 )");
198 }
199
DEF_TEST(RasterPipelineBuilderPushPopTempImmediates,r)200 DEF_TEST(RasterPipelineBuilderPushPopTempImmediates, r) {
201 // Create a very simple nonsense program.
202 SkSL::RP::Builder builder;
203 builder.set_current_stack(1);
204 builder.push_constant_i(999); // push into 2
205 builder.set_current_stack(0);
206 builder.push_constant_f(13.5f); // push into 0
207 builder.push_clone_from_stack(one_slot_at(0), /*otherStackID=*/1, /*offsetFromStackTop=*/1);
208 // push into 1 from 2
209 builder.discard_stack(1); // discard 2
210 builder.push_constant_u(357); // push into 2
211 builder.set_current_stack(1);
212 builder.push_clone_from_stack(one_slot_at(0), /*otherStackID=*/0, /*offsetFromStackTop=*/1);
213 // push into 3 from 0
214 builder.discard_stack(2); // discard 2 and 3
215 builder.set_current_stack(0);
216 builder.push_constant_f(1.2f); // push into 2
217 builder.pad_stack(3); // pad slots 3,4,5
218 builder.push_constant_f(3.4f); // push into 6
219 builder.discard_stack(7); // discard 0 through 6
220 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/1,
221 /*numUniformSlots=*/0,
222 /*numImmutableSlots=*/0);
223 check(r, *program,
224 R"(copy_constant $2 = 0x000003E7 (1.399897e-42)
225 copy_constant $0 = 0x41580000 (13.5)
226 copy_constant $1 = 0x00000165 (5.002636e-43)
227 )");
228 }
229
DEF_TEST(RasterPipelineBuilderPushPopIndirect,r)230 DEF_TEST(RasterPipelineBuilderPushPopIndirect, r) {
231 // Create a very simple nonsense program.
232 SkSL::RP::Builder builder;
233 builder.set_current_stack(1);
234 builder.push_constant_i(3);
235 builder.set_current_stack(0);
236 builder.push_slots_indirect(two_slots_at(0), /*dynamicStack=*/1, ten_slots_at(0));
237 builder.push_slots_indirect(four_slots_at(10), /*dynamicStack=*/1, ten_slots_at(10));
238 builder.push_uniform_indirect(one_slot_at(0), /*dynamicStack=*/1, five_slots_at(0));
239 builder.push_uniform_indirect(three_slots_at(5), /*dynamicStack=*/1, five_slots_at(5));
240 builder.swizzle_copy_stack_to_slots_indirect(three_slots_at(6), /*dynamicStackID=*/1,
241 ten_slots_at(0), {2, 1, 0},
242 /*offsetFromStackTop=*/3);
243 builder.copy_stack_to_slots_indirect(three_slots_at(4), /*dynamicStackID=*/1, ten_slots_at(0));
244 builder.pop_slots_indirect(five_slots_at(0), /*dynamicStackID=*/1, ten_slots_at(0));
245 builder.pop_slots_indirect(five_slots_at(10), /*dynamicStackID=*/1, ten_slots_at(10));
246 builder.set_current_stack(1);
247 builder.discard_stack(1);
248 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/20,
249 /*numUniformSlots=*/10,
250 /*numImmutableSlots=*/0);
251 check(r, *program,
252 R"(copy_constant $10 = 0x00000003 (4.203895e-45)
253 copy_from_indirect_unmasked $0..1 = Indirect(v0..1 + $10)
254 copy_from_indirect_unmasked $2..5 = Indirect(v10..13 + $10)
255 copy_from_indirect_uniform_unm $6 = Indirect(u0 + $10)
256 copy_from_indirect_uniform_unm $7..9 = Indirect(u5..7 + $10)
257 swizzle_copy_to_indirect_maske Indirect(v6..8 + $10).zyx = Mask($7..9)
258 copy_to_indirect_masked Indirect(v4..6 + $10) = Mask($7..9)
259 copy_to_indirect_masked Indirect(v0..4 + $10) = Mask($5..9)
260 copy_to_indirect_masked Indirect(v10..14 + $10) = Mask($0..4)
261 )");
262 }
263
DEF_TEST(RasterPipelineBuilderCopySlotsMasked,r)264 DEF_TEST(RasterPipelineBuilderCopySlotsMasked, r) {
265 // Create a very simple nonsense program.
266 SkSL::RP::Builder builder;
267 builder.copy_slots_masked(two_slots_at(0), two_slots_at(2));
268 builder.copy_slots_masked(four_slots_at(1), four_slots_at(5));
269 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/9,
270 /*numUniformSlots=*/0,
271 /*numImmutableSlots=*/0);
272 check(r, *program,
273 R"(copy_2_slots_masked v0..1 = Mask(v2..3)
274 copy_4_slots_masked v1..4 = Mask(v5..8)
275 )");
276 }
277
DEF_TEST(RasterPipelineBuilderCopySlotsUnmasked,r)278 DEF_TEST(RasterPipelineBuilderCopySlotsUnmasked, r) {
279 // Create a very simple nonsense program.
280 SkSL::RP::Builder builder;
281 builder.copy_slots_unmasked(three_slots_at(0), three_slots_at(2));
282 builder.copy_slots_unmasked(five_slots_at(1), five_slots_at(5));
283 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/10,
284 /*numUniformSlots=*/0,
285 /*numImmutableSlots=*/0);
286 check(r, *program,
287 R"(copy_3_slots_unmasked v0..2 = v2..4
288 copy_4_slots_unmasked v1..4 = v5..8
289 copy_slot_unmasked v5 = v9
290 )");
291 }
292
DEF_TEST(RasterPipelineBuilderPushPopSlots,r)293 DEF_TEST(RasterPipelineBuilderPushPopSlots, r) {
294 // Create a very simple nonsense program.
295 SkSL::RP::Builder builder;
296 builder.push_slots(four_slots_at(10)); // push from 10~13 into $0~$3
297 builder.copy_stack_to_slots(one_slot_at(5), 3); // copy from $1 into 5
298 builder.pop_slots_unmasked(two_slots_at(20)); // pop from $2~$3 into 20~21 (unmasked)
299 builder.enableExecutionMaskWrites();
300 builder.copy_stack_to_slots_unmasked(one_slot_at(4), 2); // copy from $0 into 4
301 builder.push_slots(three_slots_at(30)); // push from 30~32 into $2~$4
302 builder.pop_slots(five_slots_at(0)); // pop from $0~$4 into 0~4 (masked)
303 builder.disableExecutionMaskWrites();
304
305 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/50,
306 /*numUniformSlots=*/0,
307 /*numImmutableSlots=*/0);
308 check(r, *program,
309 R"(copy_4_slots_unmasked $0..3 = v10..13
310 copy_slot_unmasked v5 = $1
311 copy_2_slots_unmasked v20..21 = $2..3
312 copy_slot_unmasked v4 = $0
313 copy_3_slots_unmasked $2..4 = v30..32
314 copy_4_slots_masked v0..3 = Mask($0..3)
315 copy_slot_masked v4 = Mask($4)
316 )");
317 }
318
DEF_TEST(RasterPipelineBuilderDuplicateSelectAndSwizzleSlots,r)319 DEF_TEST(RasterPipelineBuilderDuplicateSelectAndSwizzleSlots, r) {
320 // Create a very simple nonsense program.
321 SkSL::RP::Builder builder;
322 builder.push_constant_f(1.0f); // push into 0
323 builder.push_duplicates(1); // duplicate into 1
324 builder.push_duplicates(2); // duplicate into 2~3
325 builder.push_duplicates(3); // duplicate into 4~6
326 builder.push_duplicates(5); // duplicate into 7~11
327 builder.select(4); // select from 4~7 and 8~11 into 4~7
328 builder.select(3); // select from 2~4 and 5~7 into 2~4
329 builder.select(1); // select from 3 and 4 into 3
330 builder.swizzle_copy_stack_to_slots(four_slots_at(1), {3, 2, 1, 0}, 4);
331 builder.swizzle_copy_stack_to_slots(four_slots_at(0), {0, 1, 3}, 3);
332 builder.swizzle(4, {3, 2, 1, 0}); // reverse the order of 0~3 (value.wzyx)
333 builder.swizzle(4, {1, 2}); // eliminate elements 0 and 3 (value.yz)
334 builder.swizzle(2, {0}); // eliminate element 1 (value.x)
335 builder.discard_stack(1); // balance stack
336 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/6,
337 /*numUniformSlots=*/0,
338 /*numImmutableSlots=*/0);
339 check(r, *program,
340 R"(splat_4_constants $0..3 = 0x3F800000 (1.0)
341 splat_4_constants $4..7 = 0x3F800000 (1.0)
342 splat_4_constants $8..11 = 0x3F800000 (1.0)
343 copy_4_slots_masked $4..7 = Mask($8..11)
344 copy_3_slots_masked $2..4 = Mask($5..7)
345 copy_slot_masked $3 = Mask($4)
346 swizzle_copy_4_slots_masked (v1..4).wzyx = Mask($0..3)
347 swizzle_copy_3_slots_masked (v0..3).xyw = Mask($1..3)
348 swizzle_4 $0..3 = ($0..3).wzyx
349 swizzle_2 $0..1 = ($0..2).yz
350 )");
351 }
352
DEF_TEST(RasterPipelineBuilderTransposeMatrix,r)353 DEF_TEST(RasterPipelineBuilderTransposeMatrix, r) {
354 // Create a very simple nonsense program.
355 SkSL::RP::Builder builder;
356 builder.push_constant_f(1.0f); // push into 0
357 builder.push_duplicates(15); // duplicate into 1~15
358 builder.transpose(2, 2); // transpose a 2x2 matrix
359 builder.transpose(3, 3); // transpose a 3x3 matrix
360 builder.transpose(4, 4); // transpose a 4x4 matrix
361 builder.transpose(2, 4); // transpose a 2x4 matrix
362 builder.transpose(4, 3); // transpose a 4x3 matrix
363 builder.discard_stack(16); // balance stack
364 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
365 /*numUniformSlots=*/0,
366 /*numImmutableSlots=*/0);
367 check(r, *program,
368 R"(splat_4_constants $0..3 = 0x3F800000 (1.0)
369 splat_4_constants $4..7 = 0x3F800000 (1.0)
370 splat_4_constants $8..11 = 0x3F800000 (1.0)
371 splat_4_constants $12..15 = 0x3F800000 (1.0)
372 swizzle_3 $13..15 = ($13..15).yxz
373 shuffle $8..15 = ($8..15)[2 5 0 3 6 1 4 7]
374 shuffle $1..15 = ($1..15)[3 7 11 0 4 8 12 1 5 9 13 2 6 10 14]
375 shuffle $9..15 = ($9..15)[3 0 4 1 5 2 6]
376 shuffle $5..15 = ($5..15)[2 5 8 0 3 6 9 1 4 7 10]
377 )");
378 }
379
DEF_TEST(RasterPipelineBuilderDiagonalMatrix,r)380 DEF_TEST(RasterPipelineBuilderDiagonalMatrix, r) {
381 // Create a very simple nonsense program.
382 SkSL::RP::Builder builder;
383 builder.push_constant_f(0.0f); // push into 0
384 builder.push_constant_f(1.0f); // push into 1
385 builder.diagonal_matrix(2, 2); // generate a 2x2 diagonal matrix
386 builder.discard_stack(4); // balance stack
387 builder.push_constant_f(0.0f); // push into 0
388 builder.push_constant_f(2.0f); // push into 1
389 builder.diagonal_matrix(4, 4); // generate a 4x4 diagonal matrix
390 builder.discard_stack(16); // balance stack
391 builder.push_constant_f(0.0f); // push into 0
392 builder.push_constant_f(3.0f); // push into 1
393 builder.diagonal_matrix(2, 3); // generate a 2x3 diagonal matrix
394 builder.discard_stack(6); // balance stack
395 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
396 /*numUniformSlots=*/0,
397 /*numImmutableSlots=*/0);
398 check(r, *program,
399 R"(copy_constant $0 = 0
400 copy_constant $1 = 0x3F800000 (1.0)
401 swizzle_4 $0..3 = ($0..3).yxxy
402 copy_constant $0 = 0
403 copy_constant $1 = 0x40000000 (2.0)
404 shuffle $0..15 = ($0..15)[1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1]
405 copy_constant $0 = 0
406 copy_constant $1 = 0x40400000 (3.0)
407 shuffle $0..5 = ($0..5)[1 0 0 0 1 0]
408 )");
409 }
410
DEF_TEST(RasterPipelineBuilderMatrixResize,r)411 DEF_TEST(RasterPipelineBuilderMatrixResize, r) {
412 // Create a very simple nonsense program.
413 SkSL::RP::Builder builder;
414 builder.push_constant_f(1.0f); // synthesize a 2x2 matrix
415 builder.push_constant_f(2.0f);
416 builder.push_constant_f(3.0f);
417 builder.push_constant_f(4.0f);
418 builder.matrix_resize(2, 2, 4, 4); // resize 2x2 matrix into 4x4
419 builder.matrix_resize(4, 4, 2, 2); // resize 4x4 matrix back into 2x2
420 builder.matrix_resize(2, 2, 2, 4); // resize 2x2 matrix into 2x4
421 builder.matrix_resize(2, 4, 4, 2); // resize 2x4 matrix into 4x2
422 builder.matrix_resize(4, 2, 3, 3); // resize 4x2 matrix into 3x3
423 builder.discard_stack(9); // balance stack
424 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
425 /*numUniformSlots=*/0,
426 /*numImmutableSlots=*/0);
427 check(r, *program,
428 R"(copy_constant $0 = 0x3F800000 (1.0)
429 copy_constant $1 = 0x40000000 (2.0)
430 copy_constant $2 = 0x40400000 (3.0)
431 copy_constant $3 = 0x40800000 (4.0)
432 copy_constant $4 = 0
433 copy_constant $5 = 0x3F800000 (1.0)
434 shuffle $2..15 = ($2..15)[2 2 0 1 2 2 2 2 3 2 2 2 2 3]
435 shuffle $2..3 = ($2..3)[2 3]
436 copy_constant $4 = 0
437 shuffle $2..7 = ($2..7)[2 2 0 1 2 2]
438 copy_constant $8 = 0
439 shuffle $2..7 = ($2..7)[2 3 6 6 6 6]
440 copy_constant $8 = 0
441 copy_constant $9 = 0x3F800000 (1.0)
442 shuffle $2..8 = ($2..8)[6 0 1 6 2 3 7]
443 )");
444 }
445
DEF_TEST(RasterPipelineBuilderBranches,r)446 DEF_TEST(RasterPipelineBuilderBranches, r) {
447 #if SK_HAS_MUSTTAIL
448 // We have guaranteed tail-calling, and don't need to rewind the stack.
449 static constexpr char kExpectationWithKnownExecutionMask[] =
450 R"(jump jump +9 (label 3 at #10)
451 label label 0
452 copy_constant v0 = 0
453 label label 0x00000001
454 copy_constant v1 = 0
455 jump jump -4 (label 0 at #2)
456 label label 0x00000002
457 copy_constant v2 = 0
458 jump jump -7 (label 0 at #2)
459 label label 0x00000003
460 branch_if_no_active_lanes_eq branch -4 (label 2 at #7) if no lanes of v2 == 0
461 branch_if_no_active_lanes_eq branch -10 (label 0 at #2) if no lanes of v2 == 0x00000001 (1.401298e-45)
462 )";
463 static constexpr char kExpectationWithExecutionMaskWrites[] =
464 R"(jump jump +10 (label 3 at #11)
465 label label 0
466 copy_constant v0 = 0
467 label label 0x00000001
468 copy_constant v1 = 0
469 branch_if_no_lanes_active branch_if_no_lanes_active -2 (label 1 at #4)
470 branch_if_all_lanes_active branch_if_all_lanes_active -5 (label 0 at #2)
471 label label 0x00000002
472 copy_constant v2 = 0
473 branch_if_any_lanes_active branch_if_any_lanes_active -8 (label 0 at #2)
474 label label 0x00000003
475 branch_if_no_active_lanes_eq branch -4 (label 2 at #8) if no lanes of v2 == 0
476 branch_if_no_active_lanes_eq branch -11 (label 0 at #2) if no lanes of v2 == 0x00000001 (1.401298e-45)
477 )";
478 #else
479 // We don't have guaranteed tail-calling, so we rewind the stack immediately before any backward
480 // branches.
481 static constexpr char kExpectationWithKnownExecutionMask[] =
482 R"(jump jump +11 (label 3 at #12)
483 label label 0
484 copy_constant v0 = 0
485 label label 0x00000001
486 copy_constant v1 = 0
487 stack_rewind
488 jump jump -5 (label 0 at #2)
489 label label 0x00000002
490 copy_constant v2 = 0
491 stack_rewind
492 jump jump -9 (label 0 at #2)
493 label label 0x00000003
494 stack_rewind
495 branch_if_no_active_lanes_eq branch -6 (label 2 at #8) if no lanes of v2 == 0
496 stack_rewind
497 branch_if_no_active_lanes_eq branch -14 (label 0 at #2) if no lanes of v2 == 0x00000001 (1.401298e-45)
498 )";
499 static constexpr char kExpectationWithExecutionMaskWrites[] =
500 R"(jump jump +13 (label 3 at #14)
501 label label 0
502 copy_constant v0 = 0
503 label label 0x00000001
504 copy_constant v1 = 0
505 stack_rewind
506 branch_if_no_lanes_active branch_if_no_lanes_active -3 (label 1 at #4)
507 stack_rewind
508 branch_if_all_lanes_active branch_if_all_lanes_active -7 (label 0 at #2)
509 label label 0x00000002
510 copy_constant v2 = 0
511 stack_rewind
512 branch_if_any_lanes_active branch_if_any_lanes_active -11 (label 0 at #2)
513 label label 0x00000003
514 stack_rewind
515 branch_if_no_active_lanes_eq branch -6 (label 2 at #10) if no lanes of v2 == 0
516 stack_rewind
517 branch_if_no_active_lanes_eq branch -16 (label 0 at #2) if no lanes of v2 == 0x00000001 (1.401298e-45)
518 )";
519 #endif
520
521 for (bool enableExecutionMaskWrites : {false, true}) {
522 // Create a very simple nonsense program.
523 SkSL::RP::Builder builder;
524 int label1 = builder.nextLabelID();
525 int label2 = builder.nextLabelID();
526 int label3 = builder.nextLabelID();
527 int label4 = builder.nextLabelID();
528
529 if (enableExecutionMaskWrites) {
530 builder.enableExecutionMaskWrites();
531 }
532
533 builder.jump(label4);
534 builder.label(label1);
535 builder.zero_slots_unmasked(one_slot_at(0));
536 builder.label(label2);
537 builder.zero_slots_unmasked(one_slot_at(1));
538 builder.branch_if_no_lanes_active(label2);
539 builder.branch_if_no_lanes_active(label3);
540 builder.branch_if_all_lanes_active(label1);
541 builder.label(label3);
542 builder.zero_slots_unmasked(one_slot_at(2));
543 builder.branch_if_any_lanes_active(label1);
544 builder.branch_if_any_lanes_active(label1);
545 builder.label(label4);
546 builder.branch_if_no_active_lanes_on_stack_top_equal(0, label3);
547 builder.branch_if_no_active_lanes_on_stack_top_equal(0, label2);
548 builder.branch_if_no_active_lanes_on_stack_top_equal(1, label1);
549 builder.branch_if_no_active_lanes_on_stack_top_equal(1, label4);
550
551 if (enableExecutionMaskWrites) {
552 builder.disableExecutionMaskWrites();
553 }
554
555 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/3,
556 /*numUniformSlots=*/0,
557 /*numImmutableSlots=*/0);
558
559 check(r, *program, enableExecutionMaskWrites ? kExpectationWithExecutionMaskWrites
560 : kExpectationWithKnownExecutionMask);
561 }
562 }
563
DEF_TEST(RasterPipelineBuilderBackwardsBranchOverInvocationShouldRewind,r)564 DEF_TEST(RasterPipelineBuilderBackwardsBranchOverInvocationShouldRewind, r) {
565 // Branching backward over a call to invoke_shader should always emit a stack_rewind op.
566 SkSL::RP::Builder builder;
567 int label1 = builder.nextLabelID();
568 builder.push_constant_f(10.0f);
569 builder.label(label1);
570 builder.push_constant_f(20.0f);
571 builder.invoke_shader(9);
572 builder.push_constant_f(30.0f);
573 builder.discard_stack(3);
574 builder.branch_if_any_lanes_active(label1);
575
576 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/3,
577 /*numUniformSlots=*/0,
578 /*numImmutableSlots=*/0);
579 check(r, *program,
580 R"(copy_constant $0 = 0x41200000 (10.0)
581 label label 0
582 copy_constant $1 = 0x41A00000 (20.0)
583 invoke_shader invoke_shader 0x00000009
584 stack_rewind
585 jump jump -4 (label 0 at #2)
586 )");
587 }
588
DEF_TEST(RasterPipelineBuilderBackwardsBranchWithoutInvocationMightNotRewind,r)589 DEF_TEST(RasterPipelineBuilderBackwardsBranchWithoutInvocationMightNotRewind, r) {
590 SkSL::RP::Builder builder;
591 int label1 = builder.nextLabelID();
592 builder.push_constant_f(10.0f);
593 builder.invoke_shader(9);
594 builder.push_constant_f(20.0f);
595 builder.label(label1);
596 builder.push_constant_f(30.0f);
597 builder.discard_stack(3);
598 builder.branch_if_any_lanes_active(label1);
599
600 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/3,
601 /*numUniformSlots=*/0,
602 /*numImmutableSlots=*/0);
603 #if SK_HAS_MUSTTAIL
604 check(r, *program,
605 R"(copy_constant $0 = 0x41200000 (10.0)
606 invoke_shader invoke_shader 0x00000009
607 copy_constant $1 = 0x41A00000 (20.0)
608 label label 0
609 jump jump -1 (label 0 at #4)
610 )");
611 #else
612 check(r, *program,
613 R"(copy_constant $0 = 0x41200000 (10.0)
614 invoke_shader invoke_shader 0x00000009
615 copy_constant $1 = 0x41A00000 (20.0)
616 label label 0
617 stack_rewind
618 jump jump -2 (label 0 at #4)
619 )");
620 #endif
621 }
622
DEF_TEST(RasterPipelineBuilderBinaryFloatOps,r)623 DEF_TEST(RasterPipelineBuilderBinaryFloatOps, r) {
624 using BuilderOp = SkSL::RP::BuilderOp;
625
626 SkSL::RP::Builder builder;
627 builder.push_constant_f(10.0f);
628 builder.push_duplicates(30);
629 builder.binary_op(BuilderOp::add_n_floats, 1);
630 builder.binary_op(BuilderOp::sub_n_floats, 2);
631 builder.binary_op(BuilderOp::mul_n_floats, 3);
632 builder.binary_op(BuilderOp::div_n_floats, 4);
633 builder.binary_op(BuilderOp::max_n_floats, 3);
634 builder.binary_op(BuilderOp::min_n_floats, 2);
635 builder.binary_op(BuilderOp::cmplt_n_floats, 5);
636 builder.binary_op(BuilderOp::cmple_n_floats, 4);
637 builder.binary_op(BuilderOp::cmpeq_n_floats, 3);
638 builder.binary_op(BuilderOp::cmpne_n_floats, 2);
639 builder.discard_stack(2);
640 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
641 /*numUniformSlots=*/0,
642 /*numImmutableSlots=*/0);
643 check(r, *program,
644 R"(splat_4_constants $0..3 = 0x41200000 (10.0)
645 splat_4_constants $4..7 = 0x41200000 (10.0)
646 splat_4_constants $8..11 = 0x41200000 (10.0)
647 splat_4_constants $12..15 = 0x41200000 (10.0)
648 splat_4_constants $16..19 = 0x41200000 (10.0)
649 splat_4_constants $20..23 = 0x41200000 (10.0)
650 splat_4_constants $24..27 = 0x41200000 (10.0)
651 splat_2_constants $28..29 = 0x41200000 (10.0)
652 add_imm_float $29 += 0x41200000 (10.0)
653 sub_2_floats $26..27 -= $28..29
654 mul_3_floats $22..24 *= $25..27
655 div_4_floats $17..20 /= $21..24
656 max_3_floats $15..17 = max($15..17, $18..20)
657 min_2_floats $14..15 = min($14..15, $16..17)
658 cmplt_n_floats $6..10 = lessThan($6..10, $11..15)
659 cmple_4_floats $3..6 = lessThanEqual($3..6, $7..10)
660 cmpeq_3_floats $1..3 = equal($1..3, $4..6)
661 cmpne_2_floats $0..1 = notEqual($0..1, $2..3)
662 )");
663 }
664
DEF_TEST(RasterPipelineBuilderBinaryIntOps,r)665 DEF_TEST(RasterPipelineBuilderBinaryIntOps, r) {
666 using BuilderOp = SkSL::RP::BuilderOp;
667
668 SkSL::RP::Builder builder;
669 builder.push_constant_i(123);
670 builder.push_duplicates(40);
671 builder.binary_op(BuilderOp::bitwise_and_n_ints, 1);
672 builder.binary_op(BuilderOp::bitwise_xor_n_ints, 2);
673 builder.binary_op(BuilderOp::bitwise_or_n_ints, 3);
674 builder.binary_op(BuilderOp::add_n_ints, 2);
675 builder.binary_op(BuilderOp::sub_n_ints, 3);
676 builder.binary_op(BuilderOp::mul_n_ints, 4);
677 builder.binary_op(BuilderOp::div_n_ints, 5);
678 builder.binary_op(BuilderOp::max_n_ints, 4);
679 builder.binary_op(BuilderOp::min_n_ints, 3);
680 builder.binary_op(BuilderOp::cmplt_n_ints, 1);
681 builder.binary_op(BuilderOp::cmple_n_ints, 2);
682 builder.binary_op(BuilderOp::cmpeq_n_ints, 3);
683 builder.binary_op(BuilderOp::cmpne_n_ints, 4);
684 builder.discard_stack(4);
685 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
686 /*numUniformSlots=*/0,
687 /*numImmutableSlots=*/0);
688 check(r, *program,
689 R"(splat_4_constants $0..3 = 0x0000007B (1.723597e-43)
690 splat_4_constants $4..7 = 0x0000007B (1.723597e-43)
691 splat_4_constants $8..11 = 0x0000007B (1.723597e-43)
692 splat_4_constants $12..15 = 0x0000007B (1.723597e-43)
693 splat_4_constants $16..19 = 0x0000007B (1.723597e-43)
694 splat_4_constants $20..23 = 0x0000007B (1.723597e-43)
695 splat_4_constants $24..27 = 0x0000007B (1.723597e-43)
696 splat_4_constants $28..31 = 0x0000007B (1.723597e-43)
697 splat_4_constants $32..35 = 0x0000007B (1.723597e-43)
698 splat_4_constants $36..39 = 0x0000007B (1.723597e-43)
699 bitwise_and_imm_int $39 &= 0x0000007B
700 bitwise_xor_2_ints $36..37 ^= $38..39
701 bitwise_or_3_ints $32..34 |= $35..37
702 add_2_ints $31..32 += $33..34
703 sub_3_ints $27..29 -= $30..32
704 mul_4_ints $22..25 *= $26..29
705 div_n_ints $16..20 /= $21..25
706 max_4_ints $13..16 = max($13..16, $17..20)
707 min_3_ints $11..13 = min($11..13, $14..16)
708 cmplt_int $12 = lessThan($12, $13)
709 cmple_2_ints $9..10 = lessThanEqual($9..10, $11..12)
710 cmpeq_3_ints $5..7 = equal($5..7, $8..10)
711 cmpne_4_ints $0..3 = notEqual($0..3, $4..7)
712 )");
713 }
714
DEF_TEST(RasterPipelineBuilderBinaryUIntOps,r)715 DEF_TEST(RasterPipelineBuilderBinaryUIntOps, r) {
716 using BuilderOp = SkSL::RP::BuilderOp;
717
718 SkSL::RP::Builder builder;
719 builder.push_constant_u(456);
720 builder.push_duplicates(21);
721 builder.binary_op(BuilderOp::div_n_uints, 6);
722 builder.binary_op(BuilderOp::cmplt_n_uints, 5);
723 builder.binary_op(BuilderOp::cmple_n_uints, 4);
724 builder.binary_op(BuilderOp::max_n_uints, 3);
725 builder.binary_op(BuilderOp::min_n_uints, 2);
726 builder.discard_stack(2);
727 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
728 /*numUniformSlots=*/0,
729 /*numImmutableSlots=*/0);
730 check(r, *program,
731 R"(splat_4_constants $0..3 = 0x000001C8 (6.389921e-43)
732 splat_4_constants $4..7 = 0x000001C8 (6.389921e-43)
733 splat_4_constants $8..11 = 0x000001C8 (6.389921e-43)
734 splat_4_constants $12..15 = 0x000001C8 (6.389921e-43)
735 splat_4_constants $16..19 = 0x000001C8 (6.389921e-43)
736 splat_2_constants $20..21 = 0x000001C8 (6.389921e-43)
737 div_n_uints $10..15 /= $16..21
738 cmplt_n_uints $6..10 = lessThan($6..10, $11..15)
739 cmple_4_uints $3..6 = lessThanEqual($3..6, $7..10)
740 max_3_uints $1..3 = max($1..3, $4..6)
741 min_2_uints $0..1 = min($0..1, $2..3)
742 )");
743 }
744
DEF_TEST(RasterPipelineBuilderUnaryOps,r)745 DEF_TEST(RasterPipelineBuilderUnaryOps, r) {
746 using BuilderOp = SkSL::RP::BuilderOp;
747
748 SkSL::RP::Builder builder;
749 builder.push_constant_i(456);
750 builder.push_duplicates(4);
751 builder.unary_op(BuilderOp::cast_to_float_from_int, 1);
752 builder.unary_op(BuilderOp::cast_to_float_from_uint, 2);
753 builder.unary_op(BuilderOp::cast_to_int_from_float, 3);
754 builder.unary_op(BuilderOp::cast_to_uint_from_float, 4);
755 builder.unary_op(BuilderOp::cos_float, 4);
756 builder.unary_op(BuilderOp::tan_float, 3);
757 builder.unary_op(BuilderOp::sin_float, 2);
758 builder.unary_op(BuilderOp::sqrt_float, 1);
759 builder.unary_op(BuilderOp::abs_int, 2);
760 builder.unary_op(BuilderOp::floor_float, 3);
761 builder.unary_op(BuilderOp::ceil_float, 4);
762 builder.discard_stack(5);
763 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
764 /*numUniformSlots=*/0,
765 /*numImmutableSlots=*/0);
766 check(r, *program,
767 R"(splat_4_constants $0..3 = 0x000001C8 (6.389921e-43)
768 copy_constant $4 = 0x000001C8 (6.389921e-43)
769 cast_to_float_from_int $4 = IntToFloat($4)
770 cast_to_float_from_2_uints $3..4 = UintToFloat($3..4)
771 cast_to_int_from_3_floats $2..4 = FloatToInt($2..4)
772 cast_to_uint_from_4_floats $1..4 = FloatToUint($1..4)
773 cos_float $1 = cos($1)
774 cos_float $2 = cos($2)
775 cos_float $3 = cos($3)
776 cos_float $4 = cos($4)
777 tan_float $2 = tan($2)
778 tan_float $3 = tan($3)
779 tan_float $4 = tan($4)
780 sin_float $3 = sin($3)
781 sin_float $4 = sin($4)
782 sqrt_float $4 = sqrt($4)
783 abs_2_ints $3..4 = abs($3..4)
784 floor_3_floats $2..4 = floor($2..4)
785 ceil_4_floats $1..4 = ceil($1..4)
786 )");
787 }
788
DEF_TEST(RasterPipelineBuilderUniforms,r)789 DEF_TEST(RasterPipelineBuilderUniforms, r) {
790 using BuilderOp = SkSL::RP::BuilderOp;
791
792 // Create a very simple nonsense program.
793 SkSL::RP::Builder builder;
794 builder.push_uniform(one_slot_at(0)); // push into 0
795 builder.push_uniform(two_slots_at(1)); // push into 1~2
796 builder.push_uniform(three_slots_at(3)); // push into 3~5
797 builder.push_uniform(four_slots_at(6)); // push into 6~9
798 builder.push_uniform(five_slots_at(0)); // push into 10~14
799 builder.unary_op(BuilderOp::abs_int, 1); // perform work so the program isn't eliminated
800 builder.discard_stack(15); // balance stack
801 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
802 /*numUniformSlots=*/10,
803 /*numImmutableSlots=*/0);
804 check(r, *program,
805 R"(copy_4_uniforms $0..3 = u0..3
806 copy_4_uniforms $4..7 = u4..7
807 copy_2_uniforms $8..9 = u8..9
808 copy_4_uniforms $10..13 = u0..3
809 copy_uniform $14 = u4
810 abs_int $14 = abs($14)
811 )");
812 }
813
DEF_TEST(RasterPipelineBuilderPushZeros,r)814 DEF_TEST(RasterPipelineBuilderPushZeros, r) {
815 using BuilderOp = SkSL::RP::BuilderOp;
816
817 // Create a very simple nonsense program.
818 SkSL::RP::Builder builder;
819 builder.push_zeros(1); // push into 0
820 builder.push_zeros(2); // push into 1~2
821 builder.push_zeros(3); // push into 3~5
822 builder.push_zeros(4); // push into 6~9
823 builder.push_zeros(5); // push into 10~14
824 builder.unary_op(BuilderOp::abs_int, 1); // perform work so the program isn't eliminated
825 builder.discard_stack(15); // balance stack
826 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
827 /*numUniformSlots=*/10,
828 /*numImmutableSlots=*/0);
829 check(r, *program,
830 R"(splat_4_constants $0..3 = 0
831 splat_4_constants $4..7 = 0
832 splat_4_constants $8..11 = 0
833 splat_3_constants $12..14 = 0
834 abs_int $14 = abs($14)
835 )");
836 }
837
DEF_TEST(RasterPipelineBuilderTernaryFloatOps,r)838 DEF_TEST(RasterPipelineBuilderTernaryFloatOps, r) {
839 using BuilderOp = SkSL::RP::BuilderOp;
840
841 SkSL::RP::Builder builder;
842 builder.push_constant_f(0.75f);
843 builder.push_duplicates(8);
844 builder.ternary_op(BuilderOp::mix_n_floats, 3);
845 builder.discard_stack(3);
846 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
847 /*numUniformSlots=*/0,
848 /*numImmutableSlots=*/0);
849 check(r, *program,
850 R"(splat_4_constants $0..3 = 0x3F400000 (0.75)
851 splat_4_constants $4..7 = 0x3F400000 (0.75)
852 copy_constant $8 = 0x3F400000 (0.75)
853 mix_3_floats $0..2 = mix($3..5, $6..8, $0..2)
854 )");
855 }
856
DEF_TEST(RasterPipelineBuilderAutomaticStackRewinding,r)857 DEF_TEST(RasterPipelineBuilderAutomaticStackRewinding, r) {
858 using BuilderOp = SkSL::RP::BuilderOp;
859
860 SkSL::RP::Builder builder;
861 builder.push_constant_i(1);
862 builder.push_duplicates(2000);
863 builder.unary_op(BuilderOp::abs_int, 1); // perform work so the program isn't eliminated
864 builder.discard_stack(2001);
865 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/0,
866 /*numUniformSlots=*/0,
867 /*numImmutableSlots=*/0);
868 sk_sp<SkData> dump = get_program_dump(*program);
869
870 #if SK_HAS_MUSTTAIL
871 // We have guaranteed tail-calling, so we never use `stack_rewind`.
872 REPORTER_ASSERT(r, !skstd::contains(as_string_view(dump), "stack_rewind"));
873 #else
874 // We can't guarantee tail-calling, so we should automatically insert `stack_rewind` stages into
875 // long programs.
876 REPORTER_ASSERT(r, skstd::contains(as_string_view(dump), "stack_rewind"));
877 #endif
878 }
879
DEF_TEST(RasterPipelineBuilderTraceOps,r)880 DEF_TEST(RasterPipelineBuilderTraceOps, r) {
881 for (bool provideDebugTrace : {false, true}) {
882 SkSL::RP::Builder builder;
883 // Create a trace mask stack on stack-ID 123.
884 builder.set_current_stack(123);
885 builder.push_constant_i(~0);
886 // Emit trace ops.
887 builder.trace_enter(123, 2);
888 builder.trace_scope(123, +1);
889 builder.trace_line(123, 456);
890 builder.trace_var(123, two_slots_at(3));
891 builder.trace_scope(123, -1);
892 builder.trace_exit(123, 2);
893 // Discard the trace mask.
894 builder.discard_stack(1);
895
896 if (!provideDebugTrace) {
897 // Test the output when no DebugTrace info is provided.
898 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/20,
899 /*numUniformSlots=*/0,
900 /*numImmutableSlots=*/0);
901 check(r, *program,
902 R"(copy_constant $0 = 0xFFFFFFFF
903 trace_enter TraceEnter(???) when $0 is true
904 trace_scope TraceScope(+1) when $0 is true
905 trace_line TraceLine(456) when $0 is true
906 trace_var TraceVar(v3..4) when $0 is true
907 trace_scope TraceScope(-1) when $0 is true
908 trace_exit TraceExit(???) when $0 is true
909 )");
910 } else {
911 // Test the output when we supply a populated DebugTrace.
912 SkSL::DebugTracePriv trace;
913 trace.fFuncInfo = {{"FunctionA"}, {"FunctionB"}, {"FunctionC"}, {"FunctionD"}};
914
915 SkSL::SlotDebugInfo slot;
916 slot.name = "Var0";
917 trace.fSlotInfo.push_back(slot);
918 slot.name = "Var1";
919 trace.fSlotInfo.push_back(slot);
920 slot.name = "Var2";
921 trace.fSlotInfo.push_back(slot);
922 slot.name = "Var3";
923 trace.fSlotInfo.push_back(slot);
924 slot.name = "Var4";
925 trace.fSlotInfo.push_back(slot);
926
927 std::unique_ptr<SkSL::RP::Program> program = builder.finish(/*numValueSlots=*/20,
928 /*numUniformSlots=*/0,
929 /*numImmutableSlots=*/0,
930 &trace);
931 check(r, *program,
932 R"(copy_constant $0 = 0xFFFFFFFF
933 trace_enter TraceEnter(FunctionC) when $0 is true
934 trace_scope TraceScope(+1) when $0 is true
935 trace_line TraceLine(456) when $0 is true
936 trace_var TraceVar(Var3, Var4) when $0 is true
937 trace_scope TraceScope(-1) when $0 is true
938 trace_exit TraceExit(FunctionC) when $0 is true
939 )");
940 }
941 }
942 }
943