xref: /aosp_15_r20/external/skia/src/sksl/codegen/SkSLRasterPipelineBuilder.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2022 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/codegen/SkSLRasterPipelineBuilder.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
10*c8dee2aaSAndroid Build Coastguard Worker #include <optional>
11*c8dee2aaSAndroid Build Coastguard Worker 
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMalloc.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTFitsIn.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkArenaAlloc.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkSafeMath.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkOpts.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRasterPipelineContextUtils.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRasterPipelineOpContexts.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRasterPipelineOpList.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTHash.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLPosition.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLString.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/tracing/SkSLDebugTracePriv.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/tracing/SkSLTraceHook.h"
27*c8dee2aaSAndroid Build Coastguard Worker 
28*c8dee2aaSAndroid Build Coastguard Worker #if !defined(SKSL_STANDALONE)
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRasterPipeline.h"
30*c8dee2aaSAndroid Build Coastguard Worker #endif
31*c8dee2aaSAndroid Build Coastguard Worker 
32*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
33*c8dee2aaSAndroid Build Coastguard Worker #include <cmath>
34*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
35*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
36*c8dee2aaSAndroid Build Coastguard Worker #include <iterator>
37*c8dee2aaSAndroid Build Coastguard Worker #include <string>
38*c8dee2aaSAndroid Build Coastguard Worker #include <string_view>
39*c8dee2aaSAndroid Build Coastguard Worker #include <tuple>
40*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
41*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
42*c8dee2aaSAndroid Build Coastguard Worker 
43*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
44*c8dee2aaSAndroid Build Coastguard Worker 
45*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL::RP {
46*c8dee2aaSAndroid Build Coastguard Worker 
47*c8dee2aaSAndroid Build Coastguard Worker #define ALL_SINGLE_SLOT_UNARY_OP_CASES  \
48*c8dee2aaSAndroid Build Coastguard Worker          BuilderOp::acos_float:         \
49*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::asin_float:         \
50*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::atan_float:         \
51*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cos_float:          \
52*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::exp_float:          \
53*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::exp2_float:         \
54*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::log_float:          \
55*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::log2_float:         \
56*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::sin_float:          \
57*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::sqrt_float:         \
58*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::tan_float
59*c8dee2aaSAndroid Build Coastguard Worker 
60*c8dee2aaSAndroid Build Coastguard Worker #define ALL_MULTI_SLOT_UNARY_OP_CASES        \
61*c8dee2aaSAndroid Build Coastguard Worker          BuilderOp::abs_int:                 \
62*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cast_to_float_from_int:  \
63*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cast_to_float_from_uint: \
64*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cast_to_int_from_float:  \
65*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cast_to_uint_from_float: \
66*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::ceil_float:              \
67*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::floor_float:             \
68*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::invsqrt_float
69*c8dee2aaSAndroid Build Coastguard Worker 
70*c8dee2aaSAndroid Build Coastguard Worker #define ALL_N_WAY_BINARY_OP_CASES   \
71*c8dee2aaSAndroid Build Coastguard Worker          BuilderOp::atan2_n_floats: \
72*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::pow_n_floats
73*c8dee2aaSAndroid Build Coastguard Worker 
74*c8dee2aaSAndroid Build Coastguard Worker #define ALL_MULTI_SLOT_BINARY_OP_CASES  \
75*c8dee2aaSAndroid Build Coastguard Worker          BuilderOp::add_n_floats:       \
76*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::add_n_ints:         \
77*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::sub_n_floats:       \
78*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::sub_n_ints:         \
79*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::mul_n_floats:       \
80*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::mul_n_ints:         \
81*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::div_n_floats:       \
82*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::div_n_ints:         \
83*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::div_n_uints:        \
84*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::bitwise_and_n_ints: \
85*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::bitwise_or_n_ints:  \
86*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::bitwise_xor_n_ints: \
87*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::mod_n_floats:       \
88*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::min_n_floats:       \
89*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::min_n_ints:         \
90*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::min_n_uints:        \
91*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::max_n_floats:       \
92*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::max_n_ints:         \
93*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::max_n_uints:        \
94*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmple_n_floats:     \
95*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmple_n_ints:       \
96*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmple_n_uints:      \
97*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmplt_n_floats:     \
98*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmplt_n_ints:       \
99*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmplt_n_uints:      \
100*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmpeq_n_floats:     \
101*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmpeq_n_ints:       \
102*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmpne_n_floats:     \
103*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmpne_n_ints
104*c8dee2aaSAndroid Build Coastguard Worker 
105*c8dee2aaSAndroid Build Coastguard Worker #define ALL_IMMEDIATE_BINARY_OP_CASES    \
106*c8dee2aaSAndroid Build Coastguard Worker          BuilderOp::add_imm_float:       \
107*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::add_imm_int:         \
108*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::mul_imm_float:       \
109*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::mul_imm_int:         \
110*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::bitwise_and_imm_int: \
111*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::bitwise_xor_imm_int: \
112*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::min_imm_float:       \
113*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::max_imm_float:       \
114*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmple_imm_float:     \
115*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmple_imm_int:       \
116*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmple_imm_uint:      \
117*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmplt_imm_float:     \
118*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmplt_imm_int:       \
119*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmplt_imm_uint:      \
120*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmpeq_imm_float:     \
121*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmpeq_imm_int:       \
122*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmpne_imm_float:     \
123*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::cmpne_imm_int
124*c8dee2aaSAndroid Build Coastguard Worker 
125*c8dee2aaSAndroid Build Coastguard Worker #define ALL_IMMEDIATE_MULTI_SLOT_BINARY_OP_CASES \
126*c8dee2aaSAndroid Build Coastguard Worker          BuilderOp::bitwise_and_imm_int
127*c8dee2aaSAndroid Build Coastguard Worker 
128*c8dee2aaSAndroid Build Coastguard Worker #define ALL_N_WAY_TERNARY_OP_CASES       \
129*c8dee2aaSAndroid Build Coastguard Worker          BuilderOp::smoothstep_n_floats
130*c8dee2aaSAndroid Build Coastguard Worker 
131*c8dee2aaSAndroid Build Coastguard Worker #define ALL_MULTI_SLOT_TERNARY_OP_CASES \
132*c8dee2aaSAndroid Build Coastguard Worker          BuilderOp::mix_n_floats:       \
133*c8dee2aaSAndroid Build Coastguard Worker     case BuilderOp::mix_n_ints
134*c8dee2aaSAndroid Build Coastguard Worker 
is_immediate_op(BuilderOp op)135*c8dee2aaSAndroid Build Coastguard Worker static bool is_immediate_op(BuilderOp op) {
136*c8dee2aaSAndroid Build Coastguard Worker     switch (op) {
137*c8dee2aaSAndroid Build Coastguard Worker         case ALL_IMMEDIATE_BINARY_OP_CASES: return true;
138*c8dee2aaSAndroid Build Coastguard Worker         default:                            return false;
139*c8dee2aaSAndroid Build Coastguard Worker     }
140*c8dee2aaSAndroid Build Coastguard Worker }
141*c8dee2aaSAndroid Build Coastguard Worker 
is_multi_slot_immediate_op(BuilderOp op)142*c8dee2aaSAndroid Build Coastguard Worker static bool is_multi_slot_immediate_op(BuilderOp op) {
143*c8dee2aaSAndroid Build Coastguard Worker     switch (op) {
144*c8dee2aaSAndroid Build Coastguard Worker         case ALL_IMMEDIATE_MULTI_SLOT_BINARY_OP_CASES: return true;
145*c8dee2aaSAndroid Build Coastguard Worker         default:                                       return false;
146*c8dee2aaSAndroid Build Coastguard Worker     }
147*c8dee2aaSAndroid Build Coastguard Worker }
148*c8dee2aaSAndroid Build Coastguard Worker 
convert_n_way_op_to_immediate(BuilderOp op,int slots,int32_t * constantValue)149*c8dee2aaSAndroid Build Coastguard Worker static BuilderOp convert_n_way_op_to_immediate(BuilderOp op, int slots, int32_t* constantValue) {
150*c8dee2aaSAndroid Build Coastguard Worker     // We rely on the exact ordering of SkRP ops here; the immediate-mode op must always come
151*c8dee2aaSAndroid Build Coastguard Worker     // directly before the n-way op. (If we have more than one, the increasing-slot variations
152*c8dee2aaSAndroid Build Coastguard Worker     // continue backwards from there.)
153*c8dee2aaSAndroid Build Coastguard Worker     BuilderOp immOp = (BuilderOp)((int)op - 1);
154*c8dee2aaSAndroid Build Coastguard Worker 
155*c8dee2aaSAndroid Build Coastguard Worker     // Some immediate ops support multiple slots.
156*c8dee2aaSAndroid Build Coastguard Worker     if (is_multi_slot_immediate_op(immOp)) {
157*c8dee2aaSAndroid Build Coastguard Worker         return immOp;
158*c8dee2aaSAndroid Build Coastguard Worker     }
159*c8dee2aaSAndroid Build Coastguard Worker 
160*c8dee2aaSAndroid Build Coastguard Worker     // Most immediate ops only directly support a single slot. However, it's still faster to execute
161*c8dee2aaSAndroid Build Coastguard Worker     // `add_imm_int, add_imm_int` instead of `splat_2_ints, add_2_ints`, so we allow those
162*c8dee2aaSAndroid Build Coastguard Worker     // conversions as well.
163*c8dee2aaSAndroid Build Coastguard Worker     if (slots <= 2) {
164*c8dee2aaSAndroid Build Coastguard Worker         if (is_immediate_op(immOp)) {
165*c8dee2aaSAndroid Build Coastguard Worker             return immOp;
166*c8dee2aaSAndroid Build Coastguard Worker         }
167*c8dee2aaSAndroid Build Coastguard Worker 
168*c8dee2aaSAndroid Build Coastguard Worker         // We also allow for immediate-mode subtraction, by adding a negative value.
169*c8dee2aaSAndroid Build Coastguard Worker         switch (op) {
170*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::sub_n_ints:
171*c8dee2aaSAndroid Build Coastguard Worker                 *constantValue *= -1;
172*c8dee2aaSAndroid Build Coastguard Worker                 return BuilderOp::add_imm_int;
173*c8dee2aaSAndroid Build Coastguard Worker 
174*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::sub_n_floats: {
175*c8dee2aaSAndroid Build Coastguard Worker                 // This negates the floating-point value by inverting its sign bit.
176*c8dee2aaSAndroid Build Coastguard Worker                 *constantValue ^= 0x80000000;
177*c8dee2aaSAndroid Build Coastguard Worker                 return BuilderOp::add_imm_float;
178*c8dee2aaSAndroid Build Coastguard Worker             }
179*c8dee2aaSAndroid Build Coastguard Worker             default:
180*c8dee2aaSAndroid Build Coastguard Worker                 break;
181*c8dee2aaSAndroid Build Coastguard Worker         }
182*c8dee2aaSAndroid Build Coastguard Worker     }
183*c8dee2aaSAndroid Build Coastguard Worker 
184*c8dee2aaSAndroid Build Coastguard Worker     // We don't have an immediate-mode version of this op.
185*c8dee2aaSAndroid Build Coastguard Worker     return op;
186*c8dee2aaSAndroid Build Coastguard Worker }
187*c8dee2aaSAndroid Build Coastguard Worker 
appendInstruction(BuilderOp op,SlotList slots,int immA,int immB,int immC,int immD)188*c8dee2aaSAndroid Build Coastguard Worker void Builder::appendInstruction(BuilderOp op, SlotList slots,
189*c8dee2aaSAndroid Build Coastguard Worker                                 int immA, int immB, int immC, int immD) {
190*c8dee2aaSAndroid Build Coastguard Worker     fInstructions.push_back({op, slots.fSlotA, slots.fSlotB,
191*c8dee2aaSAndroid Build Coastguard Worker                              immA, immB, immC, immD, fCurrentStackID});
192*c8dee2aaSAndroid Build Coastguard Worker }
193*c8dee2aaSAndroid Build Coastguard Worker 
lastInstruction(int fromBack)194*c8dee2aaSAndroid Build Coastguard Worker Instruction* Builder::lastInstruction(int fromBack) {
195*c8dee2aaSAndroid Build Coastguard Worker     if (fInstructions.size() <= fromBack) {
196*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
197*c8dee2aaSAndroid Build Coastguard Worker     }
198*c8dee2aaSAndroid Build Coastguard Worker     Instruction* inst = &fInstructions.fromBack(fromBack);
199*c8dee2aaSAndroid Build Coastguard Worker     if (inst->fStackID != fCurrentStackID) {
200*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
201*c8dee2aaSAndroid Build Coastguard Worker     }
202*c8dee2aaSAndroid Build Coastguard Worker     return inst;
203*c8dee2aaSAndroid Build Coastguard Worker }
204*c8dee2aaSAndroid Build Coastguard Worker 
lastInstructionOnAnyStack(int fromBack)205*c8dee2aaSAndroid Build Coastguard Worker Instruction* Builder::lastInstructionOnAnyStack(int fromBack) {
206*c8dee2aaSAndroid Build Coastguard Worker     if (fInstructions.size() <= fromBack) {
207*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
208*c8dee2aaSAndroid Build Coastguard Worker     }
209*c8dee2aaSAndroid Build Coastguard Worker     return &fInstructions.fromBack(fromBack);
210*c8dee2aaSAndroid Build Coastguard Worker }
211*c8dee2aaSAndroid Build Coastguard Worker 
unary_op(BuilderOp op,int32_t slots)212*c8dee2aaSAndroid Build Coastguard Worker void Builder::unary_op(BuilderOp op, int32_t slots) {
213*c8dee2aaSAndroid Build Coastguard Worker     switch (op) {
214*c8dee2aaSAndroid Build Coastguard Worker         case ALL_SINGLE_SLOT_UNARY_OP_CASES:
215*c8dee2aaSAndroid Build Coastguard Worker         case ALL_MULTI_SLOT_UNARY_OP_CASES:
216*c8dee2aaSAndroid Build Coastguard Worker             this->appendInstruction(op, {}, slots);
217*c8dee2aaSAndroid Build Coastguard Worker             break;
218*c8dee2aaSAndroid Build Coastguard Worker 
219*c8dee2aaSAndroid Build Coastguard Worker         default:
220*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAIL("not a unary op");
221*c8dee2aaSAndroid Build Coastguard Worker             break;
222*c8dee2aaSAndroid Build Coastguard Worker     }
223*c8dee2aaSAndroid Build Coastguard Worker }
224*c8dee2aaSAndroid Build Coastguard Worker 
binary_op(BuilderOp op,int32_t slots)225*c8dee2aaSAndroid Build Coastguard Worker void Builder::binary_op(BuilderOp op, int32_t slots) {
226*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstruction = this->lastInstruction()) {
227*c8dee2aaSAndroid Build Coastguard Worker         // If we just pushed or splatted a constant onto the stack...
228*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == BuilderOp::push_constant &&
229*c8dee2aaSAndroid Build Coastguard Worker             lastInstruction->fImmA >= slots) {
230*c8dee2aaSAndroid Build Coastguard Worker             // ... and this op has an immediate-mode equivalent...
231*c8dee2aaSAndroid Build Coastguard Worker             int32_t constantValue = lastInstruction->fImmB;
232*c8dee2aaSAndroid Build Coastguard Worker             BuilderOp immOp = convert_n_way_op_to_immediate(op, slots, &constantValue);
233*c8dee2aaSAndroid Build Coastguard Worker             if (immOp != op) {
234*c8dee2aaSAndroid Build Coastguard Worker                 // ... discard the constants from the stack, and use an immediate-mode op.
235*c8dee2aaSAndroid Build Coastguard Worker                 this->discard_stack(slots);
236*c8dee2aaSAndroid Build Coastguard Worker                 this->appendInstruction(immOp, {}, slots, constantValue);
237*c8dee2aaSAndroid Build Coastguard Worker                 return;
238*c8dee2aaSAndroid Build Coastguard Worker             }
239*c8dee2aaSAndroid Build Coastguard Worker         }
240*c8dee2aaSAndroid Build Coastguard Worker     }
241*c8dee2aaSAndroid Build Coastguard Worker 
242*c8dee2aaSAndroid Build Coastguard Worker     switch (op) {
243*c8dee2aaSAndroid Build Coastguard Worker         case ALL_N_WAY_BINARY_OP_CASES:
244*c8dee2aaSAndroid Build Coastguard Worker         case ALL_MULTI_SLOT_BINARY_OP_CASES:
245*c8dee2aaSAndroid Build Coastguard Worker             this->appendInstruction(op, {}, slots);
246*c8dee2aaSAndroid Build Coastguard Worker             break;
247*c8dee2aaSAndroid Build Coastguard Worker 
248*c8dee2aaSAndroid Build Coastguard Worker         default:
249*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAIL("not a binary op");
250*c8dee2aaSAndroid Build Coastguard Worker             break;
251*c8dee2aaSAndroid Build Coastguard Worker     }
252*c8dee2aaSAndroid Build Coastguard Worker }
253*c8dee2aaSAndroid Build Coastguard Worker 
ternary_op(BuilderOp op,int32_t slots)254*c8dee2aaSAndroid Build Coastguard Worker void Builder::ternary_op(BuilderOp op, int32_t slots) {
255*c8dee2aaSAndroid Build Coastguard Worker     switch (op) {
256*c8dee2aaSAndroid Build Coastguard Worker         case ALL_N_WAY_TERNARY_OP_CASES:
257*c8dee2aaSAndroid Build Coastguard Worker         case ALL_MULTI_SLOT_TERNARY_OP_CASES:
258*c8dee2aaSAndroid Build Coastguard Worker             this->appendInstruction(op, {}, slots);
259*c8dee2aaSAndroid Build Coastguard Worker             break;
260*c8dee2aaSAndroid Build Coastguard Worker 
261*c8dee2aaSAndroid Build Coastguard Worker         default:
262*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAIL("not a ternary op");
263*c8dee2aaSAndroid Build Coastguard Worker             break;
264*c8dee2aaSAndroid Build Coastguard Worker     }
265*c8dee2aaSAndroid Build Coastguard Worker }
266*c8dee2aaSAndroid Build Coastguard Worker 
dot_floats(int32_t slots)267*c8dee2aaSAndroid Build Coastguard Worker void Builder::dot_floats(int32_t slots) {
268*c8dee2aaSAndroid Build Coastguard Worker     switch (slots) {
269*c8dee2aaSAndroid Build Coastguard Worker         case 1: this->appendInstruction(BuilderOp::mul_n_floats, {}, slots); break;
270*c8dee2aaSAndroid Build Coastguard Worker         case 2: this->appendInstruction(BuilderOp::dot_2_floats, {}, slots); break;
271*c8dee2aaSAndroid Build Coastguard Worker         case 3: this->appendInstruction(BuilderOp::dot_3_floats, {}, slots); break;
272*c8dee2aaSAndroid Build Coastguard Worker         case 4: this->appendInstruction(BuilderOp::dot_4_floats, {}, slots); break;
273*c8dee2aaSAndroid Build Coastguard Worker 
274*c8dee2aaSAndroid Build Coastguard Worker         default:
275*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAIL("invalid number of slots");
276*c8dee2aaSAndroid Build Coastguard Worker             break;
277*c8dee2aaSAndroid Build Coastguard Worker     }
278*c8dee2aaSAndroid Build Coastguard Worker }
279*c8dee2aaSAndroid Build Coastguard Worker 
refract_floats()280*c8dee2aaSAndroid Build Coastguard Worker void Builder::refract_floats() {
281*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::refract_4_floats, {});
282*c8dee2aaSAndroid Build Coastguard Worker }
283*c8dee2aaSAndroid Build Coastguard Worker 
inverse_matrix(int32_t n)284*c8dee2aaSAndroid Build Coastguard Worker void Builder::inverse_matrix(int32_t n) {
285*c8dee2aaSAndroid Build Coastguard Worker     switch (n) {
286*c8dee2aaSAndroid Build Coastguard Worker         case 2:  this->appendInstruction(BuilderOp::inverse_mat2, {}, 4);  break;
287*c8dee2aaSAndroid Build Coastguard Worker         case 3:  this->appendInstruction(BuilderOp::inverse_mat3, {}, 9);  break;
288*c8dee2aaSAndroid Build Coastguard Worker         case 4:  this->appendInstruction(BuilderOp::inverse_mat4, {}, 16); break;
289*c8dee2aaSAndroid Build Coastguard Worker         default: SkUNREACHABLE;
290*c8dee2aaSAndroid Build Coastguard Worker     }
291*c8dee2aaSAndroid Build Coastguard Worker }
292*c8dee2aaSAndroid Build Coastguard Worker 
pad_stack(int32_t count)293*c8dee2aaSAndroid Build Coastguard Worker void Builder::pad_stack(int32_t count) {
294*c8dee2aaSAndroid Build Coastguard Worker     if (count > 0) {
295*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::pad_stack, {}, count);
296*c8dee2aaSAndroid Build Coastguard Worker     }
297*c8dee2aaSAndroid Build Coastguard Worker }
298*c8dee2aaSAndroid Build Coastguard Worker 
simplifyImmediateUnmaskedOp()299*c8dee2aaSAndroid Build Coastguard Worker bool Builder::simplifyImmediateUnmaskedOp() {
300*c8dee2aaSAndroid Build Coastguard Worker     if (fInstructions.size() < 3) {
301*c8dee2aaSAndroid Build Coastguard Worker         return false;
302*c8dee2aaSAndroid Build Coastguard Worker     }
303*c8dee2aaSAndroid Build Coastguard Worker 
304*c8dee2aaSAndroid Build Coastguard Worker     // If we detect a pattern of 'push, immediate-op, unmasked pop', then we can
305*c8dee2aaSAndroid Build Coastguard Worker     // convert it into an immediate-op directly onto the value slots and take the
306*c8dee2aaSAndroid Build Coastguard Worker     // stack entirely out of the equation.
307*c8dee2aaSAndroid Build Coastguard Worker     Instruction* popInstruction  = this->lastInstruction(/*fromBack=*/0);
308*c8dee2aaSAndroid Build Coastguard Worker     Instruction* immInstruction  = this->lastInstruction(/*fromBack=*/1);
309*c8dee2aaSAndroid Build Coastguard Worker     Instruction* pushInstruction = this->lastInstruction(/*fromBack=*/2);
310*c8dee2aaSAndroid Build Coastguard Worker 
311*c8dee2aaSAndroid Build Coastguard Worker     // If the last instruction is an unmasked pop...
312*c8dee2aaSAndroid Build Coastguard Worker     if (popInstruction && immInstruction && pushInstruction &&
313*c8dee2aaSAndroid Build Coastguard Worker         popInstruction->fOp == BuilderOp::copy_stack_to_slots_unmasked) {
314*c8dee2aaSAndroid Build Coastguard Worker         // ... and the prior instruction was an immediate-mode op, with the same number of slots...
315*c8dee2aaSAndroid Build Coastguard Worker         if (is_immediate_op(immInstruction->fOp) &&
316*c8dee2aaSAndroid Build Coastguard Worker             immInstruction->fImmA == popInstruction->fImmA) {
317*c8dee2aaSAndroid Build Coastguard Worker             // ... and we support multiple-slot immediates (if this op calls for it)...
318*c8dee2aaSAndroid Build Coastguard Worker             if (immInstruction->fImmA == 1 || is_multi_slot_immediate_op(immInstruction->fOp)) {
319*c8dee2aaSAndroid Build Coastguard Worker                 // ... and the prior instruction was `push_slots` or `push_immutable` of at least
320*c8dee2aaSAndroid Build Coastguard Worker                 // that many slots...
321*c8dee2aaSAndroid Build Coastguard Worker                 if ((pushInstruction->fOp == BuilderOp::push_slots ||
322*c8dee2aaSAndroid Build Coastguard Worker                      pushInstruction->fOp == BuilderOp::push_immutable) &&
323*c8dee2aaSAndroid Build Coastguard Worker                     pushInstruction->fImmA >= popInstruction->fImmA) {
324*c8dee2aaSAndroid Build Coastguard Worker                     // ... onto the same slot range...
325*c8dee2aaSAndroid Build Coastguard Worker                     Slot immSlot = popInstruction->fSlotA + popInstruction->fImmA;
326*c8dee2aaSAndroid Build Coastguard Worker                     Slot pushSlot = pushInstruction->fSlotA + pushInstruction->fImmA;
327*c8dee2aaSAndroid Build Coastguard Worker                     if (immSlot == pushSlot) {
328*c8dee2aaSAndroid Build Coastguard Worker                         // ... we can shrink the push, eliminate the pop, and perform the immediate
329*c8dee2aaSAndroid Build Coastguard Worker                         // op in-place instead.
330*c8dee2aaSAndroid Build Coastguard Worker                         pushInstruction->fImmA -= immInstruction->fImmA;
331*c8dee2aaSAndroid Build Coastguard Worker                         immInstruction->fSlotA = immSlot - immInstruction->fImmA;
332*c8dee2aaSAndroid Build Coastguard Worker                         fInstructions.pop_back();
333*c8dee2aaSAndroid Build Coastguard Worker                         return true;
334*c8dee2aaSAndroid Build Coastguard Worker                     }
335*c8dee2aaSAndroid Build Coastguard Worker                 }
336*c8dee2aaSAndroid Build Coastguard Worker             }
337*c8dee2aaSAndroid Build Coastguard Worker         }
338*c8dee2aaSAndroid Build Coastguard Worker     }
339*c8dee2aaSAndroid Build Coastguard Worker 
340*c8dee2aaSAndroid Build Coastguard Worker     return false;
341*c8dee2aaSAndroid Build Coastguard Worker }
342*c8dee2aaSAndroid Build Coastguard Worker 
discard_stack(int32_t count,int stackID)343*c8dee2aaSAndroid Build Coastguard Worker void Builder::discard_stack(int32_t count, int stackID) {
344*c8dee2aaSAndroid Build Coastguard Worker     // If we pushed something onto the stack and then immediately discarded part of it, we can
345*c8dee2aaSAndroid Build Coastguard Worker     // shrink or eliminate the push.
346*c8dee2aaSAndroid Build Coastguard Worker     while (count > 0) {
347*c8dee2aaSAndroid Build Coastguard Worker         Instruction* lastInstruction = this->lastInstructionOnAnyStack();
348*c8dee2aaSAndroid Build Coastguard Worker         if (!lastInstruction || lastInstruction->fStackID != stackID) {
349*c8dee2aaSAndroid Build Coastguard Worker             break;
350*c8dee2aaSAndroid Build Coastguard Worker         }
351*c8dee2aaSAndroid Build Coastguard Worker 
352*c8dee2aaSAndroid Build Coastguard Worker         switch (lastInstruction->fOp) {
353*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::discard_stack:
354*c8dee2aaSAndroid Build Coastguard Worker                 // Our last op was actually a separate discard_stack; combine the discards.
355*c8dee2aaSAndroid Build Coastguard Worker                 lastInstruction->fImmA += count;
356*c8dee2aaSAndroid Build Coastguard Worker                 return;
357*c8dee2aaSAndroid Build Coastguard Worker 
358*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_clone:
359*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_clone_from_stack:
360*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_clone_indirect_from_stack:
361*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_constant:
362*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_immutable:
363*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_immutable_indirect:
364*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_slots:
365*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_slots_indirect:
366*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_uniform:
367*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_uniform_indirect:
368*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::pad_stack: {
369*c8dee2aaSAndroid Build Coastguard Worker                 // Our last op was a multi-slot push; these cancel out. Eliminate the op if its
370*c8dee2aaSAndroid Build Coastguard Worker                 // count reached zero.
371*c8dee2aaSAndroid Build Coastguard Worker                 int cancelOut = std::min(count, lastInstruction->fImmA);
372*c8dee2aaSAndroid Build Coastguard Worker                 count                  -= cancelOut;
373*c8dee2aaSAndroid Build Coastguard Worker                 lastInstruction->fImmA -= cancelOut;
374*c8dee2aaSAndroid Build Coastguard Worker                 if (lastInstruction->fImmA == 0) {
375*c8dee2aaSAndroid Build Coastguard Worker                     fInstructions.pop_back();
376*c8dee2aaSAndroid Build Coastguard Worker                 }
377*c8dee2aaSAndroid Build Coastguard Worker                 continue;
378*c8dee2aaSAndroid Build Coastguard Worker             }
379*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_condition_mask:
380*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_loop_mask:
381*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_return_mask:
382*c8dee2aaSAndroid Build Coastguard Worker                 // Our last op was a single-slot push; cancel out one discard and eliminate the op.
383*c8dee2aaSAndroid Build Coastguard Worker                 --count;
384*c8dee2aaSAndroid Build Coastguard Worker                 fInstructions.pop_back();
385*c8dee2aaSAndroid Build Coastguard Worker                 continue;
386*c8dee2aaSAndroid Build Coastguard Worker 
387*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::copy_stack_to_slots_unmasked: {
388*c8dee2aaSAndroid Build Coastguard Worker                 // Look for a pattern of `push, immediate-ops, pop` and simplify it down to an
389*c8dee2aaSAndroid Build Coastguard Worker                 // immediate-op directly to the value slot.
390*c8dee2aaSAndroid Build Coastguard Worker                 if (count == 1) {
391*c8dee2aaSAndroid Build Coastguard Worker                     if (this->simplifyImmediateUnmaskedOp()) {
392*c8dee2aaSAndroid Build Coastguard Worker                         return;
393*c8dee2aaSAndroid Build Coastguard Worker                     }
394*c8dee2aaSAndroid Build Coastguard Worker                 }
395*c8dee2aaSAndroid Build Coastguard Worker 
396*c8dee2aaSAndroid Build Coastguard Worker                 // A `copy_stack_to_slots_unmasked` op, followed immediately by a `discard_stack`
397*c8dee2aaSAndroid Build Coastguard Worker                 // op with an equal number of slots, is interpreted as an unmasked stack pop.
398*c8dee2aaSAndroid Build Coastguard Worker                 // We can simplify pops in a variety of ways. First, temporarily get rid of
399*c8dee2aaSAndroid Build Coastguard Worker                 // `copy_stack_to_slots_unmasked`.
400*c8dee2aaSAndroid Build Coastguard Worker                 if (count == lastInstruction->fImmA) {
401*c8dee2aaSAndroid Build Coastguard Worker                     SlotRange dst{lastInstruction->fSlotA, lastInstruction->fImmA};
402*c8dee2aaSAndroid Build Coastguard Worker                     fInstructions.pop_back();
403*c8dee2aaSAndroid Build Coastguard Worker 
404*c8dee2aaSAndroid Build Coastguard Worker                     // See if we can write this pop in a simpler way.
405*c8dee2aaSAndroid Build Coastguard Worker                     this->simplifyPopSlotsUnmasked(&dst);
406*c8dee2aaSAndroid Build Coastguard Worker 
407*c8dee2aaSAndroid Build Coastguard Worker                     // If simplification consumed the entire range, we're done!
408*c8dee2aaSAndroid Build Coastguard Worker                     if (dst.count == 0) {
409*c8dee2aaSAndroid Build Coastguard Worker                         return;
410*c8dee2aaSAndroid Build Coastguard Worker                     }
411*c8dee2aaSAndroid Build Coastguard Worker 
412*c8dee2aaSAndroid Build Coastguard Worker                     // Simplification did not consume the entire range. We are still responsible for
413*c8dee2aaSAndroid Build Coastguard Worker                     // copying-back and discarding any remaining slots.
414*c8dee2aaSAndroid Build Coastguard Worker                     this->copy_stack_to_slots_unmasked(dst);
415*c8dee2aaSAndroid Build Coastguard Worker                     count = dst.count;
416*c8dee2aaSAndroid Build Coastguard Worker                 }
417*c8dee2aaSAndroid Build Coastguard Worker                 break;
418*c8dee2aaSAndroid Build Coastguard Worker             }
419*c8dee2aaSAndroid Build Coastguard Worker             default:
420*c8dee2aaSAndroid Build Coastguard Worker                 break;
421*c8dee2aaSAndroid Build Coastguard Worker         }
422*c8dee2aaSAndroid Build Coastguard Worker 
423*c8dee2aaSAndroid Build Coastguard Worker         // This instruction wasn't a push.
424*c8dee2aaSAndroid Build Coastguard Worker         break;
425*c8dee2aaSAndroid Build Coastguard Worker     }
426*c8dee2aaSAndroid Build Coastguard Worker 
427*c8dee2aaSAndroid Build Coastguard Worker     if (count > 0) {
428*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::discard_stack, {}, count);
429*c8dee2aaSAndroid Build Coastguard Worker     }
430*c8dee2aaSAndroid Build Coastguard Worker }
431*c8dee2aaSAndroid Build Coastguard Worker 
label(int labelID)432*c8dee2aaSAndroid Build Coastguard Worker void Builder::label(int labelID) {
433*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(labelID >= 0 && labelID < fNumLabels);
434*c8dee2aaSAndroid Build Coastguard Worker 
435*c8dee2aaSAndroid Build Coastguard Worker     // If the previous instruction was a branch to this label, it's a no-op; jumping to the very
436*c8dee2aaSAndroid Build Coastguard Worker     // next instruction is effectively meaningless.
437*c8dee2aaSAndroid Build Coastguard Worker     while (const Instruction* lastInstruction = this->lastInstructionOnAnyStack()) {
438*c8dee2aaSAndroid Build Coastguard Worker         switch (lastInstruction->fOp) {
439*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::jump:
440*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::branch_if_all_lanes_active:
441*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::branch_if_any_lanes_active:
442*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::branch_if_no_lanes_active:
443*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::branch_if_no_active_lanes_on_stack_top_equal:
444*c8dee2aaSAndroid Build Coastguard Worker                 if (lastInstruction->fImmA == labelID) {
445*c8dee2aaSAndroid Build Coastguard Worker                     fInstructions.pop_back();
446*c8dee2aaSAndroid Build Coastguard Worker                     continue;
447*c8dee2aaSAndroid Build Coastguard Worker                 }
448*c8dee2aaSAndroid Build Coastguard Worker                 break;
449*c8dee2aaSAndroid Build Coastguard Worker 
450*c8dee2aaSAndroid Build Coastguard Worker             default:
451*c8dee2aaSAndroid Build Coastguard Worker                 break;
452*c8dee2aaSAndroid Build Coastguard Worker         }
453*c8dee2aaSAndroid Build Coastguard Worker         break;
454*c8dee2aaSAndroid Build Coastguard Worker     }
455*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::label, {}, labelID);
456*c8dee2aaSAndroid Build Coastguard Worker }
457*c8dee2aaSAndroid Build Coastguard Worker 
jump(int labelID)458*c8dee2aaSAndroid Build Coastguard Worker void Builder::jump(int labelID) {
459*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(labelID >= 0 && labelID < fNumLabels);
460*c8dee2aaSAndroid Build Coastguard Worker     if (const Instruction* lastInstruction = this->lastInstructionOnAnyStack()) {
461*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == BuilderOp::jump) {
462*c8dee2aaSAndroid Build Coastguard Worker             // The previous instruction was also `jump`, so this branch could never possibly occur.
463*c8dee2aaSAndroid Build Coastguard Worker             return;
464*c8dee2aaSAndroid Build Coastguard Worker         }
465*c8dee2aaSAndroid Build Coastguard Worker     }
466*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::jump, {}, labelID);
467*c8dee2aaSAndroid Build Coastguard Worker }
468*c8dee2aaSAndroid Build Coastguard Worker 
branch_if_any_lanes_active(int labelID)469*c8dee2aaSAndroid Build Coastguard Worker void Builder::branch_if_any_lanes_active(int labelID) {
470*c8dee2aaSAndroid Build Coastguard Worker     if (!this->executionMaskWritesAreEnabled()) {
471*c8dee2aaSAndroid Build Coastguard Worker         this->jump(labelID);
472*c8dee2aaSAndroid Build Coastguard Worker         return;
473*c8dee2aaSAndroid Build Coastguard Worker     }
474*c8dee2aaSAndroid Build Coastguard Worker 
475*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(labelID >= 0 && labelID < fNumLabels);
476*c8dee2aaSAndroid Build Coastguard Worker     if (const Instruction* lastInstruction = this->lastInstructionOnAnyStack()) {
477*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == BuilderOp::branch_if_any_lanes_active ||
478*c8dee2aaSAndroid Build Coastguard Worker             lastInstruction->fOp == BuilderOp::jump) {
479*c8dee2aaSAndroid Build Coastguard Worker             // The previous instruction was `jump` or `branch_if_any_lanes_active`, so this branch
480*c8dee2aaSAndroid Build Coastguard Worker             // could never possibly occur.
481*c8dee2aaSAndroid Build Coastguard Worker             return;
482*c8dee2aaSAndroid Build Coastguard Worker         }
483*c8dee2aaSAndroid Build Coastguard Worker     }
484*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::branch_if_any_lanes_active, {}, labelID);
485*c8dee2aaSAndroid Build Coastguard Worker }
486*c8dee2aaSAndroid Build Coastguard Worker 
branch_if_all_lanes_active(int labelID)487*c8dee2aaSAndroid Build Coastguard Worker void Builder::branch_if_all_lanes_active(int labelID) {
488*c8dee2aaSAndroid Build Coastguard Worker     if (!this->executionMaskWritesAreEnabled()) {
489*c8dee2aaSAndroid Build Coastguard Worker         this->jump(labelID);
490*c8dee2aaSAndroid Build Coastguard Worker         return;
491*c8dee2aaSAndroid Build Coastguard Worker     }
492*c8dee2aaSAndroid Build Coastguard Worker 
493*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(labelID >= 0 && labelID < fNumLabels);
494*c8dee2aaSAndroid Build Coastguard Worker     if (const Instruction* lastInstruction = this->lastInstructionOnAnyStack()) {
495*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == BuilderOp::branch_if_all_lanes_active ||
496*c8dee2aaSAndroid Build Coastguard Worker             lastInstruction->fOp == BuilderOp::jump) {
497*c8dee2aaSAndroid Build Coastguard Worker             // The previous instruction was `jump` or `branch_if_all_lanes_active`, so this branch
498*c8dee2aaSAndroid Build Coastguard Worker             // could never possibly occur.
499*c8dee2aaSAndroid Build Coastguard Worker             return;
500*c8dee2aaSAndroid Build Coastguard Worker         }
501*c8dee2aaSAndroid Build Coastguard Worker     }
502*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::branch_if_all_lanes_active, {}, labelID);
503*c8dee2aaSAndroid Build Coastguard Worker }
504*c8dee2aaSAndroid Build Coastguard Worker 
branch_if_no_lanes_active(int labelID)505*c8dee2aaSAndroid Build Coastguard Worker void Builder::branch_if_no_lanes_active(int labelID) {
506*c8dee2aaSAndroid Build Coastguard Worker     if (!this->executionMaskWritesAreEnabled()) {
507*c8dee2aaSAndroid Build Coastguard Worker         return;
508*c8dee2aaSAndroid Build Coastguard Worker     }
509*c8dee2aaSAndroid Build Coastguard Worker 
510*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(labelID >= 0 && labelID < fNumLabels);
511*c8dee2aaSAndroid Build Coastguard Worker     if (const Instruction* lastInstruction = this->lastInstructionOnAnyStack()) {
512*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == BuilderOp::branch_if_no_lanes_active ||
513*c8dee2aaSAndroid Build Coastguard Worker             lastInstruction->fOp == BuilderOp::jump) {
514*c8dee2aaSAndroid Build Coastguard Worker             // The previous instruction was `jump` or `branch_if_no_lanes_active`, so this branch
515*c8dee2aaSAndroid Build Coastguard Worker             // could never possibly occur.
516*c8dee2aaSAndroid Build Coastguard Worker             return;
517*c8dee2aaSAndroid Build Coastguard Worker         }
518*c8dee2aaSAndroid Build Coastguard Worker     }
519*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::branch_if_no_lanes_active, {}, labelID);
520*c8dee2aaSAndroid Build Coastguard Worker }
521*c8dee2aaSAndroid Build Coastguard Worker 
branch_if_no_active_lanes_on_stack_top_equal(int value,int labelID)522*c8dee2aaSAndroid Build Coastguard Worker void Builder::branch_if_no_active_lanes_on_stack_top_equal(int value, int labelID) {
523*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(labelID >= 0 && labelID < fNumLabels);
524*c8dee2aaSAndroid Build Coastguard Worker     if (const Instruction* lastInstruction = this->lastInstructionOnAnyStack()) {
525*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == BuilderOp::jump ||
526*c8dee2aaSAndroid Build Coastguard Worker             (lastInstruction->fOp == BuilderOp::branch_if_no_active_lanes_on_stack_top_equal &&
527*c8dee2aaSAndroid Build Coastguard Worker              lastInstruction->fImmB == value)) {
528*c8dee2aaSAndroid Build Coastguard Worker             // The previous instruction was `jump` or `branch_if_no_active_lanes_on_stack_top_equal`
529*c8dee2aaSAndroid Build Coastguard Worker             // (checking against the same value), so this branch could never possibly occur.
530*c8dee2aaSAndroid Build Coastguard Worker             return;
531*c8dee2aaSAndroid Build Coastguard Worker         }
532*c8dee2aaSAndroid Build Coastguard Worker     }
533*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::branch_if_no_active_lanes_on_stack_top_equal,
534*c8dee2aaSAndroid Build Coastguard Worker                             {}, labelID, value);
535*c8dee2aaSAndroid Build Coastguard Worker }
536*c8dee2aaSAndroid Build Coastguard Worker 
push_slots_or_immutable(SlotRange src,BuilderOp op)537*c8dee2aaSAndroid Build Coastguard Worker void Builder::push_slots_or_immutable(SlotRange src, BuilderOp op) {
538*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(src.count >= 0);
539*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstruction = this->lastInstruction()) {
540*c8dee2aaSAndroid Build Coastguard Worker         // If the previous instruction was pushing slots contiguous to this range, we can collapse
541*c8dee2aaSAndroid Build Coastguard Worker         // the two pushes into one larger push.
542*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == op &&
543*c8dee2aaSAndroid Build Coastguard Worker             lastInstruction->fSlotA + lastInstruction->fImmA == src.index) {
544*c8dee2aaSAndroid Build Coastguard Worker             lastInstruction->fImmA += src.count;
545*c8dee2aaSAndroid Build Coastguard Worker             src.count = 0;
546*c8dee2aaSAndroid Build Coastguard Worker         }
547*c8dee2aaSAndroid Build Coastguard Worker     }
548*c8dee2aaSAndroid Build Coastguard Worker 
549*c8dee2aaSAndroid Build Coastguard Worker     if (src.count > 0) {
550*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(op, {src.index}, src.count);
551*c8dee2aaSAndroid Build Coastguard Worker     }
552*c8dee2aaSAndroid Build Coastguard Worker 
553*c8dee2aaSAndroid Build Coastguard Worker     // Look for a sequence of "copy stack to X, discard stack, copy X to stack". This is a common
554*c8dee2aaSAndroid Build Coastguard Worker     // pattern when multiple operations in a row affect the same variable. When we see this, we can
555*c8dee2aaSAndroid Build Coastguard Worker     // eliminate both the discard and the push.
556*c8dee2aaSAndroid Build Coastguard Worker     if (fInstructions.size() >= 3) {
557*c8dee2aaSAndroid Build Coastguard Worker         const Instruction* pushInst        = this->lastInstruction(/*fromBack=*/0);
558*c8dee2aaSAndroid Build Coastguard Worker         const Instruction* discardInst     = this->lastInstruction(/*fromBack=*/1);
559*c8dee2aaSAndroid Build Coastguard Worker         const Instruction* copyToSlotsInst = this->lastInstruction(/*fromBack=*/2);
560*c8dee2aaSAndroid Build Coastguard Worker 
561*c8dee2aaSAndroid Build Coastguard Worker         if (pushInst && discardInst && copyToSlotsInst && pushInst->fOp == BuilderOp::push_slots) {
562*c8dee2aaSAndroid Build Coastguard Worker             int pushIndex = pushInst->fSlotA;
563*c8dee2aaSAndroid Build Coastguard Worker             int pushCount = pushInst->fImmA;
564*c8dee2aaSAndroid Build Coastguard Worker 
565*c8dee2aaSAndroid Build Coastguard Worker             // Look for a `discard_stack` matching our push count.
566*c8dee2aaSAndroid Build Coastguard Worker             if (discardInst->fOp == BuilderOp::discard_stack && discardInst->fImmA == pushCount) {
567*c8dee2aaSAndroid Build Coastguard Worker                 // Look for a `copy_stack_to_slots` matching our push.
568*c8dee2aaSAndroid Build Coastguard Worker                 if ((copyToSlotsInst->fOp == BuilderOp::copy_stack_to_slots ||
569*c8dee2aaSAndroid Build Coastguard Worker                      copyToSlotsInst->fOp == BuilderOp::copy_stack_to_slots_unmasked) &&
570*c8dee2aaSAndroid Build Coastguard Worker                     copyToSlotsInst->fSlotA == pushIndex && copyToSlotsInst->fImmA == pushCount) {
571*c8dee2aaSAndroid Build Coastguard Worker                     // We found a matching sequence. Remove the discard and push.
572*c8dee2aaSAndroid Build Coastguard Worker                     fInstructions.pop_back();
573*c8dee2aaSAndroid Build Coastguard Worker                     fInstructions.pop_back();
574*c8dee2aaSAndroid Build Coastguard Worker                     return;
575*c8dee2aaSAndroid Build Coastguard Worker                 }
576*c8dee2aaSAndroid Build Coastguard Worker             }
577*c8dee2aaSAndroid Build Coastguard Worker         }
578*c8dee2aaSAndroid Build Coastguard Worker     }
579*c8dee2aaSAndroid Build Coastguard Worker }
580*c8dee2aaSAndroid Build Coastguard Worker 
push_slots_or_immutable_indirect(SlotRange fixedRange,int dynamicStackID,SlotRange limitRange,BuilderOp op)581*c8dee2aaSAndroid Build Coastguard Worker void Builder::push_slots_or_immutable_indirect(SlotRange fixedRange,
582*c8dee2aaSAndroid Build Coastguard Worker                                                int dynamicStackID,
583*c8dee2aaSAndroid Build Coastguard Worker                                                SlotRange limitRange,
584*c8dee2aaSAndroid Build Coastguard Worker                                                BuilderOp op) {
585*c8dee2aaSAndroid Build Coastguard Worker     // SlotA: fixed-range start
586*c8dee2aaSAndroid Build Coastguard Worker     // SlotB: limit-range end
587*c8dee2aaSAndroid Build Coastguard Worker     // immA: number of slots
588*c8dee2aaSAndroid Build Coastguard Worker     // immB: dynamic stack ID
589*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(op,
590*c8dee2aaSAndroid Build Coastguard Worker                             {fixedRange.index, limitRange.index + limitRange.count},
591*c8dee2aaSAndroid Build Coastguard Worker                             fixedRange.count,
592*c8dee2aaSAndroid Build Coastguard Worker                             dynamicStackID);
593*c8dee2aaSAndroid Build Coastguard Worker }
594*c8dee2aaSAndroid Build Coastguard Worker 
push_uniform(SlotRange src)595*c8dee2aaSAndroid Build Coastguard Worker void Builder::push_uniform(SlotRange src) {
596*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(src.count >= 0);
597*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstruction = this->lastInstruction()) {
598*c8dee2aaSAndroid Build Coastguard Worker         // If the previous instruction was pushing uniforms contiguous to this range, we can
599*c8dee2aaSAndroid Build Coastguard Worker         // collapse the two pushes into one larger push.
600*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == BuilderOp::push_uniform &&
601*c8dee2aaSAndroid Build Coastguard Worker             lastInstruction->fSlotA + lastInstruction->fImmA == src.index) {
602*c8dee2aaSAndroid Build Coastguard Worker             lastInstruction->fImmA += src.count;
603*c8dee2aaSAndroid Build Coastguard Worker             return;
604*c8dee2aaSAndroid Build Coastguard Worker         }
605*c8dee2aaSAndroid Build Coastguard Worker     }
606*c8dee2aaSAndroid Build Coastguard Worker 
607*c8dee2aaSAndroid Build Coastguard Worker     if (src.count > 0) {
608*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::push_uniform, {src.index}, src.count);
609*c8dee2aaSAndroid Build Coastguard Worker     }
610*c8dee2aaSAndroid Build Coastguard Worker }
611*c8dee2aaSAndroid Build Coastguard Worker 
push_uniform_indirect(SlotRange fixedRange,int dynamicStackID,SlotRange limitRange)612*c8dee2aaSAndroid Build Coastguard Worker void Builder::push_uniform_indirect(SlotRange fixedRange,
613*c8dee2aaSAndroid Build Coastguard Worker                                     int dynamicStackID,
614*c8dee2aaSAndroid Build Coastguard Worker                                     SlotRange limitRange) {
615*c8dee2aaSAndroid Build Coastguard Worker     // SlotA: fixed-range start
616*c8dee2aaSAndroid Build Coastguard Worker     // SlotB: limit-range end
617*c8dee2aaSAndroid Build Coastguard Worker     // immA: number of slots
618*c8dee2aaSAndroid Build Coastguard Worker     // immB: dynamic stack ID
619*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::push_uniform_indirect,
620*c8dee2aaSAndroid Build Coastguard Worker                             {fixedRange.index, limitRange.index + limitRange.count},
621*c8dee2aaSAndroid Build Coastguard Worker                             fixedRange.count,
622*c8dee2aaSAndroid Build Coastguard Worker                             dynamicStackID);
623*c8dee2aaSAndroid Build Coastguard Worker }
624*c8dee2aaSAndroid Build Coastguard Worker 
trace_var_indirect(int traceMaskStackID,SlotRange fixedRange,int dynamicStackID,SlotRange limitRange)625*c8dee2aaSAndroid Build Coastguard Worker void Builder::trace_var_indirect(int traceMaskStackID,
626*c8dee2aaSAndroid Build Coastguard Worker                                  SlotRange fixedRange,
627*c8dee2aaSAndroid Build Coastguard Worker                                  int dynamicStackID,
628*c8dee2aaSAndroid Build Coastguard Worker                                  SlotRange limitRange) {
629*c8dee2aaSAndroid Build Coastguard Worker     // SlotA: fixed-range start
630*c8dee2aaSAndroid Build Coastguard Worker     // SlotB: limit-range end
631*c8dee2aaSAndroid Build Coastguard Worker     // immA: trace-mask stack ID
632*c8dee2aaSAndroid Build Coastguard Worker     // immB: number of slots
633*c8dee2aaSAndroid Build Coastguard Worker     // immC: dynamic stack ID
634*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::trace_var_indirect,
635*c8dee2aaSAndroid Build Coastguard Worker                             {fixedRange.index, limitRange.index + limitRange.count},
636*c8dee2aaSAndroid Build Coastguard Worker                             traceMaskStackID,
637*c8dee2aaSAndroid Build Coastguard Worker                             fixedRange.count,
638*c8dee2aaSAndroid Build Coastguard Worker                             dynamicStackID);
639*c8dee2aaSAndroid Build Coastguard Worker }
640*c8dee2aaSAndroid Build Coastguard Worker 
push_constant_i(int32_t val,int count)641*c8dee2aaSAndroid Build Coastguard Worker void Builder::push_constant_i(int32_t val, int count) {
642*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(count >= 0);
643*c8dee2aaSAndroid Build Coastguard Worker     if (count > 0) {
644*c8dee2aaSAndroid Build Coastguard Worker         if (Instruction* lastInstruction = this->lastInstruction()) {
645*c8dee2aaSAndroid Build Coastguard Worker             // If the previous op is pushing the same value, we can just push more of them.
646*c8dee2aaSAndroid Build Coastguard Worker             if (lastInstruction->fOp == BuilderOp::push_constant && lastInstruction->fImmB == val) {
647*c8dee2aaSAndroid Build Coastguard Worker                 lastInstruction->fImmA += count;
648*c8dee2aaSAndroid Build Coastguard Worker                 return;
649*c8dee2aaSAndroid Build Coastguard Worker             }
650*c8dee2aaSAndroid Build Coastguard Worker         }
651*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::push_constant, {}, count, val);
652*c8dee2aaSAndroid Build Coastguard Worker     }
653*c8dee2aaSAndroid Build Coastguard Worker }
654*c8dee2aaSAndroid Build Coastguard Worker 
push_duplicates(int count)655*c8dee2aaSAndroid Build Coastguard Worker void Builder::push_duplicates(int count) {
656*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstruction = this->lastInstruction()) {
657*c8dee2aaSAndroid Build Coastguard Worker         // If the previous op is pushing a constant, we can just push more of them.
658*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == BuilderOp::push_constant) {
659*c8dee2aaSAndroid Build Coastguard Worker             lastInstruction->fImmA += count;
660*c8dee2aaSAndroid Build Coastguard Worker             return;
661*c8dee2aaSAndroid Build Coastguard Worker         }
662*c8dee2aaSAndroid Build Coastguard Worker     }
663*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(count >= 0);
664*c8dee2aaSAndroid Build Coastguard Worker     if (count >= 3) {
665*c8dee2aaSAndroid Build Coastguard Worker         // Use a swizzle to splat the input into a 4-slot value.
666*c8dee2aaSAndroid Build Coastguard Worker         this->swizzle(/*consumedSlots=*/1, {0, 0, 0, 0});
667*c8dee2aaSAndroid Build Coastguard Worker         count -= 3;
668*c8dee2aaSAndroid Build Coastguard Worker     }
669*c8dee2aaSAndroid Build Coastguard Worker     for (; count >= 4; count -= 4) {
670*c8dee2aaSAndroid Build Coastguard Worker         // Clone the splatted value four slots at a time.
671*c8dee2aaSAndroid Build Coastguard Worker         this->push_clone(/*numSlots=*/4);
672*c8dee2aaSAndroid Build Coastguard Worker     }
673*c8dee2aaSAndroid Build Coastguard Worker     // Use a swizzle or clone to handle the trailing items.
674*c8dee2aaSAndroid Build Coastguard Worker     switch (count) {
675*c8dee2aaSAndroid Build Coastguard Worker         case 3:  this->swizzle(/*consumedSlots=*/1, {0, 0, 0, 0}); break;
676*c8dee2aaSAndroid Build Coastguard Worker         case 2:  this->swizzle(/*consumedSlots=*/1, {0, 0, 0});    break;
677*c8dee2aaSAndroid Build Coastguard Worker         case 1:  this->push_clone(/*numSlots=*/1);                 break;
678*c8dee2aaSAndroid Build Coastguard Worker         default: break;
679*c8dee2aaSAndroid Build Coastguard Worker     }
680*c8dee2aaSAndroid Build Coastguard Worker }
681*c8dee2aaSAndroid Build Coastguard Worker 
push_clone(int numSlots,int offsetFromStackTop)682*c8dee2aaSAndroid Build Coastguard Worker void Builder::push_clone(int numSlots, int offsetFromStackTop) {
683*c8dee2aaSAndroid Build Coastguard Worker     // If we are cloning the stack top...
684*c8dee2aaSAndroid Build Coastguard Worker     if (numSlots == 1 && offsetFromStackTop == 0) {
685*c8dee2aaSAndroid Build Coastguard Worker         // ... and the previous op is pushing a constant...
686*c8dee2aaSAndroid Build Coastguard Worker         if (Instruction* lastInstruction = this->lastInstruction()) {
687*c8dee2aaSAndroid Build Coastguard Worker             if (lastInstruction->fOp == BuilderOp::push_constant) {
688*c8dee2aaSAndroid Build Coastguard Worker                 // ... we can just push more of them.
689*c8dee2aaSAndroid Build Coastguard Worker                 lastInstruction->fImmA += 1;
690*c8dee2aaSAndroid Build Coastguard Worker                 return;
691*c8dee2aaSAndroid Build Coastguard Worker             }
692*c8dee2aaSAndroid Build Coastguard Worker         }
693*c8dee2aaSAndroid Build Coastguard Worker     }
694*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::push_clone, {}, numSlots, numSlots + offsetFromStackTop);
695*c8dee2aaSAndroid Build Coastguard Worker }
696*c8dee2aaSAndroid Build Coastguard Worker 
push_clone_from_stack(SlotRange range,int otherStackID,int offsetFromStackTop)697*c8dee2aaSAndroid Build Coastguard Worker void Builder::push_clone_from_stack(SlotRange range, int otherStackID, int offsetFromStackTop) {
698*c8dee2aaSAndroid Build Coastguard Worker     // immA: number of slots
699*c8dee2aaSAndroid Build Coastguard Worker     // immB: other stack ID
700*c8dee2aaSAndroid Build Coastguard Worker     // immC: offset from stack top
701*c8dee2aaSAndroid Build Coastguard Worker     offsetFromStackTop -= range.index;
702*c8dee2aaSAndroid Build Coastguard Worker 
703*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstruction = this->lastInstruction()) {
704*c8dee2aaSAndroid Build Coastguard Worker         // If the previous op is also pushing a clone...
705*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == BuilderOp::push_clone_from_stack &&
706*c8dee2aaSAndroid Build Coastguard Worker             // ... from the same stack...
707*c8dee2aaSAndroid Build Coastguard Worker             lastInstruction->fImmB == otherStackID &&
708*c8dee2aaSAndroid Build Coastguard Worker             // ... and this clone starts at the same place that the last clone ends...
709*c8dee2aaSAndroid Build Coastguard Worker             lastInstruction->fImmC - lastInstruction->fImmA == offsetFromStackTop) {
710*c8dee2aaSAndroid Build Coastguard Worker             // ... just extend the existing clone-op.
711*c8dee2aaSAndroid Build Coastguard Worker             lastInstruction->fImmA += range.count;
712*c8dee2aaSAndroid Build Coastguard Worker             return;
713*c8dee2aaSAndroid Build Coastguard Worker         }
714*c8dee2aaSAndroid Build Coastguard Worker     }
715*c8dee2aaSAndroid Build Coastguard Worker 
716*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::push_clone_from_stack, {},
717*c8dee2aaSAndroid Build Coastguard Worker                             range.count, otherStackID, offsetFromStackTop);
718*c8dee2aaSAndroid Build Coastguard Worker }
719*c8dee2aaSAndroid Build Coastguard Worker 
push_clone_indirect_from_stack(SlotRange fixedOffset,int dynamicStackID,int otherStackID,int offsetFromStackTop)720*c8dee2aaSAndroid Build Coastguard Worker void Builder::push_clone_indirect_from_stack(SlotRange fixedOffset,
721*c8dee2aaSAndroid Build Coastguard Worker                                              int dynamicStackID,
722*c8dee2aaSAndroid Build Coastguard Worker                                              int otherStackID,
723*c8dee2aaSAndroid Build Coastguard Worker                                              int offsetFromStackTop) {
724*c8dee2aaSAndroid Build Coastguard Worker     // immA: number of slots
725*c8dee2aaSAndroid Build Coastguard Worker     // immB: other stack ID
726*c8dee2aaSAndroid Build Coastguard Worker     // immC: offset from stack top
727*c8dee2aaSAndroid Build Coastguard Worker     // immD: dynamic stack ID
728*c8dee2aaSAndroid Build Coastguard Worker     offsetFromStackTop -= fixedOffset.index;
729*c8dee2aaSAndroid Build Coastguard Worker 
730*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::push_clone_indirect_from_stack, {},
731*c8dee2aaSAndroid Build Coastguard Worker                             fixedOffset.count, otherStackID, offsetFromStackTop, dynamicStackID);
732*c8dee2aaSAndroid Build Coastguard Worker }
733*c8dee2aaSAndroid Build Coastguard Worker 
pop_slots(SlotRange dst)734*c8dee2aaSAndroid Build Coastguard Worker void Builder::pop_slots(SlotRange dst) {
735*c8dee2aaSAndroid Build Coastguard Worker     if (!this->executionMaskWritesAreEnabled()) {
736*c8dee2aaSAndroid Build Coastguard Worker         this->pop_slots_unmasked(dst);
737*c8dee2aaSAndroid Build Coastguard Worker         return;
738*c8dee2aaSAndroid Build Coastguard Worker     }
739*c8dee2aaSAndroid Build Coastguard Worker 
740*c8dee2aaSAndroid Build Coastguard Worker     this->copy_stack_to_slots(dst);
741*c8dee2aaSAndroid Build Coastguard Worker     this->discard_stack(dst.count);
742*c8dee2aaSAndroid Build Coastguard Worker }
743*c8dee2aaSAndroid Build Coastguard Worker 
simplifyPopSlotsUnmasked(SlotRange * dst)744*c8dee2aaSAndroid Build Coastguard Worker void Builder::simplifyPopSlotsUnmasked(SlotRange* dst) {
745*c8dee2aaSAndroid Build Coastguard Worker     if (!dst->count) {
746*c8dee2aaSAndroid Build Coastguard Worker         // There's nothing left to simplify.
747*c8dee2aaSAndroid Build Coastguard Worker         return;
748*c8dee2aaSAndroid Build Coastguard Worker     }
749*c8dee2aaSAndroid Build Coastguard Worker     Instruction* lastInstruction = this->lastInstruction();
750*c8dee2aaSAndroid Build Coastguard Worker     if (!lastInstruction) {
751*c8dee2aaSAndroid Build Coastguard Worker         // There's nothing left to simplify.
752*c8dee2aaSAndroid Build Coastguard Worker         return;
753*c8dee2aaSAndroid Build Coastguard Worker     }
754*c8dee2aaSAndroid Build Coastguard Worker     BuilderOp lastOp = lastInstruction->fOp;
755*c8dee2aaSAndroid Build Coastguard Worker 
756*c8dee2aaSAndroid Build Coastguard Worker     // If the last instruction is pushing a constant, we can simplify it by copying the constant
757*c8dee2aaSAndroid Build Coastguard Worker     // directly into the destination slot.
758*c8dee2aaSAndroid Build Coastguard Worker     if (lastOp == BuilderOp::push_constant) {
759*c8dee2aaSAndroid Build Coastguard Worker         // Get the last slot.
760*c8dee2aaSAndroid Build Coastguard Worker         int32_t value = lastInstruction->fImmB;
761*c8dee2aaSAndroid Build Coastguard Worker         lastInstruction->fImmA--;
762*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fImmA == 0) {
763*c8dee2aaSAndroid Build Coastguard Worker             fInstructions.pop_back();
764*c8dee2aaSAndroid Build Coastguard Worker         }
765*c8dee2aaSAndroid Build Coastguard Worker 
766*c8dee2aaSAndroid Build Coastguard Worker         // Consume one destination slot.
767*c8dee2aaSAndroid Build Coastguard Worker         dst->count--;
768*c8dee2aaSAndroid Build Coastguard Worker         Slot destinationSlot = dst->index + dst->count;
769*c8dee2aaSAndroid Build Coastguard Worker 
770*c8dee2aaSAndroid Build Coastguard Worker         // Continue simplifying if possible.
771*c8dee2aaSAndroid Build Coastguard Worker         this->simplifyPopSlotsUnmasked(dst);
772*c8dee2aaSAndroid Build Coastguard Worker 
773*c8dee2aaSAndroid Build Coastguard Worker         // Write the constant directly to the destination slot.
774*c8dee2aaSAndroid Build Coastguard Worker         this->copy_constant(destinationSlot, value);
775*c8dee2aaSAndroid Build Coastguard Worker         return;
776*c8dee2aaSAndroid Build Coastguard Worker     }
777*c8dee2aaSAndroid Build Coastguard Worker 
778*c8dee2aaSAndroid Build Coastguard Worker     // If the last instruction is pushing a uniform, we can simplify it by copying the uniform
779*c8dee2aaSAndroid Build Coastguard Worker     // directly into the destination slot.
780*c8dee2aaSAndroid Build Coastguard Worker     if (lastOp == BuilderOp::push_uniform) {
781*c8dee2aaSAndroid Build Coastguard Worker         // Get the last slot.
782*c8dee2aaSAndroid Build Coastguard Worker         Slot sourceSlot = lastInstruction->fSlotA + lastInstruction->fImmA - 1;
783*c8dee2aaSAndroid Build Coastguard Worker         lastInstruction->fImmA--;
784*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fImmA == 0) {
785*c8dee2aaSAndroid Build Coastguard Worker             fInstructions.pop_back();
786*c8dee2aaSAndroid Build Coastguard Worker         }
787*c8dee2aaSAndroid Build Coastguard Worker 
788*c8dee2aaSAndroid Build Coastguard Worker         // Consume one destination slot.
789*c8dee2aaSAndroid Build Coastguard Worker         dst->count--;
790*c8dee2aaSAndroid Build Coastguard Worker         Slot destinationSlot = dst->index + dst->count;
791*c8dee2aaSAndroid Build Coastguard Worker 
792*c8dee2aaSAndroid Build Coastguard Worker         // Continue simplifying if possible.
793*c8dee2aaSAndroid Build Coastguard Worker         this->simplifyPopSlotsUnmasked(dst);
794*c8dee2aaSAndroid Build Coastguard Worker 
795*c8dee2aaSAndroid Build Coastguard Worker         // Write the constant directly to the destination slot.
796*c8dee2aaSAndroid Build Coastguard Worker         this->copy_uniform_to_slots_unmasked({destinationSlot, 1}, {sourceSlot, 1});
797*c8dee2aaSAndroid Build Coastguard Worker         return;
798*c8dee2aaSAndroid Build Coastguard Worker     }
799*c8dee2aaSAndroid Build Coastguard Worker 
800*c8dee2aaSAndroid Build Coastguard Worker     // If the last instruction is pushing a slot or immutable, we can just copy that slot.
801*c8dee2aaSAndroid Build Coastguard Worker     if (lastOp == BuilderOp::push_slots || lastOp == BuilderOp::push_immutable) {
802*c8dee2aaSAndroid Build Coastguard Worker         // Get the last slot.
803*c8dee2aaSAndroid Build Coastguard Worker         Slot sourceSlot = lastInstruction->fSlotA + lastInstruction->fImmA - 1;
804*c8dee2aaSAndroid Build Coastguard Worker         lastInstruction->fImmA--;
805*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fImmA == 0) {
806*c8dee2aaSAndroid Build Coastguard Worker             fInstructions.pop_back();
807*c8dee2aaSAndroid Build Coastguard Worker         }
808*c8dee2aaSAndroid Build Coastguard Worker 
809*c8dee2aaSAndroid Build Coastguard Worker         // Consume one destination slot.
810*c8dee2aaSAndroid Build Coastguard Worker         dst->count--;
811*c8dee2aaSAndroid Build Coastguard Worker         Slot destinationSlot = dst->index + dst->count;
812*c8dee2aaSAndroid Build Coastguard Worker 
813*c8dee2aaSAndroid Build Coastguard Worker         // Try once more.
814*c8dee2aaSAndroid Build Coastguard Worker         this->simplifyPopSlotsUnmasked(dst);
815*c8dee2aaSAndroid Build Coastguard Worker 
816*c8dee2aaSAndroid Build Coastguard Worker         // Copy the slot directly.
817*c8dee2aaSAndroid Build Coastguard Worker         if (lastOp == BuilderOp::push_slots) {
818*c8dee2aaSAndroid Build Coastguard Worker             if (destinationSlot != sourceSlot) {
819*c8dee2aaSAndroid Build Coastguard Worker                 this->copy_slots_unmasked({destinationSlot, 1}, {sourceSlot, 1});
820*c8dee2aaSAndroid Build Coastguard Worker             } else {
821*c8dee2aaSAndroid Build Coastguard Worker                 // Copying from a value-slot into the same value-slot is a no-op.
822*c8dee2aaSAndroid Build Coastguard Worker             }
823*c8dee2aaSAndroid Build Coastguard Worker         } else {
824*c8dee2aaSAndroid Build Coastguard Worker             // Copy from immutable data directly to the destination slot.
825*c8dee2aaSAndroid Build Coastguard Worker             this->copy_immutable_unmasked({destinationSlot, 1}, {sourceSlot, 1});
826*c8dee2aaSAndroid Build Coastguard Worker         }
827*c8dee2aaSAndroid Build Coastguard Worker         return;
828*c8dee2aaSAndroid Build Coastguard Worker     }
829*c8dee2aaSAndroid Build Coastguard Worker }
830*c8dee2aaSAndroid Build Coastguard Worker 
pop_slots_unmasked(SlotRange dst)831*c8dee2aaSAndroid Build Coastguard Worker void Builder::pop_slots_unmasked(SlotRange dst) {
832*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(dst.count >= 0);
833*c8dee2aaSAndroid Build Coastguard Worker     this->copy_stack_to_slots_unmasked(dst);
834*c8dee2aaSAndroid Build Coastguard Worker     this->discard_stack(dst.count);
835*c8dee2aaSAndroid Build Coastguard Worker }
836*c8dee2aaSAndroid Build Coastguard Worker 
exchange_src()837*c8dee2aaSAndroid Build Coastguard Worker void Builder::exchange_src() {
838*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstruction = this->lastInstruction()) {
839*c8dee2aaSAndroid Build Coastguard Worker         // If the previous op is also an exchange-src...
840*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == BuilderOp::exchange_src) {
841*c8dee2aaSAndroid Build Coastguard Worker             // ... both ops can be eliminated. A double-swap is a no-op.
842*c8dee2aaSAndroid Build Coastguard Worker             fInstructions.pop_back();
843*c8dee2aaSAndroid Build Coastguard Worker             return;
844*c8dee2aaSAndroid Build Coastguard Worker         }
845*c8dee2aaSAndroid Build Coastguard Worker     }
846*c8dee2aaSAndroid Build Coastguard Worker 
847*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::exchange_src, {});
848*c8dee2aaSAndroid Build Coastguard Worker }
849*c8dee2aaSAndroid Build Coastguard Worker 
pop_src_rgba()850*c8dee2aaSAndroid Build Coastguard Worker void Builder::pop_src_rgba() {
851*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstruction = this->lastInstruction()) {
852*c8dee2aaSAndroid Build Coastguard Worker         // If the previous op is exchanging src.rgba with the stack...
853*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == BuilderOp::exchange_src) {
854*c8dee2aaSAndroid Build Coastguard Worker             // ... both ops can be eliminated. It's just sliding the color back and forth.
855*c8dee2aaSAndroid Build Coastguard Worker             fInstructions.pop_back();
856*c8dee2aaSAndroid Build Coastguard Worker             this->discard_stack(4);
857*c8dee2aaSAndroid Build Coastguard Worker             return;
858*c8dee2aaSAndroid Build Coastguard Worker         }
859*c8dee2aaSAndroid Build Coastguard Worker     }
860*c8dee2aaSAndroid Build Coastguard Worker 
861*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::pop_src_rgba, {});
862*c8dee2aaSAndroid Build Coastguard Worker }
863*c8dee2aaSAndroid Build Coastguard Worker 
copy_stack_to_slots(SlotRange dst,int offsetFromStackTop)864*c8dee2aaSAndroid Build Coastguard Worker void Builder::copy_stack_to_slots(SlotRange dst, int offsetFromStackTop) {
865*c8dee2aaSAndroid Build Coastguard Worker     // If the execution mask is known to be all-true, then we can ignore the write mask.
866*c8dee2aaSAndroid Build Coastguard Worker     if (!this->executionMaskWritesAreEnabled()) {
867*c8dee2aaSAndroid Build Coastguard Worker         this->copy_stack_to_slots_unmasked(dst, offsetFromStackTop);
868*c8dee2aaSAndroid Build Coastguard Worker         return;
869*c8dee2aaSAndroid Build Coastguard Worker     }
870*c8dee2aaSAndroid Build Coastguard Worker 
871*c8dee2aaSAndroid Build Coastguard Worker     // If the last instruction copied the previous stack slots, just extend it.
872*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstruction = this->lastInstruction()) {
873*c8dee2aaSAndroid Build Coastguard Worker         // If the last op is copy-stack-to-slots...
874*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == BuilderOp::copy_stack_to_slots &&
875*c8dee2aaSAndroid Build Coastguard Worker             // and this op's destination is immediately after the last copy-slots-op's destination
876*c8dee2aaSAndroid Build Coastguard Worker             lastInstruction->fSlotA + lastInstruction->fImmA == dst.index &&
877*c8dee2aaSAndroid Build Coastguard Worker             // and this op's source is immediately after the last copy-slots-op's source
878*c8dee2aaSAndroid Build Coastguard Worker             lastInstruction->fImmB - lastInstruction->fImmA == offsetFromStackTop) {
879*c8dee2aaSAndroid Build Coastguard Worker             // then we can just extend the copy!
880*c8dee2aaSAndroid Build Coastguard Worker             lastInstruction->fImmA += dst.count;
881*c8dee2aaSAndroid Build Coastguard Worker             return;
882*c8dee2aaSAndroid Build Coastguard Worker         }
883*c8dee2aaSAndroid Build Coastguard Worker     }
884*c8dee2aaSAndroid Build Coastguard Worker 
885*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::copy_stack_to_slots, {dst.index},
886*c8dee2aaSAndroid Build Coastguard Worker                             dst.count, offsetFromStackTop);
887*c8dee2aaSAndroid Build Coastguard Worker }
888*c8dee2aaSAndroid Build Coastguard Worker 
copy_stack_to_slots_indirect(SlotRange fixedRange,int dynamicStackID,SlotRange limitRange)889*c8dee2aaSAndroid Build Coastguard Worker void Builder::copy_stack_to_slots_indirect(SlotRange fixedRange,
890*c8dee2aaSAndroid Build Coastguard Worker                                            int dynamicStackID,
891*c8dee2aaSAndroid Build Coastguard Worker                                            SlotRange limitRange) {
892*c8dee2aaSAndroid Build Coastguard Worker     // SlotA: fixed-range start
893*c8dee2aaSAndroid Build Coastguard Worker     // SlotB: limit-range end
894*c8dee2aaSAndroid Build Coastguard Worker     // immA: number of slots
895*c8dee2aaSAndroid Build Coastguard Worker     // immB: dynamic stack ID
896*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::copy_stack_to_slots_indirect,
897*c8dee2aaSAndroid Build Coastguard Worker                             {fixedRange.index, limitRange.index + limitRange.count},
898*c8dee2aaSAndroid Build Coastguard Worker                             fixedRange.count,
899*c8dee2aaSAndroid Build Coastguard Worker                             dynamicStackID);
900*c8dee2aaSAndroid Build Coastguard Worker }
901*c8dee2aaSAndroid Build Coastguard Worker 
slot_ranges_overlap(SlotRange x,SlotRange y)902*c8dee2aaSAndroid Build Coastguard Worker static bool slot_ranges_overlap(SlotRange x, SlotRange y) {
903*c8dee2aaSAndroid Build Coastguard Worker     return x.index < y.index + y.count &&
904*c8dee2aaSAndroid Build Coastguard Worker            y.index < x.index + x.count;
905*c8dee2aaSAndroid Build Coastguard Worker }
906*c8dee2aaSAndroid Build Coastguard Worker 
copy_constant(Slot slot,int constantValue)907*c8dee2aaSAndroid Build Coastguard Worker void Builder::copy_constant(Slot slot, int constantValue) {
908*c8dee2aaSAndroid Build Coastguard Worker     // If the last instruction copied the same constant, just extend it.
909*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstr = this->lastInstruction()) {
910*c8dee2aaSAndroid Build Coastguard Worker         // If the last op is copy-constant...
911*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstr->fOp == BuilderOp::copy_constant &&
912*c8dee2aaSAndroid Build Coastguard Worker             // ... and has the same value...
913*c8dee2aaSAndroid Build Coastguard Worker             lastInstr->fImmB == constantValue &&
914*c8dee2aaSAndroid Build Coastguard Worker             // ... and the slot is immediately after the last copy-constant's destination...
915*c8dee2aaSAndroid Build Coastguard Worker             lastInstr->fSlotA + lastInstr->fImmA == slot) {
916*c8dee2aaSAndroid Build Coastguard Worker             // ... then we can extend the copy!
917*c8dee2aaSAndroid Build Coastguard Worker             lastInstr->fImmA += 1;
918*c8dee2aaSAndroid Build Coastguard Worker             return;
919*c8dee2aaSAndroid Build Coastguard Worker         }
920*c8dee2aaSAndroid Build Coastguard Worker     }
921*c8dee2aaSAndroid Build Coastguard Worker 
922*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::copy_constant, {slot}, 1, constantValue);
923*c8dee2aaSAndroid Build Coastguard Worker }
924*c8dee2aaSAndroid Build Coastguard Worker 
copy_slots_unmasked(SlotRange dst,SlotRange src)925*c8dee2aaSAndroid Build Coastguard Worker void Builder::copy_slots_unmasked(SlotRange dst, SlotRange src) {
926*c8dee2aaSAndroid Build Coastguard Worker     // If the last instruction copied adjacent slots, just extend it.
927*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstr = this->lastInstruction()) {
928*c8dee2aaSAndroid Build Coastguard Worker         // If the last op is a match...
929*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstr->fOp == BuilderOp::copy_slot_unmasked &&
930*c8dee2aaSAndroid Build Coastguard Worker             // and this op's destination is immediately after the last copy-slots-op's destination
931*c8dee2aaSAndroid Build Coastguard Worker             lastInstr->fSlotA + lastInstr->fImmA == dst.index &&
932*c8dee2aaSAndroid Build Coastguard Worker             // and this op's source is immediately after the last copy-slots-op's source
933*c8dee2aaSAndroid Build Coastguard Worker             lastInstr->fSlotB + lastInstr->fImmA == src.index &&
934*c8dee2aaSAndroid Build Coastguard Worker             // and the source/dest ranges will not overlap
935*c8dee2aaSAndroid Build Coastguard Worker             !slot_ranges_overlap({lastInstr->fSlotB, lastInstr->fImmA + dst.count},
936*c8dee2aaSAndroid Build Coastguard Worker                                  {lastInstr->fSlotA, lastInstr->fImmA + dst.count})) {
937*c8dee2aaSAndroid Build Coastguard Worker             // then we can just extend the copy!
938*c8dee2aaSAndroid Build Coastguard Worker             lastInstr->fImmA += dst.count;
939*c8dee2aaSAndroid Build Coastguard Worker             return;
940*c8dee2aaSAndroid Build Coastguard Worker         }
941*c8dee2aaSAndroid Build Coastguard Worker     }
942*c8dee2aaSAndroid Build Coastguard Worker 
943*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(dst.count == src.count);
944*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::copy_slot_unmasked, {dst.index, src.index}, dst.count);
945*c8dee2aaSAndroid Build Coastguard Worker }
946*c8dee2aaSAndroid Build Coastguard Worker 
copy_immutable_unmasked(SlotRange dst,SlotRange src)947*c8dee2aaSAndroid Build Coastguard Worker void Builder::copy_immutable_unmasked(SlotRange dst, SlotRange src) {
948*c8dee2aaSAndroid Build Coastguard Worker     // If the last instruction copied adjacent immutable data, just extend it.
949*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstr = this->lastInstruction()) {
950*c8dee2aaSAndroid Build Coastguard Worker         // If the last op is a match...
951*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstr->fOp == BuilderOp::copy_immutable_unmasked &&
952*c8dee2aaSAndroid Build Coastguard Worker             // and this op's destination is immediately after the last copy-slots-op's destination
953*c8dee2aaSAndroid Build Coastguard Worker             lastInstr->fSlotA + lastInstr->fImmA == dst.index &&
954*c8dee2aaSAndroid Build Coastguard Worker             // and this op's source is immediately after the last copy-slots-op's source
955*c8dee2aaSAndroid Build Coastguard Worker             lastInstr->fSlotB + lastInstr->fImmA == src.index) {
956*c8dee2aaSAndroid Build Coastguard Worker             // then we can just extend the copy!
957*c8dee2aaSAndroid Build Coastguard Worker             lastInstr->fImmA += dst.count;
958*c8dee2aaSAndroid Build Coastguard Worker             return;
959*c8dee2aaSAndroid Build Coastguard Worker         }
960*c8dee2aaSAndroid Build Coastguard Worker     }
961*c8dee2aaSAndroid Build Coastguard Worker 
962*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(dst.count == src.count);
963*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::copy_immutable_unmasked, {dst.index, src.index}, dst.count);
964*c8dee2aaSAndroid Build Coastguard Worker }
965*c8dee2aaSAndroid Build Coastguard Worker 
copy_uniform_to_slots_unmasked(SlotRange dst,SlotRange src)966*c8dee2aaSAndroid Build Coastguard Worker void Builder::copy_uniform_to_slots_unmasked(SlotRange dst, SlotRange src) {
967*c8dee2aaSAndroid Build Coastguard Worker     // If the last instruction copied adjacent uniforms, just extend it.
968*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstr = this->lastInstruction()) {
969*c8dee2aaSAndroid Build Coastguard Worker         // If the last op is copy-constant...
970*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstr->fOp == BuilderOp::copy_uniform_to_slots_unmasked &&
971*c8dee2aaSAndroid Build Coastguard Worker             // and this op's destination is immediately after the last copy-constant's destination
972*c8dee2aaSAndroid Build Coastguard Worker             lastInstr->fSlotB + lastInstr->fImmA == dst.index &&
973*c8dee2aaSAndroid Build Coastguard Worker             // and this op's source is immediately after the last copy-constant's source
974*c8dee2aaSAndroid Build Coastguard Worker             lastInstr->fSlotA + lastInstr->fImmA == src.index) {
975*c8dee2aaSAndroid Build Coastguard Worker             // then we can just extend the copy!
976*c8dee2aaSAndroid Build Coastguard Worker             lastInstr->fImmA += dst.count;
977*c8dee2aaSAndroid Build Coastguard Worker             return;
978*c8dee2aaSAndroid Build Coastguard Worker         }
979*c8dee2aaSAndroid Build Coastguard Worker     }
980*c8dee2aaSAndroid Build Coastguard Worker 
981*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(dst.count == src.count);
982*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::copy_uniform_to_slots_unmasked, {src.index, dst.index},
983*c8dee2aaSAndroid Build Coastguard Worker                             dst.count);
984*c8dee2aaSAndroid Build Coastguard Worker }
985*c8dee2aaSAndroid Build Coastguard Worker 
copy_stack_to_slots_unmasked(SlotRange dst,int offsetFromStackTop)986*c8dee2aaSAndroid Build Coastguard Worker void Builder::copy_stack_to_slots_unmasked(SlotRange dst, int offsetFromStackTop) {
987*c8dee2aaSAndroid Build Coastguard Worker     // If the last instruction copied the previous stack slots, just extend it.
988*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstr = this->lastInstruction()) {
989*c8dee2aaSAndroid Build Coastguard Worker         // If the last op is copy-stack-to-slots-unmasked...
990*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstr->fOp == BuilderOp::copy_stack_to_slots_unmasked &&
991*c8dee2aaSAndroid Build Coastguard Worker             // and this op's destination is immediately after the last copy-slots-op's destination
992*c8dee2aaSAndroid Build Coastguard Worker             lastInstr->fSlotA + lastInstr->fImmA == dst.index &&
993*c8dee2aaSAndroid Build Coastguard Worker             // and this op's source is immediately after the last copy-slots-op's source
994*c8dee2aaSAndroid Build Coastguard Worker             lastInstr->fImmB - lastInstr->fImmA == offsetFromStackTop) {
995*c8dee2aaSAndroid Build Coastguard Worker             // then we can just extend the copy!
996*c8dee2aaSAndroid Build Coastguard Worker             lastInstr->fImmA += dst.count;
997*c8dee2aaSAndroid Build Coastguard Worker             return;
998*c8dee2aaSAndroid Build Coastguard Worker         }
999*c8dee2aaSAndroid Build Coastguard Worker     }
1000*c8dee2aaSAndroid Build Coastguard Worker 
1001*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::copy_stack_to_slots_unmasked, {dst.index},
1002*c8dee2aaSAndroid Build Coastguard Worker                             dst.count, offsetFromStackTop);
1003*c8dee2aaSAndroid Build Coastguard Worker }
1004*c8dee2aaSAndroid Build Coastguard Worker 
pop_return_mask()1005*c8dee2aaSAndroid Build Coastguard Worker void Builder::pop_return_mask() {
1006*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(this->executionMaskWritesAreEnabled());
1007*c8dee2aaSAndroid Build Coastguard Worker 
1008*c8dee2aaSAndroid Build Coastguard Worker     // This instruction is going to overwrite the return mask. If the previous instruction was
1009*c8dee2aaSAndroid Build Coastguard Worker     // masking off the return mask, that's wasted work and it can be eliminated.
1010*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstruction = this->lastInstructionOnAnyStack()) {
1011*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == BuilderOp::mask_off_return_mask) {
1012*c8dee2aaSAndroid Build Coastguard Worker             fInstructions.pop_back();
1013*c8dee2aaSAndroid Build Coastguard Worker         }
1014*c8dee2aaSAndroid Build Coastguard Worker     }
1015*c8dee2aaSAndroid Build Coastguard Worker 
1016*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::pop_return_mask, {});
1017*c8dee2aaSAndroid Build Coastguard Worker }
1018*c8dee2aaSAndroid Build Coastguard Worker 
push_condition_mask()1019*c8dee2aaSAndroid Build Coastguard Worker void Builder::push_condition_mask() {
1020*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(this->executionMaskWritesAreEnabled());
1021*c8dee2aaSAndroid Build Coastguard Worker 
1022*c8dee2aaSAndroid Build Coastguard Worker     // If the previous instruction is popping the condition mask, we can restore it onto the stack
1023*c8dee2aaSAndroid Build Coastguard Worker     // "for free" instead of copying it.
1024*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstruction = this->lastInstruction()) {
1025*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == BuilderOp::pop_condition_mask) {
1026*c8dee2aaSAndroid Build Coastguard Worker             this->pad_stack(1);
1027*c8dee2aaSAndroid Build Coastguard Worker             return;
1028*c8dee2aaSAndroid Build Coastguard Worker         }
1029*c8dee2aaSAndroid Build Coastguard Worker     }
1030*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::push_condition_mask, {});
1031*c8dee2aaSAndroid Build Coastguard Worker }
1032*c8dee2aaSAndroid Build Coastguard Worker 
merge_condition_mask()1033*c8dee2aaSAndroid Build Coastguard Worker void Builder::merge_condition_mask() {
1034*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(this->executionMaskWritesAreEnabled());
1035*c8dee2aaSAndroid Build Coastguard Worker 
1036*c8dee2aaSAndroid Build Coastguard Worker     // This instruction is going to overwrite the condition mask. If the previous instruction was
1037*c8dee2aaSAndroid Build Coastguard Worker     // loading the condition mask, that's wasted work and it can be eliminated.
1038*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstruction = this->lastInstructionOnAnyStack()) {
1039*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == BuilderOp::pop_condition_mask) {
1040*c8dee2aaSAndroid Build Coastguard Worker             int stackID = lastInstruction->fStackID;
1041*c8dee2aaSAndroid Build Coastguard Worker             fInstructions.pop_back();
1042*c8dee2aaSAndroid Build Coastguard Worker             this->discard_stack(/*count=*/1, stackID);
1043*c8dee2aaSAndroid Build Coastguard Worker         }
1044*c8dee2aaSAndroid Build Coastguard Worker     }
1045*c8dee2aaSAndroid Build Coastguard Worker 
1046*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::merge_condition_mask, {});
1047*c8dee2aaSAndroid Build Coastguard Worker }
1048*c8dee2aaSAndroid Build Coastguard Worker 
zero_slots_unmasked(SlotRange dst)1049*c8dee2aaSAndroid Build Coastguard Worker void Builder::zero_slots_unmasked(SlotRange dst) {
1050*c8dee2aaSAndroid Build Coastguard Worker     if (Instruction* lastInstruction = this->lastInstruction()) {
1051*c8dee2aaSAndroid Build Coastguard Worker         if (lastInstruction->fOp == BuilderOp::copy_constant && lastInstruction->fImmB == 0) {
1052*c8dee2aaSAndroid Build Coastguard Worker             if (lastInstruction->fSlotA + lastInstruction->fImmA == dst.index) {
1053*c8dee2aaSAndroid Build Coastguard Worker                 // The previous instruction was zeroing the range immediately before this range.
1054*c8dee2aaSAndroid Build Coastguard Worker                 // Combine the ranges.
1055*c8dee2aaSAndroid Build Coastguard Worker                 lastInstruction->fImmA += dst.count;
1056*c8dee2aaSAndroid Build Coastguard Worker                 return;
1057*c8dee2aaSAndroid Build Coastguard Worker             }
1058*c8dee2aaSAndroid Build Coastguard Worker 
1059*c8dee2aaSAndroid Build Coastguard Worker             if (lastInstruction->fSlotA == dst.index + dst.count) {
1060*c8dee2aaSAndroid Build Coastguard Worker                 // The previous instruction was zeroing the range immediately after this range.
1061*c8dee2aaSAndroid Build Coastguard Worker                 // Combine the ranges.
1062*c8dee2aaSAndroid Build Coastguard Worker                 lastInstruction->fSlotA = dst.index;
1063*c8dee2aaSAndroid Build Coastguard Worker                 lastInstruction->fImmA += dst.count;
1064*c8dee2aaSAndroid Build Coastguard Worker                 return;
1065*c8dee2aaSAndroid Build Coastguard Worker             }
1066*c8dee2aaSAndroid Build Coastguard Worker         }
1067*c8dee2aaSAndroid Build Coastguard Worker     }
1068*c8dee2aaSAndroid Build Coastguard Worker 
1069*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::copy_constant, {dst.index}, dst.count, 0);
1070*c8dee2aaSAndroid Build Coastguard Worker }
1071*c8dee2aaSAndroid Build Coastguard Worker 
pack_nybbles(SkSpan<const int8_t> components)1072*c8dee2aaSAndroid Build Coastguard Worker static int pack_nybbles(SkSpan<const int8_t> components) {
1073*c8dee2aaSAndroid Build Coastguard Worker     // Pack up to 8 elements into nybbles, in reverse order.
1074*c8dee2aaSAndroid Build Coastguard Worker     int packed = 0;
1075*c8dee2aaSAndroid Build Coastguard Worker     for (auto iter = components.rbegin(); iter != components.rend(); ++iter) {
1076*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(*iter >= 0 && *iter <= 0xF);
1077*c8dee2aaSAndroid Build Coastguard Worker         packed <<= 4;
1078*c8dee2aaSAndroid Build Coastguard Worker         packed |= *iter;
1079*c8dee2aaSAndroid Build Coastguard Worker     }
1080*c8dee2aaSAndroid Build Coastguard Worker     return packed;
1081*c8dee2aaSAndroid Build Coastguard Worker }
1082*c8dee2aaSAndroid Build Coastguard Worker 
1083*c8dee2aaSAndroid Build Coastguard Worker template <typename T>
unpack_nybbles_to_offsets(uint32_t components,SkSpan<T> offsets)1084*c8dee2aaSAndroid Build Coastguard Worker static void unpack_nybbles_to_offsets(uint32_t components, SkSpan<T> offsets) {
1085*c8dee2aaSAndroid Build Coastguard Worker     // Unpack component nybbles into byte-offsets pointing at stack slots.
1086*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < offsets.size(); ++index) {
1087*c8dee2aaSAndroid Build Coastguard Worker         offsets[index] = (components & 0xF) * SkOpts::raster_pipeline_highp_stride * sizeof(float);
1088*c8dee2aaSAndroid Build Coastguard Worker         components >>= 4;
1089*c8dee2aaSAndroid Build Coastguard Worker     }
1090*c8dee2aaSAndroid Build Coastguard Worker }
1091*c8dee2aaSAndroid Build Coastguard Worker 
max_packed_nybble(uint32_t components,size_t numComponents)1092*c8dee2aaSAndroid Build Coastguard Worker static int max_packed_nybble(uint32_t components, size_t numComponents) {
1093*c8dee2aaSAndroid Build Coastguard Worker     int largest = 0;
1094*c8dee2aaSAndroid Build Coastguard Worker     for (size_t index = 0; index < numComponents; ++index) {
1095*c8dee2aaSAndroid Build Coastguard Worker         largest = std::max<int>(largest, components & 0xF);
1096*c8dee2aaSAndroid Build Coastguard Worker         components >>= 4;
1097*c8dee2aaSAndroid Build Coastguard Worker     }
1098*c8dee2aaSAndroid Build Coastguard Worker     return largest;
1099*c8dee2aaSAndroid Build Coastguard Worker }
1100*c8dee2aaSAndroid Build Coastguard Worker 
swizzle_copy_stack_to_slots(SlotRange dst,SkSpan<const int8_t> components,int offsetFromStackTop)1101*c8dee2aaSAndroid Build Coastguard Worker void Builder::swizzle_copy_stack_to_slots(SlotRange dst,
1102*c8dee2aaSAndroid Build Coastguard Worker                                           SkSpan<const int8_t> components,
1103*c8dee2aaSAndroid Build Coastguard Worker                                           int offsetFromStackTop) {
1104*c8dee2aaSAndroid Build Coastguard Worker     // When the execution-mask writes-enabled flag is off, we could squeeze out a little bit of
1105*c8dee2aaSAndroid Build Coastguard Worker     // extra speed here by implementing and using an unmasked version of this op.
1106*c8dee2aaSAndroid Build Coastguard Worker 
1107*c8dee2aaSAndroid Build Coastguard Worker     // SlotA: fixed-range start
1108*c8dee2aaSAndroid Build Coastguard Worker     // immA: number of swizzle components
1109*c8dee2aaSAndroid Build Coastguard Worker     // immB: swizzle components
1110*c8dee2aaSAndroid Build Coastguard Worker     // immC: offset from stack top
1111*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::swizzle_copy_stack_to_slots, {dst.index},
1112*c8dee2aaSAndroid Build Coastguard Worker                             (int)components.size(),
1113*c8dee2aaSAndroid Build Coastguard Worker                             pack_nybbles(components),
1114*c8dee2aaSAndroid Build Coastguard Worker                             offsetFromStackTop);
1115*c8dee2aaSAndroid Build Coastguard Worker }
1116*c8dee2aaSAndroid Build Coastguard Worker 
swizzle_copy_stack_to_slots_indirect(SlotRange fixedRange,int dynamicStackID,SlotRange limitRange,SkSpan<const int8_t> components,int offsetFromStackTop)1117*c8dee2aaSAndroid Build Coastguard Worker void Builder::swizzle_copy_stack_to_slots_indirect(SlotRange fixedRange,
1118*c8dee2aaSAndroid Build Coastguard Worker                                                    int dynamicStackID,
1119*c8dee2aaSAndroid Build Coastguard Worker                                                    SlotRange limitRange,
1120*c8dee2aaSAndroid Build Coastguard Worker                                                    SkSpan<const int8_t> components,
1121*c8dee2aaSAndroid Build Coastguard Worker                                                    int offsetFromStackTop) {
1122*c8dee2aaSAndroid Build Coastguard Worker     // When the execution-mask writes-enabled flag is off, we could squeeze out a little bit of
1123*c8dee2aaSAndroid Build Coastguard Worker     // extra speed here by implementing and using an unmasked version of this op.
1124*c8dee2aaSAndroid Build Coastguard Worker 
1125*c8dee2aaSAndroid Build Coastguard Worker     // SlotA: fixed-range start
1126*c8dee2aaSAndroid Build Coastguard Worker     // SlotB: limit-range end
1127*c8dee2aaSAndroid Build Coastguard Worker     // immA: number of swizzle components
1128*c8dee2aaSAndroid Build Coastguard Worker     // immB: swizzle components
1129*c8dee2aaSAndroid Build Coastguard Worker     // immC: offset from stack top
1130*c8dee2aaSAndroid Build Coastguard Worker     // immD: dynamic stack ID
1131*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::swizzle_copy_stack_to_slots_indirect,
1132*c8dee2aaSAndroid Build Coastguard Worker                             {fixedRange.index, limitRange.index + limitRange.count},
1133*c8dee2aaSAndroid Build Coastguard Worker                             (int)components.size(),
1134*c8dee2aaSAndroid Build Coastguard Worker                             pack_nybbles(components),
1135*c8dee2aaSAndroid Build Coastguard Worker                             offsetFromStackTop,
1136*c8dee2aaSAndroid Build Coastguard Worker                             dynamicStackID);
1137*c8dee2aaSAndroid Build Coastguard Worker }
1138*c8dee2aaSAndroid Build Coastguard Worker 
swizzle(int consumedSlots,SkSpan<const int8_t> components)1139*c8dee2aaSAndroid Build Coastguard Worker void Builder::swizzle(int consumedSlots, SkSpan<const int8_t> components) {
1140*c8dee2aaSAndroid Build Coastguard Worker     // Consumes `consumedSlots` elements on the stack, then generates `elementSpan.size()` elements.
1141*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(consumedSlots >= 0);
1142*c8dee2aaSAndroid Build Coastguard Worker 
1143*c8dee2aaSAndroid Build Coastguard Worker     // We only allow up to 16 elements, and they can only reach 0-15 slots, due to nybble packing.
1144*c8dee2aaSAndroid Build Coastguard Worker     int numElements = components.size();
1145*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(numElements <= 16);
1146*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(std::all_of(components.begin(), components.end(), [](int8_t e){ return e >= 0; }));
1147*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(std::all_of(components.begin(), components.end(), [](int8_t e){ return e <= 0xF; }));
1148*c8dee2aaSAndroid Build Coastguard Worker 
1149*c8dee2aaSAndroid Build Coastguard Worker     // Make a local copy of the element array.
1150*c8dee2aaSAndroid Build Coastguard Worker     int8_t elements[16] = {};
1151*c8dee2aaSAndroid Build Coastguard Worker     std::copy(components.begin(), components.end(), std::begin(elements));
1152*c8dee2aaSAndroid Build Coastguard Worker 
1153*c8dee2aaSAndroid Build Coastguard Worker     while (numElements > 0) {
1154*c8dee2aaSAndroid Build Coastguard Worker         // If the first element of the swizzle is zero...
1155*c8dee2aaSAndroid Build Coastguard Worker         if (elements[0] != 0) {
1156*c8dee2aaSAndroid Build Coastguard Worker             break;
1157*c8dee2aaSAndroid Build Coastguard Worker         }
1158*c8dee2aaSAndroid Build Coastguard Worker         // ...and zero isn't used elsewhere in the swizzle...
1159*c8dee2aaSAndroid Build Coastguard Worker         if (std::any_of(&elements[1], &elements[numElements], [](int8_t e) { return e == 0; })) {
1160*c8dee2aaSAndroid Build Coastguard Worker             break;
1161*c8dee2aaSAndroid Build Coastguard Worker         }
1162*c8dee2aaSAndroid Build Coastguard Worker         // We can omit the first slot from the swizzle entirely.
1163*c8dee2aaSAndroid Build Coastguard Worker         // Slide everything forward by one slot, and reduce the element index by one.
1164*c8dee2aaSAndroid Build Coastguard Worker         for (int index = 1; index < numElements; ++index) {
1165*c8dee2aaSAndroid Build Coastguard Worker             elements[index - 1] = elements[index] - 1;
1166*c8dee2aaSAndroid Build Coastguard Worker         }
1167*c8dee2aaSAndroid Build Coastguard Worker         elements[numElements - 1] = 0;
1168*c8dee2aaSAndroid Build Coastguard Worker         --consumedSlots;
1169*c8dee2aaSAndroid Build Coastguard Worker         --numElements;
1170*c8dee2aaSAndroid Build Coastguard Worker     }
1171*c8dee2aaSAndroid Build Coastguard Worker 
1172*c8dee2aaSAndroid Build Coastguard Worker     // A completely empty swizzle is a discard.
1173*c8dee2aaSAndroid Build Coastguard Worker     if (numElements == 0) {
1174*c8dee2aaSAndroid Build Coastguard Worker         this->discard_stack(consumedSlots);
1175*c8dee2aaSAndroid Build Coastguard Worker         return;
1176*c8dee2aaSAndroid Build Coastguard Worker     }
1177*c8dee2aaSAndroid Build Coastguard Worker 
1178*c8dee2aaSAndroid Build Coastguard Worker     if (consumedSlots <= 4 && numElements <= 4) {
1179*c8dee2aaSAndroid Build Coastguard Worker         // We can fit everything into a little swizzle.
1180*c8dee2aaSAndroid Build Coastguard Worker         int op = (int)BuilderOp::swizzle_1 + numElements - 1;
1181*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction((BuilderOp)op, {}, consumedSlots,
1182*c8dee2aaSAndroid Build Coastguard Worker                                 pack_nybbles(SkSpan(elements, numElements)));
1183*c8dee2aaSAndroid Build Coastguard Worker         return;
1184*c8dee2aaSAndroid Build Coastguard Worker     }
1185*c8dee2aaSAndroid Build Coastguard Worker 
1186*c8dee2aaSAndroid Build Coastguard Worker     // This is a big swizzle. We use the `shuffle` op to handle these. immA counts the consumed
1187*c8dee2aaSAndroid Build Coastguard Worker     // slots. immB counts the generated slots. immC and immD hold packed-nybble shuffle values.
1188*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::shuffle, {},
1189*c8dee2aaSAndroid Build Coastguard Worker                             consumedSlots, numElements,
1190*c8dee2aaSAndroid Build Coastguard Worker                             pack_nybbles(SkSpan(&elements[0], 8)),
1191*c8dee2aaSAndroid Build Coastguard Worker                             pack_nybbles(SkSpan(&elements[8], 8)));
1192*c8dee2aaSAndroid Build Coastguard Worker }
1193*c8dee2aaSAndroid Build Coastguard Worker 
transpose(int columns,int rows)1194*c8dee2aaSAndroid Build Coastguard Worker void Builder::transpose(int columns, int rows) {
1195*c8dee2aaSAndroid Build Coastguard Worker     // Transposes a matrix of size CxR on the stack (into a matrix of size RxC).
1196*c8dee2aaSAndroid Build Coastguard Worker     int8_t elements[16] = {};
1197*c8dee2aaSAndroid Build Coastguard Worker     size_t index = 0;
1198*c8dee2aaSAndroid Build Coastguard Worker     for (int r = 0; r < rows; ++r) {
1199*c8dee2aaSAndroid Build Coastguard Worker         for (int c = 0; c < columns; ++c) {
1200*c8dee2aaSAndroid Build Coastguard Worker             elements[index++] = (c * rows) + r;
1201*c8dee2aaSAndroid Build Coastguard Worker         }
1202*c8dee2aaSAndroid Build Coastguard Worker     }
1203*c8dee2aaSAndroid Build Coastguard Worker     this->swizzle(/*consumedSlots=*/columns * rows, SkSpan(elements, index));
1204*c8dee2aaSAndroid Build Coastguard Worker }
1205*c8dee2aaSAndroid Build Coastguard Worker 
diagonal_matrix(int columns,int rows)1206*c8dee2aaSAndroid Build Coastguard Worker void Builder::diagonal_matrix(int columns, int rows) {
1207*c8dee2aaSAndroid Build Coastguard Worker     // Generates a CxR diagonal matrix from the top two scalars on the stack.
1208*c8dee2aaSAndroid Build Coastguard Worker     int8_t elements[16] = {};
1209*c8dee2aaSAndroid Build Coastguard Worker     size_t index = 0;
1210*c8dee2aaSAndroid Build Coastguard Worker     for (int c = 0; c < columns; ++c) {
1211*c8dee2aaSAndroid Build Coastguard Worker         for (int r = 0; r < rows; ++r) {
1212*c8dee2aaSAndroid Build Coastguard Worker             elements[index++] = (c == r) ? 1 : 0;
1213*c8dee2aaSAndroid Build Coastguard Worker         }
1214*c8dee2aaSAndroid Build Coastguard Worker     }
1215*c8dee2aaSAndroid Build Coastguard Worker     this->swizzle(/*consumedSlots=*/2, SkSpan(elements, index));
1216*c8dee2aaSAndroid Build Coastguard Worker }
1217*c8dee2aaSAndroid Build Coastguard Worker 
matrix_resize(int origColumns,int origRows,int newColumns,int newRows)1218*c8dee2aaSAndroid Build Coastguard Worker void Builder::matrix_resize(int origColumns, int origRows, int newColumns, int newRows) {
1219*c8dee2aaSAndroid Build Coastguard Worker     // Resizes a CxR matrix at the top of the stack to C'xR'.
1220*c8dee2aaSAndroid Build Coastguard Worker     int8_t elements[16] = {};
1221*c8dee2aaSAndroid Build Coastguard Worker     size_t index = 0;
1222*c8dee2aaSAndroid Build Coastguard Worker 
1223*c8dee2aaSAndroid Build Coastguard Worker     size_t consumedSlots = origColumns * origRows;
1224*c8dee2aaSAndroid Build Coastguard Worker     size_t zeroOffset = 0, oneOffset = 0;
1225*c8dee2aaSAndroid Build Coastguard Worker 
1226*c8dee2aaSAndroid Build Coastguard Worker     for (int c = 0; c < newColumns; ++c) {
1227*c8dee2aaSAndroid Build Coastguard Worker         for (int r = 0; r < newRows; ++r) {
1228*c8dee2aaSAndroid Build Coastguard Worker             if (c < origColumns && r < origRows) {
1229*c8dee2aaSAndroid Build Coastguard Worker                 // Push an element from the original matrix.
1230*c8dee2aaSAndroid Build Coastguard Worker                 elements[index++] = (c * origRows) + r;
1231*c8dee2aaSAndroid Build Coastguard Worker             } else {
1232*c8dee2aaSAndroid Build Coastguard Worker                 // This element is outside the original matrix; push 1 or 0.
1233*c8dee2aaSAndroid Build Coastguard Worker                 if (c == r) {
1234*c8dee2aaSAndroid Build Coastguard Worker                     // We need to synthesize a literal 1.
1235*c8dee2aaSAndroid Build Coastguard Worker                     if (oneOffset == 0) {
1236*c8dee2aaSAndroid Build Coastguard Worker                         this->push_constant_f(1.0f);
1237*c8dee2aaSAndroid Build Coastguard Worker                         oneOffset = consumedSlots++;
1238*c8dee2aaSAndroid Build Coastguard Worker                     }
1239*c8dee2aaSAndroid Build Coastguard Worker                     elements[index++] = oneOffset;
1240*c8dee2aaSAndroid Build Coastguard Worker                 } else {
1241*c8dee2aaSAndroid Build Coastguard Worker                     // We need to synthesize a literal 0.
1242*c8dee2aaSAndroid Build Coastguard Worker                     if (zeroOffset == 0) {
1243*c8dee2aaSAndroid Build Coastguard Worker                         this->push_constant_f(0.0f);
1244*c8dee2aaSAndroid Build Coastguard Worker                         zeroOffset = consumedSlots++;
1245*c8dee2aaSAndroid Build Coastguard Worker                     }
1246*c8dee2aaSAndroid Build Coastguard Worker                     elements[index++] = zeroOffset;
1247*c8dee2aaSAndroid Build Coastguard Worker                 }
1248*c8dee2aaSAndroid Build Coastguard Worker             }
1249*c8dee2aaSAndroid Build Coastguard Worker         }
1250*c8dee2aaSAndroid Build Coastguard Worker     }
1251*c8dee2aaSAndroid Build Coastguard Worker     this->swizzle(consumedSlots, SkSpan(elements, index));
1252*c8dee2aaSAndroid Build Coastguard Worker }
1253*c8dee2aaSAndroid Build Coastguard Worker 
matrix_multiply(int leftColumns,int leftRows,int rightColumns,int rightRows)1254*c8dee2aaSAndroid Build Coastguard Worker void Builder::matrix_multiply(int leftColumns, int leftRows, int rightColumns, int rightRows) {
1255*c8dee2aaSAndroid Build Coastguard Worker     BuilderOp op;
1256*c8dee2aaSAndroid Build Coastguard Worker     switch (leftColumns) {
1257*c8dee2aaSAndroid Build Coastguard Worker         case 2:  op = BuilderOp::matrix_multiply_2; break;
1258*c8dee2aaSAndroid Build Coastguard Worker         case 3:  op = BuilderOp::matrix_multiply_3; break;
1259*c8dee2aaSAndroid Build Coastguard Worker         case 4:  op = BuilderOp::matrix_multiply_4; break;
1260*c8dee2aaSAndroid Build Coastguard Worker         default: SkDEBUGFAIL("unsupported matrix dimensions"); return;
1261*c8dee2aaSAndroid Build Coastguard Worker     }
1262*c8dee2aaSAndroid Build Coastguard Worker 
1263*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(op, {}, leftColumns, leftRows, rightColumns, rightRows);
1264*c8dee2aaSAndroid Build Coastguard Worker }
1265*c8dee2aaSAndroid Build Coastguard Worker 
finish(int numValueSlots,int numUniformSlots,int numImmutableSlots,DebugTracePriv * debugTrace)1266*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Program> Builder::finish(int numValueSlots,
1267*c8dee2aaSAndroid Build Coastguard Worker                                          int numUniformSlots,
1268*c8dee2aaSAndroid Build Coastguard Worker                                          int numImmutableSlots,
1269*c8dee2aaSAndroid Build Coastguard Worker                                          DebugTracePriv* debugTrace) {
1270*c8dee2aaSAndroid Build Coastguard Worker     // Verify that calls to enableExecutionMaskWrites and disableExecutionMaskWrites are balanced.
1271*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fExecutionMaskWritesEnabled == 0);
1272*c8dee2aaSAndroid Build Coastguard Worker 
1273*c8dee2aaSAndroid Build Coastguard Worker     return std::make_unique<Program>(std::move(fInstructions), numValueSlots, numUniformSlots,
1274*c8dee2aaSAndroid Build Coastguard Worker                                      numImmutableSlots, fNumLabels, debugTrace);
1275*c8dee2aaSAndroid Build Coastguard Worker }
1276*c8dee2aaSAndroid Build Coastguard Worker 
stack_usage(const Instruction & inst)1277*c8dee2aaSAndroid Build Coastguard Worker static int stack_usage(const Instruction& inst) {
1278*c8dee2aaSAndroid Build Coastguard Worker     switch (inst.fOp) {
1279*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::push_condition_mask:
1280*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::push_loop_mask:
1281*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::push_return_mask:
1282*c8dee2aaSAndroid Build Coastguard Worker             return 1;
1283*c8dee2aaSAndroid Build Coastguard Worker 
1284*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::push_src_rgba:
1285*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::push_dst_rgba:
1286*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::push_device_xy01:
1287*c8dee2aaSAndroid Build Coastguard Worker             return 4;
1288*c8dee2aaSAndroid Build Coastguard Worker 
1289*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::push_immutable:
1290*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::push_immutable_indirect:
1291*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::push_constant:
1292*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::push_slots:
1293*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::push_slots_indirect:
1294*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::push_uniform:
1295*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::push_uniform_indirect:
1296*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::push_clone:
1297*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::push_clone_from_stack:
1298*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::push_clone_indirect_from_stack:
1299*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::pad_stack:
1300*c8dee2aaSAndroid Build Coastguard Worker             return inst.fImmA;
1301*c8dee2aaSAndroid Build Coastguard Worker 
1302*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::pop_condition_mask:
1303*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::pop_loop_mask:
1304*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::pop_and_reenable_loop_mask:
1305*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::pop_return_mask:
1306*c8dee2aaSAndroid Build Coastguard Worker             return -1;
1307*c8dee2aaSAndroid Build Coastguard Worker 
1308*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::pop_src_rgba:
1309*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::pop_dst_rgba:
1310*c8dee2aaSAndroid Build Coastguard Worker             return -4;
1311*c8dee2aaSAndroid Build Coastguard Worker 
1312*c8dee2aaSAndroid Build Coastguard Worker         case ALL_N_WAY_BINARY_OP_CASES:
1313*c8dee2aaSAndroid Build Coastguard Worker         case ALL_MULTI_SLOT_BINARY_OP_CASES:
1314*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::discard_stack:
1315*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::select:
1316*c8dee2aaSAndroid Build Coastguard Worker             return -inst.fImmA;
1317*c8dee2aaSAndroid Build Coastguard Worker 
1318*c8dee2aaSAndroid Build Coastguard Worker         case ALL_N_WAY_TERNARY_OP_CASES:
1319*c8dee2aaSAndroid Build Coastguard Worker         case ALL_MULTI_SLOT_TERNARY_OP_CASES:
1320*c8dee2aaSAndroid Build Coastguard Worker             return 2 * -inst.fImmA;
1321*c8dee2aaSAndroid Build Coastguard Worker 
1322*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::swizzle_1:
1323*c8dee2aaSAndroid Build Coastguard Worker             return 1 - inst.fImmA;  // consumes immA slots and emits a scalar
1324*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::swizzle_2:
1325*c8dee2aaSAndroid Build Coastguard Worker             return 2 - inst.fImmA;  // consumes immA slots and emits a 2-slot vector
1326*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::swizzle_3:
1327*c8dee2aaSAndroid Build Coastguard Worker             return 3 - inst.fImmA;  // consumes immA slots and emits a 3-slot vector
1328*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::swizzle_4:
1329*c8dee2aaSAndroid Build Coastguard Worker             return 4 - inst.fImmA;  // consumes immA slots and emits a 4-slot vector
1330*c8dee2aaSAndroid Build Coastguard Worker 
1331*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::dot_2_floats:
1332*c8dee2aaSAndroid Build Coastguard Worker             return -3;  // consumes two 2-slot vectors and emits one scalar
1333*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::dot_3_floats:
1334*c8dee2aaSAndroid Build Coastguard Worker             return -5;  // consumes two 3-slot vectors and emits one scalar
1335*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::dot_4_floats:
1336*c8dee2aaSAndroid Build Coastguard Worker             return -7;  // consumes two 4-slot vectors and emits one scalar
1337*c8dee2aaSAndroid Build Coastguard Worker 
1338*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::refract_4_floats:
1339*c8dee2aaSAndroid Build Coastguard Worker             return -5;  // consumes nine slots (N + I + eta) and emits a 4-slot vector (R)
1340*c8dee2aaSAndroid Build Coastguard Worker 
1341*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::matrix_multiply_2:
1342*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::matrix_multiply_3:
1343*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::matrix_multiply_4:
1344*c8dee2aaSAndroid Build Coastguard Worker             // consumes the left- and right-matrices; emits result over existing padding slots
1345*c8dee2aaSAndroid Build Coastguard Worker             return -(inst.fImmA * inst.fImmB + inst.fImmC * inst.fImmD);
1346*c8dee2aaSAndroid Build Coastguard Worker 
1347*c8dee2aaSAndroid Build Coastguard Worker         case BuilderOp::shuffle: {
1348*c8dee2aaSAndroid Build Coastguard Worker             int consumed = inst.fImmA;
1349*c8dee2aaSAndroid Build Coastguard Worker             int generated = inst.fImmB;
1350*c8dee2aaSAndroid Build Coastguard Worker             return generated - consumed;
1351*c8dee2aaSAndroid Build Coastguard Worker         }
1352*c8dee2aaSAndroid Build Coastguard Worker         case ALL_SINGLE_SLOT_UNARY_OP_CASES:
1353*c8dee2aaSAndroid Build Coastguard Worker         case ALL_MULTI_SLOT_UNARY_OP_CASES:
1354*c8dee2aaSAndroid Build Coastguard Worker         case ALL_IMMEDIATE_BINARY_OP_CASES:
1355*c8dee2aaSAndroid Build Coastguard Worker         default:
1356*c8dee2aaSAndroid Build Coastguard Worker             return 0;
1357*c8dee2aaSAndroid Build Coastguard Worker     }
1358*c8dee2aaSAndroid Build Coastguard Worker }
1359*c8dee2aaSAndroid Build Coastguard Worker 
tempStackMaxDepths() const1360*c8dee2aaSAndroid Build Coastguard Worker Program::StackDepths Program::tempStackMaxDepths() const {
1361*c8dee2aaSAndroid Build Coastguard Worker     // Count the number of separate temp stacks that the program uses.
1362*c8dee2aaSAndroid Build Coastguard Worker     int numStacks = 1;
1363*c8dee2aaSAndroid Build Coastguard Worker     for (const Instruction& inst : fInstructions) {
1364*c8dee2aaSAndroid Build Coastguard Worker         numStacks = std::max(numStacks, inst.fStackID + 1);
1365*c8dee2aaSAndroid Build Coastguard Worker     }
1366*c8dee2aaSAndroid Build Coastguard Worker 
1367*c8dee2aaSAndroid Build Coastguard Worker     // Walk the program and calculate how deep each stack can potentially get.
1368*c8dee2aaSAndroid Build Coastguard Worker     StackDepths largest, current;
1369*c8dee2aaSAndroid Build Coastguard Worker     largest.push_back_n(numStacks, 0);
1370*c8dee2aaSAndroid Build Coastguard Worker     current.push_back_n(numStacks, 0);
1371*c8dee2aaSAndroid Build Coastguard Worker 
1372*c8dee2aaSAndroid Build Coastguard Worker     for (const Instruction& inst : fInstructions) {
1373*c8dee2aaSAndroid Build Coastguard Worker         int stackID = inst.fStackID;
1374*c8dee2aaSAndroid Build Coastguard Worker         current[stackID] += stack_usage(inst);
1375*c8dee2aaSAndroid Build Coastguard Worker         largest[stackID] = std::max(current[stackID], largest[stackID]);
1376*c8dee2aaSAndroid Build Coastguard Worker         // If we assert here, the generated program has popped off the top of the stack.
1377*c8dee2aaSAndroid Build Coastguard Worker         SkASSERTF(current[stackID] >= 0, "unbalanced temp stack push/pop on stack %d", stackID);
1378*c8dee2aaSAndroid Build Coastguard Worker     }
1379*c8dee2aaSAndroid Build Coastguard Worker 
1380*c8dee2aaSAndroid Build Coastguard Worker     // Ensure that when the program is complete, our stacks are fully balanced.
1381*c8dee2aaSAndroid Build Coastguard Worker     for (int stackID = 0; stackID < numStacks; ++stackID) {
1382*c8dee2aaSAndroid Build Coastguard Worker         // If we assert here, the generated program has pushed more data than it has popped.
1383*c8dee2aaSAndroid Build Coastguard Worker         SkASSERTF(current[stackID] == 0, "unbalanced temp stack push/pop on stack %d", stackID);
1384*c8dee2aaSAndroid Build Coastguard Worker     }
1385*c8dee2aaSAndroid Build Coastguard Worker 
1386*c8dee2aaSAndroid Build Coastguard Worker     return largest;
1387*c8dee2aaSAndroid Build Coastguard Worker }
1388*c8dee2aaSAndroid Build Coastguard Worker 
Program(TArray<Instruction> instrs,int numValueSlots,int numUniformSlots,int numImmutableSlots,int numLabels,DebugTracePriv * debugTrace)1389*c8dee2aaSAndroid Build Coastguard Worker Program::Program(TArray<Instruction> instrs,
1390*c8dee2aaSAndroid Build Coastguard Worker                  int numValueSlots,
1391*c8dee2aaSAndroid Build Coastguard Worker                  int numUniformSlots,
1392*c8dee2aaSAndroid Build Coastguard Worker                  int numImmutableSlots,
1393*c8dee2aaSAndroid Build Coastguard Worker                  int numLabels,
1394*c8dee2aaSAndroid Build Coastguard Worker                  DebugTracePriv* debugTrace)
1395*c8dee2aaSAndroid Build Coastguard Worker         : fInstructions(std::move(instrs))
1396*c8dee2aaSAndroid Build Coastguard Worker         , fNumValueSlots(numValueSlots)
1397*c8dee2aaSAndroid Build Coastguard Worker         , fNumUniformSlots(numUniformSlots)
1398*c8dee2aaSAndroid Build Coastguard Worker         , fNumImmutableSlots(numImmutableSlots)
1399*c8dee2aaSAndroid Build Coastguard Worker         , fNumLabels(numLabels)
1400*c8dee2aaSAndroid Build Coastguard Worker         , fDebugTrace(debugTrace) {
1401*c8dee2aaSAndroid Build Coastguard Worker     fTempStackMaxDepths = this->tempStackMaxDepths();
1402*c8dee2aaSAndroid Build Coastguard Worker 
1403*c8dee2aaSAndroid Build Coastguard Worker     fNumTempStackSlots = 0;
1404*c8dee2aaSAndroid Build Coastguard Worker     for (const int depth : fTempStackMaxDepths) {
1405*c8dee2aaSAndroid Build Coastguard Worker         fNumTempStackSlots += depth;
1406*c8dee2aaSAndroid Build Coastguard Worker     }
1407*c8dee2aaSAndroid Build Coastguard Worker 
1408*c8dee2aaSAndroid Build Coastguard Worker     if (fDebugTrace) {
1409*c8dee2aaSAndroid Build Coastguard Worker         fTraceHook = SkSL::Tracer::Make(&fDebugTrace->fTraceInfo);
1410*c8dee2aaSAndroid Build Coastguard Worker     }
1411*c8dee2aaSAndroid Build Coastguard Worker }
1412*c8dee2aaSAndroid Build Coastguard Worker 
1413*c8dee2aaSAndroid Build Coastguard Worker Program::~Program() = default;
1414*c8dee2aaSAndroid Build Coastguard Worker 
immutable_data_is_splattable(int32_t * immutablePtr,int numSlots)1415*c8dee2aaSAndroid Build Coastguard Worker static bool immutable_data_is_splattable(int32_t* immutablePtr, int numSlots) {
1416*c8dee2aaSAndroid Build Coastguard Worker     // If every value between `immutablePtr[0]` and `immutablePtr[numSlots]` is bit-identical, we
1417*c8dee2aaSAndroid Build Coastguard Worker     // can use a splat.
1418*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 1; index < numSlots; ++index) {
1419*c8dee2aaSAndroid Build Coastguard Worker         if (immutablePtr[0] != immutablePtr[index]) {
1420*c8dee2aaSAndroid Build Coastguard Worker             return false;
1421*c8dee2aaSAndroid Build Coastguard Worker         }
1422*c8dee2aaSAndroid Build Coastguard Worker     }
1423*c8dee2aaSAndroid Build Coastguard Worker     return true;
1424*c8dee2aaSAndroid Build Coastguard Worker }
1425*c8dee2aaSAndroid Build Coastguard Worker 
appendCopy(TArray<Stage> * pipeline,SkArenaAlloc * alloc,std::byte * basePtr,ProgramOp baseStage,SkRPOffset dst,int dstStride,SkRPOffset src,int srcStride,int numSlots) const1426*c8dee2aaSAndroid Build Coastguard Worker void Program::appendCopy(TArray<Stage>* pipeline,
1427*c8dee2aaSAndroid Build Coastguard Worker                          SkArenaAlloc* alloc,
1428*c8dee2aaSAndroid Build Coastguard Worker                          std::byte* basePtr,  // only used for immutable-value copies
1429*c8dee2aaSAndroid Build Coastguard Worker                          ProgramOp baseStage,
1430*c8dee2aaSAndroid Build Coastguard Worker                          SkRPOffset dst, int dstStride,
1431*c8dee2aaSAndroid Build Coastguard Worker                          SkRPOffset src, int srcStride,
1432*c8dee2aaSAndroid Build Coastguard Worker                          int numSlots) const {
1433*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(numSlots >= 0);
1434*c8dee2aaSAndroid Build Coastguard Worker     while (numSlots > 4) {
1435*c8dee2aaSAndroid Build Coastguard Worker         // If we are appending a large copy, split it up into groups of four at a time.
1436*c8dee2aaSAndroid Build Coastguard Worker         this->appendCopy(pipeline, alloc, basePtr,
1437*c8dee2aaSAndroid Build Coastguard Worker                          baseStage,
1438*c8dee2aaSAndroid Build Coastguard Worker                          dst, dstStride,
1439*c8dee2aaSAndroid Build Coastguard Worker                          src, srcStride,
1440*c8dee2aaSAndroid Build Coastguard Worker                          /*numSlots=*/4);
1441*c8dee2aaSAndroid Build Coastguard Worker         dst += 4 * dstStride * sizeof(float);
1442*c8dee2aaSAndroid Build Coastguard Worker         src += 4 * srcStride * sizeof(float);
1443*c8dee2aaSAndroid Build Coastguard Worker         numSlots -= 4;
1444*c8dee2aaSAndroid Build Coastguard Worker     }
1445*c8dee2aaSAndroid Build Coastguard Worker 
1446*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(numSlots <= 4);
1447*c8dee2aaSAndroid Build Coastguard Worker 
1448*c8dee2aaSAndroid Build Coastguard Worker     if (numSlots > 0) {
1449*c8dee2aaSAndroid Build Coastguard Worker         // If we are copying immutable data, it might be representable by a splat; this is
1450*c8dee2aaSAndroid Build Coastguard Worker         // preferable, since splats are a tiny bit faster than regular copies.
1451*c8dee2aaSAndroid Build Coastguard Worker         if (basePtr) {
1452*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(srcStride == 1);
1453*c8dee2aaSAndroid Build Coastguard Worker             int32_t* immutablePtr = reinterpret_cast<int32_t*>(basePtr + src);
1454*c8dee2aaSAndroid Build Coastguard Worker             if (immutable_data_is_splattable(immutablePtr, numSlots)) {
1455*c8dee2aaSAndroid Build Coastguard Worker                 auto stage = (ProgramOp)((int)ProgramOp::copy_constant + numSlots - 1);
1456*c8dee2aaSAndroid Build Coastguard Worker                 SkRasterPipeline_ConstantCtx ctx;
1457*c8dee2aaSAndroid Build Coastguard Worker                 ctx.dst = dst;
1458*c8dee2aaSAndroid Build Coastguard Worker                 ctx.value = *immutablePtr;
1459*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({stage, SkRPCtxUtils::Pack(ctx, alloc)});
1460*c8dee2aaSAndroid Build Coastguard Worker                 return;
1461*c8dee2aaSAndroid Build Coastguard Worker             }
1462*c8dee2aaSAndroid Build Coastguard Worker         }
1463*c8dee2aaSAndroid Build Coastguard Worker 
1464*c8dee2aaSAndroid Build Coastguard Worker         // We can't use a splat, so emit the requested copy op.
1465*c8dee2aaSAndroid Build Coastguard Worker         auto stage = (ProgramOp)((int)baseStage + numSlots - 1);
1466*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_BinaryOpCtx ctx;
1467*c8dee2aaSAndroid Build Coastguard Worker         ctx.dst = dst;
1468*c8dee2aaSAndroid Build Coastguard Worker         ctx.src = src;
1469*c8dee2aaSAndroid Build Coastguard Worker         pipeline->push_back({stage, SkRPCtxUtils::Pack(ctx, alloc)});
1470*c8dee2aaSAndroid Build Coastguard Worker     }
1471*c8dee2aaSAndroid Build Coastguard Worker }
1472*c8dee2aaSAndroid Build Coastguard Worker 
appendCopySlotsUnmasked(TArray<Stage> * pipeline,SkArenaAlloc * alloc,SkRPOffset dst,SkRPOffset src,int numSlots) const1473*c8dee2aaSAndroid Build Coastguard Worker void Program::appendCopySlotsUnmasked(TArray<Stage>* pipeline,
1474*c8dee2aaSAndroid Build Coastguard Worker                                       SkArenaAlloc* alloc,
1475*c8dee2aaSAndroid Build Coastguard Worker                                       SkRPOffset dst,
1476*c8dee2aaSAndroid Build Coastguard Worker                                       SkRPOffset src,
1477*c8dee2aaSAndroid Build Coastguard Worker                                       int numSlots) const {
1478*c8dee2aaSAndroid Build Coastguard Worker     this->appendCopy(pipeline, alloc, /*basePtr=*/nullptr,
1479*c8dee2aaSAndroid Build Coastguard Worker                      ProgramOp::copy_slot_unmasked,
1480*c8dee2aaSAndroid Build Coastguard Worker                      dst, SkOpts::raster_pipeline_highp_stride,
1481*c8dee2aaSAndroid Build Coastguard Worker                      src, SkOpts::raster_pipeline_highp_stride,
1482*c8dee2aaSAndroid Build Coastguard Worker                      numSlots);
1483*c8dee2aaSAndroid Build Coastguard Worker }
1484*c8dee2aaSAndroid Build Coastguard Worker 
appendCopyImmutableUnmasked(TArray<Stage> * pipeline,SkArenaAlloc * alloc,std::byte * basePtr,SkRPOffset dst,SkRPOffset src,int numSlots) const1485*c8dee2aaSAndroid Build Coastguard Worker void Program::appendCopyImmutableUnmasked(TArray<Stage>* pipeline,
1486*c8dee2aaSAndroid Build Coastguard Worker                                           SkArenaAlloc* alloc,
1487*c8dee2aaSAndroid Build Coastguard Worker                                           std::byte* basePtr,
1488*c8dee2aaSAndroid Build Coastguard Worker                                           SkRPOffset dst,
1489*c8dee2aaSAndroid Build Coastguard Worker                                           SkRPOffset src,
1490*c8dee2aaSAndroid Build Coastguard Worker                                           int numSlots) const {
1491*c8dee2aaSAndroid Build Coastguard Worker     this->appendCopy(pipeline, alloc, basePtr,
1492*c8dee2aaSAndroid Build Coastguard Worker                      ProgramOp::copy_immutable_unmasked,
1493*c8dee2aaSAndroid Build Coastguard Worker                      dst, SkOpts::raster_pipeline_highp_stride,
1494*c8dee2aaSAndroid Build Coastguard Worker                      src, 1,
1495*c8dee2aaSAndroid Build Coastguard Worker                      numSlots);
1496*c8dee2aaSAndroid Build Coastguard Worker }
1497*c8dee2aaSAndroid Build Coastguard Worker 
appendCopySlotsMasked(TArray<Stage> * pipeline,SkArenaAlloc * alloc,SkRPOffset dst,SkRPOffset src,int numSlots) const1498*c8dee2aaSAndroid Build Coastguard Worker void Program::appendCopySlotsMasked(TArray<Stage>* pipeline,
1499*c8dee2aaSAndroid Build Coastguard Worker                                     SkArenaAlloc* alloc,
1500*c8dee2aaSAndroid Build Coastguard Worker                                     SkRPOffset dst,
1501*c8dee2aaSAndroid Build Coastguard Worker                                     SkRPOffset src,
1502*c8dee2aaSAndroid Build Coastguard Worker                                     int numSlots) const {
1503*c8dee2aaSAndroid Build Coastguard Worker     this->appendCopy(pipeline, alloc, /*basePtr=*/nullptr,
1504*c8dee2aaSAndroid Build Coastguard Worker                      ProgramOp::copy_slot_masked,
1505*c8dee2aaSAndroid Build Coastguard Worker                      dst, SkOpts::raster_pipeline_highp_stride,
1506*c8dee2aaSAndroid Build Coastguard Worker                      src, SkOpts::raster_pipeline_highp_stride,
1507*c8dee2aaSAndroid Build Coastguard Worker                      numSlots);
1508*c8dee2aaSAndroid Build Coastguard Worker }
1509*c8dee2aaSAndroid Build Coastguard Worker 
appendSingleSlotUnaryOp(TArray<Stage> * pipeline,ProgramOp stage,float * dst,int numSlots) const1510*c8dee2aaSAndroid Build Coastguard Worker void Program::appendSingleSlotUnaryOp(TArray<Stage>* pipeline, ProgramOp stage,
1511*c8dee2aaSAndroid Build Coastguard Worker                                       float* dst, int numSlots) const {
1512*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(numSlots >= 0);
1513*c8dee2aaSAndroid Build Coastguard Worker     while (numSlots--) {
1514*c8dee2aaSAndroid Build Coastguard Worker         pipeline->push_back({stage, dst});
1515*c8dee2aaSAndroid Build Coastguard Worker         dst += SkOpts::raster_pipeline_highp_stride;
1516*c8dee2aaSAndroid Build Coastguard Worker     }
1517*c8dee2aaSAndroid Build Coastguard Worker }
1518*c8dee2aaSAndroid Build Coastguard Worker 
appendMultiSlotUnaryOp(TArray<Stage> * pipeline,ProgramOp baseStage,float * dst,int numSlots) const1519*c8dee2aaSAndroid Build Coastguard Worker void Program::appendMultiSlotUnaryOp(TArray<Stage>* pipeline, ProgramOp baseStage,
1520*c8dee2aaSAndroid Build Coastguard Worker                                      float* dst, int numSlots) const {
1521*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(numSlots >= 0);
1522*c8dee2aaSAndroid Build Coastguard Worker     while (numSlots > 0) {
1523*c8dee2aaSAndroid Build Coastguard Worker         int currentSlots = std::min(numSlots, 4);
1524*c8dee2aaSAndroid Build Coastguard Worker         auto stage = (ProgramOp)((int)baseStage + currentSlots - 1);
1525*c8dee2aaSAndroid Build Coastguard Worker         pipeline->push_back({stage, dst});
1526*c8dee2aaSAndroid Build Coastguard Worker 
1527*c8dee2aaSAndroid Build Coastguard Worker         dst += 4 * SkOpts::raster_pipeline_highp_stride;
1528*c8dee2aaSAndroid Build Coastguard Worker         numSlots -= 4;
1529*c8dee2aaSAndroid Build Coastguard Worker     }
1530*c8dee2aaSAndroid Build Coastguard Worker }
1531*c8dee2aaSAndroid Build Coastguard Worker 
appendImmediateBinaryOp(TArray<Stage> * pipeline,SkArenaAlloc * alloc,ProgramOp baseStage,SkRPOffset dst,int32_t value,int numSlots) const1532*c8dee2aaSAndroid Build Coastguard Worker void Program::appendImmediateBinaryOp(TArray<Stage>* pipeline, SkArenaAlloc* alloc,
1533*c8dee2aaSAndroid Build Coastguard Worker                                       ProgramOp baseStage,
1534*c8dee2aaSAndroid Build Coastguard Worker                                       SkRPOffset dst, int32_t value, int numSlots) const {
1535*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(is_immediate_op((BuilderOp)baseStage));
1536*c8dee2aaSAndroid Build Coastguard Worker     int slotsPerStage = is_multi_slot_immediate_op((BuilderOp)baseStage) ? 4 : 1;
1537*c8dee2aaSAndroid Build Coastguard Worker 
1538*c8dee2aaSAndroid Build Coastguard Worker     SkRasterPipeline_ConstantCtx ctx;
1539*c8dee2aaSAndroid Build Coastguard Worker     ctx.dst = dst;
1540*c8dee2aaSAndroid Build Coastguard Worker     ctx.value = value;
1541*c8dee2aaSAndroid Build Coastguard Worker 
1542*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(numSlots >= 0);
1543*c8dee2aaSAndroid Build Coastguard Worker     while (numSlots > 0) {
1544*c8dee2aaSAndroid Build Coastguard Worker         int currentSlots = std::min(numSlots, slotsPerStage);
1545*c8dee2aaSAndroid Build Coastguard Worker         auto stage = (ProgramOp)((int)baseStage - (currentSlots - 1));
1546*c8dee2aaSAndroid Build Coastguard Worker         pipeline->push_back({stage, SkRPCtxUtils::Pack(ctx, alloc)});
1547*c8dee2aaSAndroid Build Coastguard Worker 
1548*c8dee2aaSAndroid Build Coastguard Worker         ctx.dst += slotsPerStage * SkOpts::raster_pipeline_highp_stride * sizeof(float);
1549*c8dee2aaSAndroid Build Coastguard Worker         numSlots -= slotsPerStage;
1550*c8dee2aaSAndroid Build Coastguard Worker     }
1551*c8dee2aaSAndroid Build Coastguard Worker }
1552*c8dee2aaSAndroid Build Coastguard Worker 
appendAdjacentNWayBinaryOp(TArray<Stage> * pipeline,SkArenaAlloc * alloc,ProgramOp stage,SkRPOffset dst,SkRPOffset src,int numSlots) const1553*c8dee2aaSAndroid Build Coastguard Worker void Program::appendAdjacentNWayBinaryOp(TArray<Stage>* pipeline, SkArenaAlloc* alloc,
1554*c8dee2aaSAndroid Build Coastguard Worker                                          ProgramOp stage,
1555*c8dee2aaSAndroid Build Coastguard Worker                                          SkRPOffset dst, SkRPOffset src, int numSlots) const {
1556*c8dee2aaSAndroid Build Coastguard Worker     // The source and destination must be directly next to one another.
1557*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(numSlots >= 0);
1558*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT((dst + SkOpts::raster_pipeline_highp_stride * numSlots * sizeof(float)) == src);
1559*c8dee2aaSAndroid Build Coastguard Worker 
1560*c8dee2aaSAndroid Build Coastguard Worker     if (numSlots > 0) {
1561*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_BinaryOpCtx ctx;
1562*c8dee2aaSAndroid Build Coastguard Worker         ctx.dst = dst;
1563*c8dee2aaSAndroid Build Coastguard Worker         ctx.src = src;
1564*c8dee2aaSAndroid Build Coastguard Worker         pipeline->push_back({stage, SkRPCtxUtils::Pack(ctx, alloc)});
1565*c8dee2aaSAndroid Build Coastguard Worker     }
1566*c8dee2aaSAndroid Build Coastguard Worker }
1567*c8dee2aaSAndroid Build Coastguard Worker 
appendAdjacentMultiSlotBinaryOp(TArray<Stage> * pipeline,SkArenaAlloc * alloc,ProgramOp baseStage,std::byte * basePtr,SkRPOffset dst,SkRPOffset src,int numSlots) const1568*c8dee2aaSAndroid Build Coastguard Worker void Program::appendAdjacentMultiSlotBinaryOp(TArray<Stage>* pipeline, SkArenaAlloc* alloc,
1569*c8dee2aaSAndroid Build Coastguard Worker                                               ProgramOp baseStage, std::byte* basePtr,
1570*c8dee2aaSAndroid Build Coastguard Worker                                               SkRPOffset dst, SkRPOffset src, int numSlots) const {
1571*c8dee2aaSAndroid Build Coastguard Worker     // The source and destination must be directly next to one another.
1572*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(numSlots >= 0);
1573*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT((dst + SkOpts::raster_pipeline_highp_stride * numSlots * sizeof(float)) == src);
1574*c8dee2aaSAndroid Build Coastguard Worker 
1575*c8dee2aaSAndroid Build Coastguard Worker     if (numSlots > 4) {
1576*c8dee2aaSAndroid Build Coastguard Worker         this->appendAdjacentNWayBinaryOp(pipeline, alloc, baseStage, dst, src, numSlots);
1577*c8dee2aaSAndroid Build Coastguard Worker         return;
1578*c8dee2aaSAndroid Build Coastguard Worker     }
1579*c8dee2aaSAndroid Build Coastguard Worker     if (numSlots > 0) {
1580*c8dee2aaSAndroid Build Coastguard Worker         auto specializedStage = (ProgramOp)((int)baseStage + numSlots);
1581*c8dee2aaSAndroid Build Coastguard Worker         pipeline->push_back({specializedStage, basePtr + dst});
1582*c8dee2aaSAndroid Build Coastguard Worker     }
1583*c8dee2aaSAndroid Build Coastguard Worker }
1584*c8dee2aaSAndroid Build Coastguard Worker 
appendAdjacentNWayTernaryOp(TArray<Stage> * pipeline,SkArenaAlloc * alloc,ProgramOp stage,std::byte * basePtr,SkRPOffset dst,SkRPOffset src0,SkRPOffset src1,int numSlots) const1585*c8dee2aaSAndroid Build Coastguard Worker void Program::appendAdjacentNWayTernaryOp(TArray<Stage>* pipeline, SkArenaAlloc* alloc,
1586*c8dee2aaSAndroid Build Coastguard Worker                                           ProgramOp stage, std::byte* basePtr, SkRPOffset dst,
1587*c8dee2aaSAndroid Build Coastguard Worker                                           SkRPOffset src0, SkRPOffset src1, int numSlots) const {
1588*c8dee2aaSAndroid Build Coastguard Worker     // The float pointers must all be immediately adjacent to each other.
1589*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(numSlots >= 0);
1590*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT((dst  + SkOpts::raster_pipeline_highp_stride * numSlots * sizeof(float)) == src0);
1591*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT((src0 + SkOpts::raster_pipeline_highp_stride * numSlots * sizeof(float)) == src1);
1592*c8dee2aaSAndroid Build Coastguard Worker 
1593*c8dee2aaSAndroid Build Coastguard Worker     if (numSlots > 0) {
1594*c8dee2aaSAndroid Build Coastguard Worker         SkRasterPipeline_TernaryOpCtx ctx;
1595*c8dee2aaSAndroid Build Coastguard Worker         ctx.dst = dst;
1596*c8dee2aaSAndroid Build Coastguard Worker         ctx.delta = src0 - dst;
1597*c8dee2aaSAndroid Build Coastguard Worker         pipeline->push_back({stage, SkRPCtxUtils::Pack(ctx, alloc)});
1598*c8dee2aaSAndroid Build Coastguard Worker     }
1599*c8dee2aaSAndroid Build Coastguard Worker }
1600*c8dee2aaSAndroid Build Coastguard Worker 
appendAdjacentMultiSlotTernaryOp(TArray<Stage> * pipeline,SkArenaAlloc * alloc,ProgramOp baseStage,std::byte * basePtr,SkRPOffset dst,SkRPOffset src0,SkRPOffset src1,int numSlots) const1601*c8dee2aaSAndroid Build Coastguard Worker void Program::appendAdjacentMultiSlotTernaryOp(TArray<Stage>* pipeline, SkArenaAlloc* alloc,
1602*c8dee2aaSAndroid Build Coastguard Worker                                                ProgramOp baseStage, std::byte* basePtr,
1603*c8dee2aaSAndroid Build Coastguard Worker                                                SkRPOffset dst, SkRPOffset src0, SkRPOffset src1,
1604*c8dee2aaSAndroid Build Coastguard Worker                                                int numSlots) const {
1605*c8dee2aaSAndroid Build Coastguard Worker     // The float pointers must all be immediately adjacent to each other.
1606*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(numSlots >= 0);
1607*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT((dst  + SkOpts::raster_pipeline_highp_stride * numSlots * sizeof(float)) == src0);
1608*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT((src0 + SkOpts::raster_pipeline_highp_stride * numSlots * sizeof(float)) == src1);
1609*c8dee2aaSAndroid Build Coastguard Worker 
1610*c8dee2aaSAndroid Build Coastguard Worker     if (numSlots > 4) {
1611*c8dee2aaSAndroid Build Coastguard Worker         this->appendAdjacentNWayTernaryOp(pipeline, alloc, baseStage, basePtr,
1612*c8dee2aaSAndroid Build Coastguard Worker                                           dst, src0, src1, numSlots);
1613*c8dee2aaSAndroid Build Coastguard Worker         return;
1614*c8dee2aaSAndroid Build Coastguard Worker     }
1615*c8dee2aaSAndroid Build Coastguard Worker     if (numSlots > 0) {
1616*c8dee2aaSAndroid Build Coastguard Worker         auto specializedStage = (ProgramOp)((int)baseStage + numSlots);
1617*c8dee2aaSAndroid Build Coastguard Worker         pipeline->push_back({specializedStage, basePtr + dst});
1618*c8dee2aaSAndroid Build Coastguard Worker     }
1619*c8dee2aaSAndroid Build Coastguard Worker }
1620*c8dee2aaSAndroid Build Coastguard Worker 
appendStackRewindForNonTailcallers(TArray<Stage> * pipeline) const1621*c8dee2aaSAndroid Build Coastguard Worker void Program::appendStackRewindForNonTailcallers(TArray<Stage>* pipeline) const {
1622*c8dee2aaSAndroid Build Coastguard Worker #if defined(SKSL_STANDALONE) || !SK_HAS_MUSTTAIL
1623*c8dee2aaSAndroid Build Coastguard Worker     // When SK_HAS_MUSTTAIL is not enabled, stack rewinds are critical because because the stack may
1624*c8dee2aaSAndroid Build Coastguard Worker     // grow after every single SkSL stage.
1625*c8dee2aaSAndroid Build Coastguard Worker     this->appendStackRewind(pipeline);
1626*c8dee2aaSAndroid Build Coastguard Worker #endif
1627*c8dee2aaSAndroid Build Coastguard Worker }
1628*c8dee2aaSAndroid Build Coastguard Worker 
appendStackRewind(TArray<Stage> * pipeline) const1629*c8dee2aaSAndroid Build Coastguard Worker void Program::appendStackRewind(TArray<Stage>* pipeline) const {
1630*c8dee2aaSAndroid Build Coastguard Worker     pipeline->push_back({ProgramOp::stack_rewind, nullptr});
1631*c8dee2aaSAndroid Build Coastguard Worker }
1632*c8dee2aaSAndroid Build Coastguard Worker 
invoke_shader(int childIdx)1633*c8dee2aaSAndroid Build Coastguard Worker void Builder::invoke_shader(int childIdx) {
1634*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::invoke_shader, {}, childIdx);
1635*c8dee2aaSAndroid Build Coastguard Worker }
1636*c8dee2aaSAndroid Build Coastguard Worker 
invoke_color_filter(int childIdx)1637*c8dee2aaSAndroid Build Coastguard Worker void Builder::invoke_color_filter(int childIdx) {
1638*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::invoke_color_filter, {}, childIdx);
1639*c8dee2aaSAndroid Build Coastguard Worker }
1640*c8dee2aaSAndroid Build Coastguard Worker 
invoke_blender(int childIdx)1641*c8dee2aaSAndroid Build Coastguard Worker void Builder::invoke_blender(int childIdx) {
1642*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::invoke_blender, {}, childIdx);
1643*c8dee2aaSAndroid Build Coastguard Worker }
1644*c8dee2aaSAndroid Build Coastguard Worker 
invoke_to_linear_srgb()1645*c8dee2aaSAndroid Build Coastguard Worker void Builder::invoke_to_linear_srgb() {
1646*c8dee2aaSAndroid Build Coastguard Worker     // The intrinsics accept a three-component value; add a fourth padding element (which will be
1647*c8dee2aaSAndroid Build Coastguard Worker     // ignored) since our RP ops deal in RGBA colors.
1648*c8dee2aaSAndroid Build Coastguard Worker     this->pad_stack(1);
1649*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::invoke_to_linear_srgb, {});
1650*c8dee2aaSAndroid Build Coastguard Worker     this->discard_stack(1);
1651*c8dee2aaSAndroid Build Coastguard Worker }
1652*c8dee2aaSAndroid Build Coastguard Worker 
invoke_from_linear_srgb()1653*c8dee2aaSAndroid Build Coastguard Worker void Builder::invoke_from_linear_srgb() {
1654*c8dee2aaSAndroid Build Coastguard Worker     // The intrinsics accept a three-component value; add a fourth padding element (which will be
1655*c8dee2aaSAndroid Build Coastguard Worker     // ignored) since our RP ops deal in RGBA colors.
1656*c8dee2aaSAndroid Build Coastguard Worker     this->pad_stack(1);
1657*c8dee2aaSAndroid Build Coastguard Worker     this->appendInstruction(BuilderOp::invoke_from_linear_srgb, {});
1658*c8dee2aaSAndroid Build Coastguard Worker     this->discard_stack(1);
1659*c8dee2aaSAndroid Build Coastguard Worker }
1660*c8dee2aaSAndroid Build Coastguard Worker 
context_bit_pun(intptr_t val)1661*c8dee2aaSAndroid Build Coastguard Worker static void* context_bit_pun(intptr_t val) {
1662*c8dee2aaSAndroid Build Coastguard Worker     return sk_bit_cast<void*>(val);
1663*c8dee2aaSAndroid Build Coastguard Worker }
1664*c8dee2aaSAndroid Build Coastguard Worker 
allocateSlotData(SkArenaAlloc * alloc) const1665*c8dee2aaSAndroid Build Coastguard Worker std::optional<Program::SlotData> Program::allocateSlotData(SkArenaAlloc* alloc) const {
1666*c8dee2aaSAndroid Build Coastguard Worker     // Allocate a contiguous slab of slot data for immutables, values, and stack entries.
1667*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1668*c8dee2aaSAndroid Build Coastguard Worker     const int scalarWidth = 1 * sizeof(float);
1669*c8dee2aaSAndroid Build Coastguard Worker     const int vectorWidth = N * sizeof(float);
1670*c8dee2aaSAndroid Build Coastguard Worker     SkSafeMath safe;
1671*c8dee2aaSAndroid Build Coastguard Worker     size_t allocSize = safe.add(safe.mul(vectorWidth, safe.add(fNumValueSlots, fNumTempStackSlots)),
1672*c8dee2aaSAndroid Build Coastguard Worker                                 safe.mul(scalarWidth, fNumImmutableSlots));
1673*c8dee2aaSAndroid Build Coastguard Worker     if (!safe || !SkTFitsIn<int>(allocSize)) {
1674*c8dee2aaSAndroid Build Coastguard Worker         return std::nullopt;
1675*c8dee2aaSAndroid Build Coastguard Worker     }
1676*c8dee2aaSAndroid Build Coastguard Worker     float* slotPtr = static_cast<float*>(alloc->makeBytesAlignedTo(allocSize, vectorWidth));
1677*c8dee2aaSAndroid Build Coastguard Worker     sk_bzero(slotPtr, allocSize);
1678*c8dee2aaSAndroid Build Coastguard Worker 
1679*c8dee2aaSAndroid Build Coastguard Worker     // Store the temp stack immediately after the values, and immutable data after the stack.
1680*c8dee2aaSAndroid Build Coastguard Worker     SlotData s;
1681*c8dee2aaSAndroid Build Coastguard Worker     s.values    = SkSpan<float>{slotPtr,        N * fNumValueSlots};
1682*c8dee2aaSAndroid Build Coastguard Worker     s.stack     = SkSpan<float>{s.values.end(), N * fNumTempStackSlots};
1683*c8dee2aaSAndroid Build Coastguard Worker     s.immutable = SkSpan<float>{s.stack.end(),  1 * fNumImmutableSlots};
1684*c8dee2aaSAndroid Build Coastguard Worker     return s;
1685*c8dee2aaSAndroid Build Coastguard Worker }
1686*c8dee2aaSAndroid Build Coastguard Worker 
appendStages(SkRasterPipeline * pipeline,SkArenaAlloc * alloc,RP::Callbacks * callbacks,SkSpan<const float> uniforms) const1687*c8dee2aaSAndroid Build Coastguard Worker bool Program::appendStages(SkRasterPipeline* pipeline,
1688*c8dee2aaSAndroid Build Coastguard Worker                            SkArenaAlloc* alloc,
1689*c8dee2aaSAndroid Build Coastguard Worker                            RP::Callbacks* callbacks,
1690*c8dee2aaSAndroid Build Coastguard Worker                            SkSpan<const float> uniforms) const {
1691*c8dee2aaSAndroid Build Coastguard Worker #if defined(SKSL_STANDALONE)
1692*c8dee2aaSAndroid Build Coastguard Worker     return false;
1693*c8dee2aaSAndroid Build Coastguard Worker #else
1694*c8dee2aaSAndroid Build Coastguard Worker     // Convert our Instruction list to an array of ProgramOps.
1695*c8dee2aaSAndroid Build Coastguard Worker     TArray<Stage> stages;
1696*c8dee2aaSAndroid Build Coastguard Worker     std::optional<SlotData> slotData = this->allocateSlotData(alloc);
1697*c8dee2aaSAndroid Build Coastguard Worker     if (!slotData) {
1698*c8dee2aaSAndroid Build Coastguard Worker         return false;
1699*c8dee2aaSAndroid Build Coastguard Worker     }
1700*c8dee2aaSAndroid Build Coastguard Worker     this->makeStages(&stages, alloc, uniforms, *slotData);
1701*c8dee2aaSAndroid Build Coastguard Worker 
1702*c8dee2aaSAndroid Build Coastguard Worker     // Allocate buffers for branch targets and labels; these are needed to convert labels into
1703*c8dee2aaSAndroid Build Coastguard Worker     // actual offsets into the pipeline and fix up branches.
1704*c8dee2aaSAndroid Build Coastguard Worker     TArray<SkRasterPipeline_BranchCtx*> branchContexts;
1705*c8dee2aaSAndroid Build Coastguard Worker     branchContexts.reserve_exact(fNumLabels);
1706*c8dee2aaSAndroid Build Coastguard Worker     TArray<int> labelOffsets;
1707*c8dee2aaSAndroid Build Coastguard Worker     labelOffsets.push_back_n(fNumLabels, -1);
1708*c8dee2aaSAndroid Build Coastguard Worker     TArray<int> branchGoesToLabel;
1709*c8dee2aaSAndroid Build Coastguard Worker     branchGoesToLabel.reserve_exact(fNumLabels);
1710*c8dee2aaSAndroid Build Coastguard Worker 
1711*c8dee2aaSAndroid Build Coastguard Worker     auto resetBasePointer = [&]() {
1712*c8dee2aaSAndroid Build Coastguard Worker         // Whenever we hand off control to another shader, we have to assume that it might overwrite
1713*c8dee2aaSAndroid Build Coastguard Worker         // the base pointer (if it uses SkSL, it will!), so we reset it on return.
1714*c8dee2aaSAndroid Build Coastguard Worker         pipeline->append(SkRasterPipelineOp::set_base_pointer, (*slotData).values.data());
1715*c8dee2aaSAndroid Build Coastguard Worker     };
1716*c8dee2aaSAndroid Build Coastguard Worker 
1717*c8dee2aaSAndroid Build Coastguard Worker     resetBasePointer();
1718*c8dee2aaSAndroid Build Coastguard Worker 
1719*c8dee2aaSAndroid Build Coastguard Worker     for (const Stage& stage : stages) {
1720*c8dee2aaSAndroid Build Coastguard Worker         switch (stage.op) {
1721*c8dee2aaSAndroid Build Coastguard Worker             case ProgramOp::stack_rewind:
1722*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->appendStackRewind();
1723*c8dee2aaSAndroid Build Coastguard Worker                 break;
1724*c8dee2aaSAndroid Build Coastguard Worker 
1725*c8dee2aaSAndroid Build Coastguard Worker             case ProgramOp::invoke_shader:
1726*c8dee2aaSAndroid Build Coastguard Worker                 if (!callbacks || !callbacks->appendShader(sk_bit_cast<intptr_t>(stage.ctx))) {
1727*c8dee2aaSAndroid Build Coastguard Worker                     return false;
1728*c8dee2aaSAndroid Build Coastguard Worker                 }
1729*c8dee2aaSAndroid Build Coastguard Worker                 resetBasePointer();
1730*c8dee2aaSAndroid Build Coastguard Worker                 break;
1731*c8dee2aaSAndroid Build Coastguard Worker 
1732*c8dee2aaSAndroid Build Coastguard Worker             case ProgramOp::invoke_color_filter:
1733*c8dee2aaSAndroid Build Coastguard Worker                 if (!callbacks || !callbacks->appendColorFilter(sk_bit_cast<intptr_t>(stage.ctx))) {
1734*c8dee2aaSAndroid Build Coastguard Worker                     return false;
1735*c8dee2aaSAndroid Build Coastguard Worker                 }
1736*c8dee2aaSAndroid Build Coastguard Worker                 resetBasePointer();
1737*c8dee2aaSAndroid Build Coastguard Worker                 break;
1738*c8dee2aaSAndroid Build Coastguard Worker 
1739*c8dee2aaSAndroid Build Coastguard Worker             case ProgramOp::invoke_blender:
1740*c8dee2aaSAndroid Build Coastguard Worker                 if (!callbacks || !callbacks->appendBlender(sk_bit_cast<intptr_t>(stage.ctx))) {
1741*c8dee2aaSAndroid Build Coastguard Worker                     return false;
1742*c8dee2aaSAndroid Build Coastguard Worker                 }
1743*c8dee2aaSAndroid Build Coastguard Worker                 resetBasePointer();
1744*c8dee2aaSAndroid Build Coastguard Worker                 break;
1745*c8dee2aaSAndroid Build Coastguard Worker 
1746*c8dee2aaSAndroid Build Coastguard Worker             case ProgramOp::invoke_to_linear_srgb:
1747*c8dee2aaSAndroid Build Coastguard Worker                 if (!callbacks) {
1748*c8dee2aaSAndroid Build Coastguard Worker                     return false;
1749*c8dee2aaSAndroid Build Coastguard Worker                 }
1750*c8dee2aaSAndroid Build Coastguard Worker                 callbacks->toLinearSrgb(stage.ctx);
1751*c8dee2aaSAndroid Build Coastguard Worker                 // A ColorSpaceXform shouldn't ever alter the base pointer, so we don't need to call
1752*c8dee2aaSAndroid Build Coastguard Worker                 // resetBasePointer here.
1753*c8dee2aaSAndroid Build Coastguard Worker                 break;
1754*c8dee2aaSAndroid Build Coastguard Worker 
1755*c8dee2aaSAndroid Build Coastguard Worker             case ProgramOp::invoke_from_linear_srgb:
1756*c8dee2aaSAndroid Build Coastguard Worker                 if (!callbacks) {
1757*c8dee2aaSAndroid Build Coastguard Worker                     return false;
1758*c8dee2aaSAndroid Build Coastguard Worker                 }
1759*c8dee2aaSAndroid Build Coastguard Worker                 callbacks->fromLinearSrgb(stage.ctx);
1760*c8dee2aaSAndroid Build Coastguard Worker                 // A ColorSpaceXform shouldn't ever alter the base pointer, so we don't need to call
1761*c8dee2aaSAndroid Build Coastguard Worker                 // resetBasePointer here.
1762*c8dee2aaSAndroid Build Coastguard Worker                 break;
1763*c8dee2aaSAndroid Build Coastguard Worker 
1764*c8dee2aaSAndroid Build Coastguard Worker             case ProgramOp::label: {
1765*c8dee2aaSAndroid Build Coastguard Worker                 // Remember the absolute pipeline position of this label.
1766*c8dee2aaSAndroid Build Coastguard Worker                 int labelID = sk_bit_cast<intptr_t>(stage.ctx);
1767*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(labelID >= 0 && labelID < fNumLabels);
1768*c8dee2aaSAndroid Build Coastguard Worker                 labelOffsets[labelID] = pipeline->getNumStages();
1769*c8dee2aaSAndroid Build Coastguard Worker                 break;
1770*c8dee2aaSAndroid Build Coastguard Worker             }
1771*c8dee2aaSAndroid Build Coastguard Worker             case ProgramOp::jump:
1772*c8dee2aaSAndroid Build Coastguard Worker             case ProgramOp::branch_if_all_lanes_active:
1773*c8dee2aaSAndroid Build Coastguard Worker             case ProgramOp::branch_if_any_lanes_active:
1774*c8dee2aaSAndroid Build Coastguard Worker             case ProgramOp::branch_if_no_lanes_active:
1775*c8dee2aaSAndroid Build Coastguard Worker             case ProgramOp::branch_if_no_active_lanes_eq: {
1776*c8dee2aaSAndroid Build Coastguard Worker                 // The branch context contain a valid label ID at this point.
1777*c8dee2aaSAndroid Build Coastguard Worker                 auto* branchCtx = static_cast<SkRasterPipeline_BranchCtx*>(stage.ctx);
1778*c8dee2aaSAndroid Build Coastguard Worker                 int labelID = branchCtx->offset;
1779*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(labelID >= 0 && labelID < fNumLabels);
1780*c8dee2aaSAndroid Build Coastguard Worker 
1781*c8dee2aaSAndroid Build Coastguard Worker                 // Replace the label ID in the branch context with the absolute pipeline position.
1782*c8dee2aaSAndroid Build Coastguard Worker                 // We will go back over the branch targets at the end and fix them up.
1783*c8dee2aaSAndroid Build Coastguard Worker                 branchCtx->offset = pipeline->getNumStages();
1784*c8dee2aaSAndroid Build Coastguard Worker 
1785*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(branchContexts.size() == branchGoesToLabel.size());
1786*c8dee2aaSAndroid Build Coastguard Worker                 branchContexts.push_back(branchCtx);
1787*c8dee2aaSAndroid Build Coastguard Worker                 branchGoesToLabel.push_back(labelID);
1788*c8dee2aaSAndroid Build Coastguard Worker                 [[fallthrough]];
1789*c8dee2aaSAndroid Build Coastguard Worker             }
1790*c8dee2aaSAndroid Build Coastguard Worker             default:
1791*c8dee2aaSAndroid Build Coastguard Worker                 // Append a regular op to the program.
1792*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT((int)stage.op < kNumRasterPipelineHighpOps);
1793*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->append((SkRasterPipelineOp)stage.op, stage.ctx);
1794*c8dee2aaSAndroid Build Coastguard Worker                 break;
1795*c8dee2aaSAndroid Build Coastguard Worker         }
1796*c8dee2aaSAndroid Build Coastguard Worker     }
1797*c8dee2aaSAndroid Build Coastguard Worker 
1798*c8dee2aaSAndroid Build Coastguard Worker     // Now that we have assembled the program and know the pipeline positions of each label and
1799*c8dee2aaSAndroid Build Coastguard Worker     // branch, fix up every branch target.
1800*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(branchContexts.size() == branchGoesToLabel.size());
1801*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < branchContexts.size(); ++index) {
1802*c8dee2aaSAndroid Build Coastguard Worker         int branchFromIdx = branchContexts[index]->offset;
1803*c8dee2aaSAndroid Build Coastguard Worker         int branchToIdx = labelOffsets[branchGoesToLabel[index]];
1804*c8dee2aaSAndroid Build Coastguard Worker         branchContexts[index]->offset = branchToIdx - branchFromIdx;
1805*c8dee2aaSAndroid Build Coastguard Worker     }
1806*c8dee2aaSAndroid Build Coastguard Worker 
1807*c8dee2aaSAndroid Build Coastguard Worker     return true;
1808*c8dee2aaSAndroid Build Coastguard Worker #endif
1809*c8dee2aaSAndroid Build Coastguard Worker }
1810*c8dee2aaSAndroid Build Coastguard Worker 
makeStages(TArray<Stage> * pipeline,SkArenaAlloc * alloc,SkSpan<const float> uniforms,const SlotData & slots) const1811*c8dee2aaSAndroid Build Coastguard Worker void Program::makeStages(TArray<Stage>* pipeline,
1812*c8dee2aaSAndroid Build Coastguard Worker                          SkArenaAlloc* alloc,
1813*c8dee2aaSAndroid Build Coastguard Worker                          SkSpan<const float> uniforms,
1814*c8dee2aaSAndroid Build Coastguard Worker                          const SlotData& slots) const {
1815*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fNumUniformSlots == SkToInt(uniforms.size()));
1816*c8dee2aaSAndroid Build Coastguard Worker 
1817*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
1818*c8dee2aaSAndroid Build Coastguard Worker     int mostRecentRewind = 0;
1819*c8dee2aaSAndroid Build Coastguard Worker 
1820*c8dee2aaSAndroid Build Coastguard Worker     // Assemble a map holding the current stack-top for each temporary stack. Position each temp
1821*c8dee2aaSAndroid Build Coastguard Worker     // stack immediately after the previous temp stack; temp stacks are never allowed to overlap.
1822*c8dee2aaSAndroid Build Coastguard Worker     int pos = 0;
1823*c8dee2aaSAndroid Build Coastguard Worker     TArray<float*> tempStackMap;
1824*c8dee2aaSAndroid Build Coastguard Worker     tempStackMap.resize(fTempStackMaxDepths.size());
1825*c8dee2aaSAndroid Build Coastguard Worker     for (int idx = 0; idx < fTempStackMaxDepths.size(); ++idx) {
1826*c8dee2aaSAndroid Build Coastguard Worker         tempStackMap[idx] = slots.stack.begin() + (pos * N);
1827*c8dee2aaSAndroid Build Coastguard Worker         pos += fTempStackMaxDepths[idx];
1828*c8dee2aaSAndroid Build Coastguard Worker     }
1829*c8dee2aaSAndroid Build Coastguard Worker 
1830*c8dee2aaSAndroid Build Coastguard Worker     // Track labels that we have reached in processing.
1831*c8dee2aaSAndroid Build Coastguard Worker     TArray<int> labelToInstructionIndex;
1832*c8dee2aaSAndroid Build Coastguard Worker     labelToInstructionIndex.push_back_n(fNumLabels, -1);
1833*c8dee2aaSAndroid Build Coastguard Worker 
1834*c8dee2aaSAndroid Build Coastguard Worker     int mostRecentInvocationInstructionIdx = 0;
1835*c8dee2aaSAndroid Build Coastguard Worker 
1836*c8dee2aaSAndroid Build Coastguard Worker     auto EmitStackRewindForBackwardsBranch = [&](int labelID) {
1837*c8dee2aaSAndroid Build Coastguard Worker         // If we have already encountered the label associated with this branch, this is a
1838*c8dee2aaSAndroid Build Coastguard Worker         // backwards branch. Add a stack-rewind immediately before the branch to ensure that
1839*c8dee2aaSAndroid Build Coastguard Worker         // long-running loops don't use an unbounded amount of stack space.
1840*c8dee2aaSAndroid Build Coastguard Worker         int labelInstructionIdx = labelToInstructionIndex[labelID];
1841*c8dee2aaSAndroid Build Coastguard Worker         if (labelInstructionIdx >= 0) {
1842*c8dee2aaSAndroid Build Coastguard Worker             if (mostRecentInvocationInstructionIdx > labelInstructionIdx) {
1843*c8dee2aaSAndroid Build Coastguard Worker                 // The backwards-branch range includes an external invocation to another shader,
1844*c8dee2aaSAndroid Build Coastguard Worker                 // color filter, blender, or colorspace conversion. In this case, we always emit a
1845*c8dee2aaSAndroid Build Coastguard Worker                 // stack rewind, since the non-tailcall stages may exist on the stack.
1846*c8dee2aaSAndroid Build Coastguard Worker                 this->appendStackRewind(pipeline);
1847*c8dee2aaSAndroid Build Coastguard Worker             } else {
1848*c8dee2aaSAndroid Build Coastguard Worker                 // The backwards-branch range only includes SkSL ops. If tailcalling is supported,
1849*c8dee2aaSAndroid Build Coastguard Worker                 // stack rewinding isn't needed. If the platform cannot tailcall, we need to rewind.
1850*c8dee2aaSAndroid Build Coastguard Worker                 this->appendStackRewindForNonTailcallers(pipeline);
1851*c8dee2aaSAndroid Build Coastguard Worker             }
1852*c8dee2aaSAndroid Build Coastguard Worker             mostRecentRewind = pipeline->size();
1853*c8dee2aaSAndroid Build Coastguard Worker         }
1854*c8dee2aaSAndroid Build Coastguard Worker     };
1855*c8dee2aaSAndroid Build Coastguard Worker 
1856*c8dee2aaSAndroid Build Coastguard Worker     auto* const basePtr = (std::byte*)slots.values.data();
1857*c8dee2aaSAndroid Build Coastguard Worker     auto OffsetFromBase = [&](const void* ptr) -> SkRPOffset {
1858*c8dee2aaSAndroid Build Coastguard Worker         return (SkRPOffset)((const std::byte*)ptr - basePtr);
1859*c8dee2aaSAndroid Build Coastguard Worker     };
1860*c8dee2aaSAndroid Build Coastguard Worker 
1861*c8dee2aaSAndroid Build Coastguard Worker     // Copy all immutable values into the immutable slots.
1862*c8dee2aaSAndroid Build Coastguard Worker     for (const Instruction& inst : fInstructions) {
1863*c8dee2aaSAndroid Build Coastguard Worker         if (inst.fOp == BuilderOp::store_immutable_value) {
1864*c8dee2aaSAndroid Build Coastguard Worker             slots.immutable[inst.fSlotA] = sk_bit_cast<float>(inst.fImmA);
1865*c8dee2aaSAndroid Build Coastguard Worker         }
1866*c8dee2aaSAndroid Build Coastguard Worker     }
1867*c8dee2aaSAndroid Build Coastguard Worker 
1868*c8dee2aaSAndroid Build Coastguard Worker     // Write each BuilderOp to the pipeline array.
1869*c8dee2aaSAndroid Build Coastguard Worker     pipeline->reserve_exact(pipeline->size() + fInstructions.size());
1870*c8dee2aaSAndroid Build Coastguard Worker     for (int instructionIdx = 0; instructionIdx < fInstructions.size(); ++instructionIdx) {
1871*c8dee2aaSAndroid Build Coastguard Worker         const Instruction& inst = fInstructions[instructionIdx];
1872*c8dee2aaSAndroid Build Coastguard Worker 
1873*c8dee2aaSAndroid Build Coastguard Worker         auto ImmutableA = [&]() { return &slots.immutable[1 * inst.fSlotA]; };
1874*c8dee2aaSAndroid Build Coastguard Worker         auto ImmutableB = [&]() { return &slots.immutable[1 * inst.fSlotB]; };
1875*c8dee2aaSAndroid Build Coastguard Worker         auto SlotA      = [&]() { return &slots.values[N * inst.fSlotA]; };
1876*c8dee2aaSAndroid Build Coastguard Worker         auto SlotB      = [&]() { return &slots.values[N * inst.fSlotB]; };
1877*c8dee2aaSAndroid Build Coastguard Worker         auto UniformA   = [&]() { return &uniforms[inst.fSlotA]; };
1878*c8dee2aaSAndroid Build Coastguard Worker         auto AllocTraceContext = [&](auto* ctx) {
1879*c8dee2aaSAndroid Build Coastguard Worker             // We pass `ctx` solely for its type; the value is unused.
1880*c8dee2aaSAndroid Build Coastguard Worker             using ContextType = typename std::remove_reference<decltype(*ctx)>::type;
1881*c8dee2aaSAndroid Build Coastguard Worker             ctx = alloc->make<ContextType>();
1882*c8dee2aaSAndroid Build Coastguard Worker             ctx->traceMask = reinterpret_cast<int*>(tempStackMap[inst.fImmA] - N);
1883*c8dee2aaSAndroid Build Coastguard Worker             ctx->traceHook = fTraceHook.get();
1884*c8dee2aaSAndroid Build Coastguard Worker             return ctx;
1885*c8dee2aaSAndroid Build Coastguard Worker         };
1886*c8dee2aaSAndroid Build Coastguard Worker         float*& tempStackPtr = tempStackMap[inst.fStackID];
1887*c8dee2aaSAndroid Build Coastguard Worker 
1888*c8dee2aaSAndroid Build Coastguard Worker         switch (inst.fOp) {
1889*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::label: {
1890*c8dee2aaSAndroid Build Coastguard Worker                 intptr_t labelID = inst.fImmA;
1891*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(labelID >= 0 && labelID < fNumLabels);
1892*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(labelToInstructionIndex[labelID] == -1);
1893*c8dee2aaSAndroid Build Coastguard Worker                 labelToInstructionIndex[labelID] = instructionIdx;
1894*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::label, context_bit_pun(labelID)});
1895*c8dee2aaSAndroid Build Coastguard Worker                 break;
1896*c8dee2aaSAndroid Build Coastguard Worker             }
1897*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::jump:
1898*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::branch_if_any_lanes_active:
1899*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::branch_if_no_lanes_active: {
1900*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(inst.fImmA >= 0 && inst.fImmA < fNumLabels);
1901*c8dee2aaSAndroid Build Coastguard Worker                 EmitStackRewindForBackwardsBranch(inst.fImmA);
1902*c8dee2aaSAndroid Build Coastguard Worker 
1903*c8dee2aaSAndroid Build Coastguard Worker                 auto* ctx = alloc->make<SkRasterPipeline_BranchCtx>();
1904*c8dee2aaSAndroid Build Coastguard Worker                 ctx->offset = inst.fImmA;
1905*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({(ProgramOp)inst.fOp, ctx});
1906*c8dee2aaSAndroid Build Coastguard Worker                 break;
1907*c8dee2aaSAndroid Build Coastguard Worker             }
1908*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::branch_if_all_lanes_active: {
1909*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(inst.fImmA >= 0 && inst.fImmA < fNumLabels);
1910*c8dee2aaSAndroid Build Coastguard Worker                 EmitStackRewindForBackwardsBranch(inst.fImmA);
1911*c8dee2aaSAndroid Build Coastguard Worker 
1912*c8dee2aaSAndroid Build Coastguard Worker                 auto* ctx = alloc->make<SkRasterPipeline_BranchIfAllLanesActiveCtx>();
1913*c8dee2aaSAndroid Build Coastguard Worker                 ctx->offset = inst.fImmA;
1914*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::branch_if_all_lanes_active, ctx});
1915*c8dee2aaSAndroid Build Coastguard Worker                 break;
1916*c8dee2aaSAndroid Build Coastguard Worker             }
1917*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::branch_if_no_active_lanes_on_stack_top_equal: {
1918*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(inst.fImmA >= 0 && inst.fImmA < fNumLabels);
1919*c8dee2aaSAndroid Build Coastguard Worker                 EmitStackRewindForBackwardsBranch(inst.fImmA);
1920*c8dee2aaSAndroid Build Coastguard Worker 
1921*c8dee2aaSAndroid Build Coastguard Worker                 auto* ctx = alloc->make<SkRasterPipeline_BranchIfEqualCtx>();
1922*c8dee2aaSAndroid Build Coastguard Worker                 ctx->offset = inst.fImmA;
1923*c8dee2aaSAndroid Build Coastguard Worker                 ctx->value = inst.fImmB;
1924*c8dee2aaSAndroid Build Coastguard Worker                 ctx->ptr = reinterpret_cast<int*>(tempStackPtr - N);
1925*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::branch_if_no_active_lanes_eq, ctx});
1926*c8dee2aaSAndroid Build Coastguard Worker                 break;
1927*c8dee2aaSAndroid Build Coastguard Worker             }
1928*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::init_lane_masks: {
1929*c8dee2aaSAndroid Build Coastguard Worker                 auto* ctx = alloc->make<SkRasterPipeline_InitLaneMasksCtx>();
1930*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::init_lane_masks, ctx});
1931*c8dee2aaSAndroid Build Coastguard Worker                 break;
1932*c8dee2aaSAndroid Build Coastguard Worker             }
1933*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::store_src_rg:
1934*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::store_src_rg, SlotA()});
1935*c8dee2aaSAndroid Build Coastguard Worker                 break;
1936*c8dee2aaSAndroid Build Coastguard Worker 
1937*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::store_src:
1938*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::store_src, SlotA()});
1939*c8dee2aaSAndroid Build Coastguard Worker                 break;
1940*c8dee2aaSAndroid Build Coastguard Worker 
1941*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::store_dst:
1942*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::store_dst, SlotA()});
1943*c8dee2aaSAndroid Build Coastguard Worker                 break;
1944*c8dee2aaSAndroid Build Coastguard Worker 
1945*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::store_device_xy01:
1946*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::store_device_xy01, SlotA()});
1947*c8dee2aaSAndroid Build Coastguard Worker                 break;
1948*c8dee2aaSAndroid Build Coastguard Worker 
1949*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::store_immutable_value:
1950*c8dee2aaSAndroid Build Coastguard Worker                 // The immutable slots were populated in an earlier pass.
1951*c8dee2aaSAndroid Build Coastguard Worker                 break;
1952*c8dee2aaSAndroid Build Coastguard Worker 
1953*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::load_src:
1954*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::load_src, SlotA()});
1955*c8dee2aaSAndroid Build Coastguard Worker                 break;
1956*c8dee2aaSAndroid Build Coastguard Worker 
1957*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::load_dst:
1958*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::load_dst, SlotA()});
1959*c8dee2aaSAndroid Build Coastguard Worker                 break;
1960*c8dee2aaSAndroid Build Coastguard Worker 
1961*c8dee2aaSAndroid Build Coastguard Worker             case ALL_SINGLE_SLOT_UNARY_OP_CASES: {
1962*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr - (inst.fImmA * N);
1963*c8dee2aaSAndroid Build Coastguard Worker                 this->appendSingleSlotUnaryOp(pipeline, (ProgramOp)inst.fOp, dst, inst.fImmA);
1964*c8dee2aaSAndroid Build Coastguard Worker                 break;
1965*c8dee2aaSAndroid Build Coastguard Worker             }
1966*c8dee2aaSAndroid Build Coastguard Worker             case ALL_MULTI_SLOT_UNARY_OP_CASES: {
1967*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr - (inst.fImmA * N);
1968*c8dee2aaSAndroid Build Coastguard Worker                 this->appendMultiSlotUnaryOp(pipeline, (ProgramOp)inst.fOp, dst, inst.fImmA);
1969*c8dee2aaSAndroid Build Coastguard Worker                 break;
1970*c8dee2aaSAndroid Build Coastguard Worker             }
1971*c8dee2aaSAndroid Build Coastguard Worker             case ALL_IMMEDIATE_BINARY_OP_CASES: {
1972*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = (inst.fSlotA == NA) ? tempStackPtr - (inst.fImmA * N)
1973*c8dee2aaSAndroid Build Coastguard Worker                                                  : SlotA();
1974*c8dee2aaSAndroid Build Coastguard Worker 
1975*c8dee2aaSAndroid Build Coastguard Worker                 this->appendImmediateBinaryOp(pipeline, alloc, (ProgramOp)inst.fOp,
1976*c8dee2aaSAndroid Build Coastguard Worker                                               OffsetFromBase(dst), inst.fImmB, inst.fImmA);
1977*c8dee2aaSAndroid Build Coastguard Worker                 break;
1978*c8dee2aaSAndroid Build Coastguard Worker             }
1979*c8dee2aaSAndroid Build Coastguard Worker             case ALL_N_WAY_BINARY_OP_CASES: {
1980*c8dee2aaSAndroid Build Coastguard Worker                 float* src = tempStackPtr - (inst.fImmA * N);
1981*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr - (inst.fImmA * 2 * N);
1982*c8dee2aaSAndroid Build Coastguard Worker                 this->appendAdjacentNWayBinaryOp(pipeline, alloc, (ProgramOp)inst.fOp,
1983*c8dee2aaSAndroid Build Coastguard Worker                                                  OffsetFromBase(dst), OffsetFromBase(src),
1984*c8dee2aaSAndroid Build Coastguard Worker                                                  inst.fImmA);
1985*c8dee2aaSAndroid Build Coastguard Worker                 break;
1986*c8dee2aaSAndroid Build Coastguard Worker             }
1987*c8dee2aaSAndroid Build Coastguard Worker             case ALL_MULTI_SLOT_BINARY_OP_CASES: {
1988*c8dee2aaSAndroid Build Coastguard Worker                 float* src = tempStackPtr - (inst.fImmA * N);
1989*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr - (inst.fImmA * 2 * N);
1990*c8dee2aaSAndroid Build Coastguard Worker                 this->appendAdjacentMultiSlotBinaryOp(pipeline, alloc, (ProgramOp)inst.fOp,
1991*c8dee2aaSAndroid Build Coastguard Worker                                                       basePtr,
1992*c8dee2aaSAndroid Build Coastguard Worker                                                       OffsetFromBase(dst),
1993*c8dee2aaSAndroid Build Coastguard Worker                                                       OffsetFromBase(src),
1994*c8dee2aaSAndroid Build Coastguard Worker                                                       inst.fImmA);
1995*c8dee2aaSAndroid Build Coastguard Worker                 break;
1996*c8dee2aaSAndroid Build Coastguard Worker             }
1997*c8dee2aaSAndroid Build Coastguard Worker             case ALL_N_WAY_TERNARY_OP_CASES: {
1998*c8dee2aaSAndroid Build Coastguard Worker                 float* src1 = tempStackPtr - (inst.fImmA * N);
1999*c8dee2aaSAndroid Build Coastguard Worker                 float* src0 = tempStackPtr - (inst.fImmA * 2 * N);
2000*c8dee2aaSAndroid Build Coastguard Worker                 float* dst  = tempStackPtr - (inst.fImmA * 3 * N);
2001*c8dee2aaSAndroid Build Coastguard Worker                 this->appendAdjacentNWayTernaryOp(pipeline, alloc, (ProgramOp)inst.fOp, basePtr,
2002*c8dee2aaSAndroid Build Coastguard Worker                                                   OffsetFromBase(dst),
2003*c8dee2aaSAndroid Build Coastguard Worker                                                   OffsetFromBase(src0),
2004*c8dee2aaSAndroid Build Coastguard Worker                                                   OffsetFromBase(src1),
2005*c8dee2aaSAndroid Build Coastguard Worker                                                   inst.fImmA);
2006*c8dee2aaSAndroid Build Coastguard Worker                 break;
2007*c8dee2aaSAndroid Build Coastguard Worker             }
2008*c8dee2aaSAndroid Build Coastguard Worker             case ALL_MULTI_SLOT_TERNARY_OP_CASES: {
2009*c8dee2aaSAndroid Build Coastguard Worker                 float* src1 = tempStackPtr - (inst.fImmA * N);
2010*c8dee2aaSAndroid Build Coastguard Worker                 float* src0 = tempStackPtr - (inst.fImmA * 2 * N);
2011*c8dee2aaSAndroid Build Coastguard Worker                 float* dst  = tempStackPtr - (inst.fImmA * 3 * N);
2012*c8dee2aaSAndroid Build Coastguard Worker                 this->appendAdjacentMultiSlotTernaryOp(pipeline, alloc,(ProgramOp)inst.fOp, basePtr,
2013*c8dee2aaSAndroid Build Coastguard Worker                                                        OffsetFromBase(dst),
2014*c8dee2aaSAndroid Build Coastguard Worker                                                        OffsetFromBase(src0),
2015*c8dee2aaSAndroid Build Coastguard Worker                                                        OffsetFromBase(src1),
2016*c8dee2aaSAndroid Build Coastguard Worker                                                        inst.fImmA);
2017*c8dee2aaSAndroid Build Coastguard Worker                 break;
2018*c8dee2aaSAndroid Build Coastguard Worker             }
2019*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::select: {
2020*c8dee2aaSAndroid Build Coastguard Worker                 float* src = tempStackPtr - (inst.fImmA * N);
2021*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr - (inst.fImmA * 2 * N);
2022*c8dee2aaSAndroid Build Coastguard Worker                 this->appendCopySlotsMasked(pipeline, alloc,
2023*c8dee2aaSAndroid Build Coastguard Worker                                             OffsetFromBase(dst),
2024*c8dee2aaSAndroid Build Coastguard Worker                                             OffsetFromBase(src),
2025*c8dee2aaSAndroid Build Coastguard Worker                                             inst.fImmA);
2026*c8dee2aaSAndroid Build Coastguard Worker                 break;
2027*c8dee2aaSAndroid Build Coastguard Worker             }
2028*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::copy_slot_masked:
2029*c8dee2aaSAndroid Build Coastguard Worker                 this->appendCopySlotsMasked(pipeline, alloc,
2030*c8dee2aaSAndroid Build Coastguard Worker                                             OffsetFromBase(SlotA()),
2031*c8dee2aaSAndroid Build Coastguard Worker                                             OffsetFromBase(SlotB()),
2032*c8dee2aaSAndroid Build Coastguard Worker                                             inst.fImmA);
2033*c8dee2aaSAndroid Build Coastguard Worker                 break;
2034*c8dee2aaSAndroid Build Coastguard Worker 
2035*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::copy_slot_unmasked:
2036*c8dee2aaSAndroid Build Coastguard Worker                 this->appendCopySlotsUnmasked(pipeline, alloc,
2037*c8dee2aaSAndroid Build Coastguard Worker                                               OffsetFromBase(SlotA()),
2038*c8dee2aaSAndroid Build Coastguard Worker                                               OffsetFromBase(SlotB()),
2039*c8dee2aaSAndroid Build Coastguard Worker                                               inst.fImmA);
2040*c8dee2aaSAndroid Build Coastguard Worker                 break;
2041*c8dee2aaSAndroid Build Coastguard Worker 
2042*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::copy_immutable_unmasked:
2043*c8dee2aaSAndroid Build Coastguard Worker                 this->appendCopyImmutableUnmasked(pipeline, alloc, basePtr,
2044*c8dee2aaSAndroid Build Coastguard Worker                                                   OffsetFromBase(SlotA()),
2045*c8dee2aaSAndroid Build Coastguard Worker                                                   OffsetFromBase(ImmutableB()),
2046*c8dee2aaSAndroid Build Coastguard Worker                                                   inst.fImmA);
2047*c8dee2aaSAndroid Build Coastguard Worker                 break;
2048*c8dee2aaSAndroid Build Coastguard Worker 
2049*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::refract_4_floats: {
2050*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr - (9 * N);
2051*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::refract_4_floats, dst});
2052*c8dee2aaSAndroid Build Coastguard Worker                 break;
2053*c8dee2aaSAndroid Build Coastguard Worker             }
2054*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::inverse_mat2:
2055*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::inverse_mat3:
2056*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::inverse_mat4: {
2057*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr - (inst.fImmA * N);
2058*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({(ProgramOp)inst.fOp, dst});
2059*c8dee2aaSAndroid Build Coastguard Worker                 break;
2060*c8dee2aaSAndroid Build Coastguard Worker             }
2061*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::dot_2_floats:
2062*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::dot_3_floats:
2063*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::dot_4_floats: {
2064*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr - (inst.fImmA * 2 * N);
2065*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({(ProgramOp)inst.fOp, dst});
2066*c8dee2aaSAndroid Build Coastguard Worker                 break;
2067*c8dee2aaSAndroid Build Coastguard Worker             }
2068*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::swizzle_1: {
2069*c8dee2aaSAndroid Build Coastguard Worker                 // A single-component swizzle just copies a slot and shrinks the stack; we can
2070*c8dee2aaSAndroid Build Coastguard Worker                 // slightly improve codegen by making that simplification here.
2071*c8dee2aaSAndroid Build Coastguard Worker                 int offset = inst.fImmB;
2072*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(offset >= 0 && offset <= 15);
2073*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr - (inst.fImmA * N);
2074*c8dee2aaSAndroid Build Coastguard Worker                 float* src = dst + (offset * N);
2075*c8dee2aaSAndroid Build Coastguard Worker                 if (src != dst) {
2076*c8dee2aaSAndroid Build Coastguard Worker                     this->appendCopySlotsUnmasked(pipeline, alloc,
2077*c8dee2aaSAndroid Build Coastguard Worker                                                   OffsetFromBase(dst),
2078*c8dee2aaSAndroid Build Coastguard Worker                                                   OffsetFromBase(src),
2079*c8dee2aaSAndroid Build Coastguard Worker                                                   /*numSlots=*/1);
2080*c8dee2aaSAndroid Build Coastguard Worker                 }
2081*c8dee2aaSAndroid Build Coastguard Worker                 break;
2082*c8dee2aaSAndroid Build Coastguard Worker             }
2083*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::swizzle_2:
2084*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::swizzle_3:
2085*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::swizzle_4: {
2086*c8dee2aaSAndroid Build Coastguard Worker                 SkRasterPipeline_SwizzleCtx ctx;
2087*c8dee2aaSAndroid Build Coastguard Worker                 ctx.dst = OffsetFromBase(tempStackPtr - (N * inst.fImmA));
2088*c8dee2aaSAndroid Build Coastguard Worker                 // Unpack component nybbles into byte-offsets pointing at stack slots.
2089*c8dee2aaSAndroid Build Coastguard Worker                 unpack_nybbles_to_offsets(inst.fImmB, SkSpan(ctx.offsets));
2090*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({(ProgramOp)inst.fOp, SkRPCtxUtils::Pack(ctx, alloc)});
2091*c8dee2aaSAndroid Build Coastguard Worker                 break;
2092*c8dee2aaSAndroid Build Coastguard Worker             }
2093*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::shuffle: {
2094*c8dee2aaSAndroid Build Coastguard Worker                 int consumed = inst.fImmA;
2095*c8dee2aaSAndroid Build Coastguard Worker                 int generated = inst.fImmB;
2096*c8dee2aaSAndroid Build Coastguard Worker 
2097*c8dee2aaSAndroid Build Coastguard Worker                 auto* ctx = alloc->make<SkRasterPipeline_ShuffleCtx>();
2098*c8dee2aaSAndroid Build Coastguard Worker                 ctx->ptr = reinterpret_cast<int32_t*>(tempStackPtr) - (N * consumed);
2099*c8dee2aaSAndroid Build Coastguard Worker                 ctx->count = generated;
2100*c8dee2aaSAndroid Build Coastguard Worker                 // Unpack immB and immC from nybble form into the offset array.
2101*c8dee2aaSAndroid Build Coastguard Worker                 unpack_nybbles_to_offsets(inst.fImmC, SkSpan(&ctx->offsets[0], 8));
2102*c8dee2aaSAndroid Build Coastguard Worker                 unpack_nybbles_to_offsets(inst.fImmD, SkSpan(&ctx->offsets[8], 8));
2103*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::shuffle, ctx});
2104*c8dee2aaSAndroid Build Coastguard Worker                 break;
2105*c8dee2aaSAndroid Build Coastguard Worker             }
2106*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::matrix_multiply_2:
2107*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::matrix_multiply_3:
2108*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::matrix_multiply_4: {
2109*c8dee2aaSAndroid Build Coastguard Worker                 int consumed = (inst.fImmB * inst.fImmC) +  // result
2110*c8dee2aaSAndroid Build Coastguard Worker                                (inst.fImmA * inst.fImmB) +  // left-matrix
2111*c8dee2aaSAndroid Build Coastguard Worker                                (inst.fImmC * inst.fImmD);   // right-matrix
2112*c8dee2aaSAndroid Build Coastguard Worker 
2113*c8dee2aaSAndroid Build Coastguard Worker                 SkRasterPipeline_MatrixMultiplyCtx ctx;
2114*c8dee2aaSAndroid Build Coastguard Worker                 ctx.dst = OffsetFromBase(tempStackPtr - (N * consumed));
2115*c8dee2aaSAndroid Build Coastguard Worker                 ctx.leftColumns  = inst.fImmA;
2116*c8dee2aaSAndroid Build Coastguard Worker                 ctx.leftRows     = inst.fImmB;
2117*c8dee2aaSAndroid Build Coastguard Worker                 ctx.rightColumns = inst.fImmC;
2118*c8dee2aaSAndroid Build Coastguard Worker                 ctx.rightRows    = inst.fImmD;
2119*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({(ProgramOp)inst.fOp, SkRPCtxUtils::Pack(ctx, alloc)});
2120*c8dee2aaSAndroid Build Coastguard Worker                 break;
2121*c8dee2aaSAndroid Build Coastguard Worker             }
2122*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::exchange_src: {
2123*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr - (4 * N);
2124*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::exchange_src, dst});
2125*c8dee2aaSAndroid Build Coastguard Worker                 break;
2126*c8dee2aaSAndroid Build Coastguard Worker             }
2127*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_src_rgba: {
2128*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr;
2129*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::store_src, dst});
2130*c8dee2aaSAndroid Build Coastguard Worker                 break;
2131*c8dee2aaSAndroid Build Coastguard Worker             }
2132*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_dst_rgba: {
2133*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr;
2134*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::store_dst, dst});
2135*c8dee2aaSAndroid Build Coastguard Worker                 break;
2136*c8dee2aaSAndroid Build Coastguard Worker             }
2137*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_device_xy01: {
2138*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr;
2139*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::store_device_xy01, dst});
2140*c8dee2aaSAndroid Build Coastguard Worker                 break;
2141*c8dee2aaSAndroid Build Coastguard Worker             }
2142*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::pop_src_rgba: {
2143*c8dee2aaSAndroid Build Coastguard Worker                 float* src = tempStackPtr - (4 * N);
2144*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::load_src, src});
2145*c8dee2aaSAndroid Build Coastguard Worker                 break;
2146*c8dee2aaSAndroid Build Coastguard Worker             }
2147*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::pop_dst_rgba: {
2148*c8dee2aaSAndroid Build Coastguard Worker                 float* src = tempStackPtr - (4 * N);
2149*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::load_dst, src});
2150*c8dee2aaSAndroid Build Coastguard Worker                 break;
2151*c8dee2aaSAndroid Build Coastguard Worker             }
2152*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_slots: {
2153*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr;
2154*c8dee2aaSAndroid Build Coastguard Worker                 this->appendCopySlotsUnmasked(pipeline, alloc,
2155*c8dee2aaSAndroid Build Coastguard Worker                                               OffsetFromBase(dst),
2156*c8dee2aaSAndroid Build Coastguard Worker                                               OffsetFromBase(SlotA()),
2157*c8dee2aaSAndroid Build Coastguard Worker                                               inst.fImmA);
2158*c8dee2aaSAndroid Build Coastguard Worker                 break;
2159*c8dee2aaSAndroid Build Coastguard Worker             }
2160*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_immutable: {
2161*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr;
2162*c8dee2aaSAndroid Build Coastguard Worker                 this->appendCopyImmutableUnmasked(pipeline, alloc, basePtr,
2163*c8dee2aaSAndroid Build Coastguard Worker                                                   OffsetFromBase(dst),
2164*c8dee2aaSAndroid Build Coastguard Worker                                                   OffsetFromBase(ImmutableA()),
2165*c8dee2aaSAndroid Build Coastguard Worker                                                   inst.fImmA);
2166*c8dee2aaSAndroid Build Coastguard Worker                 break;
2167*c8dee2aaSAndroid Build Coastguard Worker             }
2168*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::copy_stack_to_slots_indirect:
2169*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_immutable_indirect:
2170*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_slots_indirect:
2171*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_uniform_indirect: {
2172*c8dee2aaSAndroid Build Coastguard Worker                 // SlotA: fixed-range start
2173*c8dee2aaSAndroid Build Coastguard Worker                 // SlotB: limit-range end
2174*c8dee2aaSAndroid Build Coastguard Worker                 //  immA: number of slots to copy
2175*c8dee2aaSAndroid Build Coastguard Worker                 //  immB: dynamic stack ID
2176*c8dee2aaSAndroid Build Coastguard Worker                 ProgramOp op;
2177*c8dee2aaSAndroid Build Coastguard Worker                 auto* ctx = alloc->make<SkRasterPipeline_CopyIndirectCtx>();
2178*c8dee2aaSAndroid Build Coastguard Worker                 ctx->indirectOffset =
2179*c8dee2aaSAndroid Build Coastguard Worker                         reinterpret_cast<const uint32_t*>(tempStackMap[inst.fImmB]) - (1 * N);
2180*c8dee2aaSAndroid Build Coastguard Worker                 ctx->indirectLimit = inst.fSlotB - inst.fSlotA - inst.fImmA;
2181*c8dee2aaSAndroid Build Coastguard Worker                 ctx->slots = inst.fImmA;
2182*c8dee2aaSAndroid Build Coastguard Worker                 if (inst.fOp == BuilderOp::push_slots_indirect) {
2183*c8dee2aaSAndroid Build Coastguard Worker                     op = ProgramOp::copy_from_indirect_unmasked;
2184*c8dee2aaSAndroid Build Coastguard Worker                     ctx->src = reinterpret_cast<const int32_t*>(SlotA());
2185*c8dee2aaSAndroid Build Coastguard Worker                     ctx->dst = reinterpret_cast<int32_t*>(tempStackPtr);
2186*c8dee2aaSAndroid Build Coastguard Worker                 } else if (inst.fOp == BuilderOp::push_immutable_indirect) {
2187*c8dee2aaSAndroid Build Coastguard Worker                     // We reuse the indirect-uniform op for indirect copies of immutable data.
2188*c8dee2aaSAndroid Build Coastguard Worker                     op = ProgramOp::copy_from_indirect_uniform_unmasked;
2189*c8dee2aaSAndroid Build Coastguard Worker                     ctx->src = reinterpret_cast<const int32_t*>(ImmutableA());
2190*c8dee2aaSAndroid Build Coastguard Worker                     ctx->dst = reinterpret_cast<int32_t*>(tempStackPtr);
2191*c8dee2aaSAndroid Build Coastguard Worker                 } else if (inst.fOp == BuilderOp::push_uniform_indirect) {
2192*c8dee2aaSAndroid Build Coastguard Worker                     op = ProgramOp::copy_from_indirect_uniform_unmasked;
2193*c8dee2aaSAndroid Build Coastguard Worker                     ctx->src = reinterpret_cast<const int32_t*>(UniformA());
2194*c8dee2aaSAndroid Build Coastguard Worker                     ctx->dst = reinterpret_cast<int32_t*>(tempStackPtr);
2195*c8dee2aaSAndroid Build Coastguard Worker                 } else {
2196*c8dee2aaSAndroid Build Coastguard Worker                     op = ProgramOp::copy_to_indirect_masked;
2197*c8dee2aaSAndroid Build Coastguard Worker                     ctx->src = reinterpret_cast<const int32_t*>(tempStackPtr) - (ctx->slots * N);
2198*c8dee2aaSAndroid Build Coastguard Worker                     ctx->dst = reinterpret_cast<int32_t*>(SlotA());
2199*c8dee2aaSAndroid Build Coastguard Worker                 }
2200*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({op, ctx});
2201*c8dee2aaSAndroid Build Coastguard Worker                 break;
2202*c8dee2aaSAndroid Build Coastguard Worker             }
2203*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_uniform:
2204*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::copy_uniform_to_slots_unmasked: {
2205*c8dee2aaSAndroid Build Coastguard Worker                 const float* src = UniformA();
2206*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = (inst.fOp == BuilderOp::push_uniform) ? tempStackPtr : SlotB();
2207*c8dee2aaSAndroid Build Coastguard Worker 
2208*c8dee2aaSAndroid Build Coastguard Worker                 for (int remaining = inst.fImmA; remaining > 0; remaining -= 4) {
2209*c8dee2aaSAndroid Build Coastguard Worker                     auto ctx = alloc->make<SkRasterPipeline_UniformCtx>();
2210*c8dee2aaSAndroid Build Coastguard Worker                     ctx->dst = reinterpret_cast<int32_t*>(dst);
2211*c8dee2aaSAndroid Build Coastguard Worker                     ctx->src = reinterpret_cast<const int32_t*>(src);
2212*c8dee2aaSAndroid Build Coastguard Worker                     switch (remaining) {
2213*c8dee2aaSAndroid Build Coastguard Worker                         case 1:  pipeline->push_back({ProgramOp::copy_uniform,    ctx}); break;
2214*c8dee2aaSAndroid Build Coastguard Worker                         case 2:  pipeline->push_back({ProgramOp::copy_2_uniforms, ctx}); break;
2215*c8dee2aaSAndroid Build Coastguard Worker                         case 3:  pipeline->push_back({ProgramOp::copy_3_uniforms, ctx}); break;
2216*c8dee2aaSAndroid Build Coastguard Worker                         default: pipeline->push_back({ProgramOp::copy_4_uniforms, ctx}); break;
2217*c8dee2aaSAndroid Build Coastguard Worker                     }
2218*c8dee2aaSAndroid Build Coastguard Worker                     dst += 4 * N;
2219*c8dee2aaSAndroid Build Coastguard Worker                     src += 4;
2220*c8dee2aaSAndroid Build Coastguard Worker                 }
2221*c8dee2aaSAndroid Build Coastguard Worker                 break;
2222*c8dee2aaSAndroid Build Coastguard Worker             }
2223*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_condition_mask: {
2224*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr;
2225*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::store_condition_mask, dst});
2226*c8dee2aaSAndroid Build Coastguard Worker                 break;
2227*c8dee2aaSAndroid Build Coastguard Worker             }
2228*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::pop_condition_mask: {
2229*c8dee2aaSAndroid Build Coastguard Worker                 float* src = tempStackPtr - (1 * N);
2230*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::load_condition_mask, src});
2231*c8dee2aaSAndroid Build Coastguard Worker                 break;
2232*c8dee2aaSAndroid Build Coastguard Worker             }
2233*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::merge_condition_mask:
2234*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::merge_inv_condition_mask: {
2235*c8dee2aaSAndroid Build Coastguard Worker                 float* ptr = tempStackPtr - (2 * N);
2236*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({(ProgramOp)inst.fOp, ptr});
2237*c8dee2aaSAndroid Build Coastguard Worker                 break;
2238*c8dee2aaSAndroid Build Coastguard Worker             }
2239*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_loop_mask: {
2240*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr;
2241*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::store_loop_mask, dst});
2242*c8dee2aaSAndroid Build Coastguard Worker                 break;
2243*c8dee2aaSAndroid Build Coastguard Worker             }
2244*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::pop_loop_mask: {
2245*c8dee2aaSAndroid Build Coastguard Worker                 float* src = tempStackPtr - (1 * N);
2246*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::load_loop_mask, src});
2247*c8dee2aaSAndroid Build Coastguard Worker                 break;
2248*c8dee2aaSAndroid Build Coastguard Worker             }
2249*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::pop_and_reenable_loop_mask: {
2250*c8dee2aaSAndroid Build Coastguard Worker                 float* src = tempStackPtr - (1 * N);
2251*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::reenable_loop_mask, src});
2252*c8dee2aaSAndroid Build Coastguard Worker                 break;
2253*c8dee2aaSAndroid Build Coastguard Worker             }
2254*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::reenable_loop_mask:
2255*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::reenable_loop_mask, SlotA()});
2256*c8dee2aaSAndroid Build Coastguard Worker                 break;
2257*c8dee2aaSAndroid Build Coastguard Worker 
2258*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::mask_off_loop_mask:
2259*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::mask_off_loop_mask, nullptr});
2260*c8dee2aaSAndroid Build Coastguard Worker                 break;
2261*c8dee2aaSAndroid Build Coastguard Worker 
2262*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::merge_loop_mask: {
2263*c8dee2aaSAndroid Build Coastguard Worker                 float* src = tempStackPtr - (1 * N);
2264*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::merge_loop_mask, src});
2265*c8dee2aaSAndroid Build Coastguard Worker                 break;
2266*c8dee2aaSAndroid Build Coastguard Worker             }
2267*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_return_mask: {
2268*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr;
2269*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::store_return_mask, dst});
2270*c8dee2aaSAndroid Build Coastguard Worker                 break;
2271*c8dee2aaSAndroid Build Coastguard Worker             }
2272*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::pop_return_mask: {
2273*c8dee2aaSAndroid Build Coastguard Worker                 float* src = tempStackPtr - (1 * N);
2274*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::load_return_mask, src});
2275*c8dee2aaSAndroid Build Coastguard Worker                 break;
2276*c8dee2aaSAndroid Build Coastguard Worker             }
2277*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::mask_off_return_mask:
2278*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::mask_off_return_mask, nullptr});
2279*c8dee2aaSAndroid Build Coastguard Worker                 break;
2280*c8dee2aaSAndroid Build Coastguard Worker 
2281*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::copy_constant:
2282*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_constant: {
2283*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = (inst.fOp == BuilderOp::copy_constant) ? SlotA() : tempStackPtr;
2284*c8dee2aaSAndroid Build Coastguard Worker                 // Splat constant values onto the stack.
2285*c8dee2aaSAndroid Build Coastguard Worker                 for (int remaining = inst.fImmA; remaining > 0; remaining -= 4) {
2286*c8dee2aaSAndroid Build Coastguard Worker                     SkRasterPipeline_ConstantCtx ctx;
2287*c8dee2aaSAndroid Build Coastguard Worker                     ctx.dst = OffsetFromBase(dst);
2288*c8dee2aaSAndroid Build Coastguard Worker                     ctx.value = inst.fImmB;
2289*c8dee2aaSAndroid Build Coastguard Worker                     void* ptr = SkRPCtxUtils::Pack(ctx, alloc);
2290*c8dee2aaSAndroid Build Coastguard Worker                     switch (remaining) {
2291*c8dee2aaSAndroid Build Coastguard Worker                         case 1:  pipeline->push_back({ProgramOp::copy_constant,     ptr}); break;
2292*c8dee2aaSAndroid Build Coastguard Worker                         case 2:  pipeline->push_back({ProgramOp::splat_2_constants, ptr}); break;
2293*c8dee2aaSAndroid Build Coastguard Worker                         case 3:  pipeline->push_back({ProgramOp::splat_3_constants, ptr}); break;
2294*c8dee2aaSAndroid Build Coastguard Worker                         default: pipeline->push_back({ProgramOp::splat_4_constants, ptr}); break;
2295*c8dee2aaSAndroid Build Coastguard Worker                     }
2296*c8dee2aaSAndroid Build Coastguard Worker                     dst += 4 * N;
2297*c8dee2aaSAndroid Build Coastguard Worker                 }
2298*c8dee2aaSAndroid Build Coastguard Worker                 break;
2299*c8dee2aaSAndroid Build Coastguard Worker             }
2300*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::copy_stack_to_slots: {
2301*c8dee2aaSAndroid Build Coastguard Worker                 float* src = tempStackPtr - (inst.fImmB * N);
2302*c8dee2aaSAndroid Build Coastguard Worker                 this->appendCopySlotsMasked(pipeline, alloc,
2303*c8dee2aaSAndroid Build Coastguard Worker                                             OffsetFromBase(SlotA()),
2304*c8dee2aaSAndroid Build Coastguard Worker                                             OffsetFromBase(src),
2305*c8dee2aaSAndroid Build Coastguard Worker                                             inst.fImmA);
2306*c8dee2aaSAndroid Build Coastguard Worker                 break;
2307*c8dee2aaSAndroid Build Coastguard Worker             }
2308*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::copy_stack_to_slots_unmasked: {
2309*c8dee2aaSAndroid Build Coastguard Worker                 float* src = tempStackPtr - (inst.fImmB * N);
2310*c8dee2aaSAndroid Build Coastguard Worker                 this->appendCopySlotsUnmasked(pipeline, alloc,
2311*c8dee2aaSAndroid Build Coastguard Worker                                               OffsetFromBase(SlotA()),
2312*c8dee2aaSAndroid Build Coastguard Worker                                               OffsetFromBase(src),
2313*c8dee2aaSAndroid Build Coastguard Worker                                               inst.fImmA);
2314*c8dee2aaSAndroid Build Coastguard Worker                 break;
2315*c8dee2aaSAndroid Build Coastguard Worker             }
2316*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::swizzle_copy_stack_to_slots: {
2317*c8dee2aaSAndroid Build Coastguard Worker                 // SlotA: fixed-range start
2318*c8dee2aaSAndroid Build Coastguard Worker                 // immA: number of swizzle components
2319*c8dee2aaSAndroid Build Coastguard Worker                 // immB: swizzle components
2320*c8dee2aaSAndroid Build Coastguard Worker                 // immC: offset from stack top
2321*c8dee2aaSAndroid Build Coastguard Worker                 auto stage = (ProgramOp)((int)ProgramOp::swizzle_copy_slot_masked + inst.fImmA - 1);
2322*c8dee2aaSAndroid Build Coastguard Worker                 auto* ctx = alloc->make<SkRasterPipeline_SwizzleCopyCtx>();
2323*c8dee2aaSAndroid Build Coastguard Worker                 ctx->src = reinterpret_cast<const int32_t*>(tempStackPtr) - (inst.fImmC * N);
2324*c8dee2aaSAndroid Build Coastguard Worker                 ctx->dst = reinterpret_cast<int32_t*>(SlotA());
2325*c8dee2aaSAndroid Build Coastguard Worker                 unpack_nybbles_to_offsets(inst.fImmB, SkSpan(ctx->offsets));
2326*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({stage, ctx});
2327*c8dee2aaSAndroid Build Coastguard Worker                 break;
2328*c8dee2aaSAndroid Build Coastguard Worker             }
2329*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_clone: {
2330*c8dee2aaSAndroid Build Coastguard Worker                 float* src = tempStackPtr - (inst.fImmB * N);
2331*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr;
2332*c8dee2aaSAndroid Build Coastguard Worker                 this->appendCopySlotsUnmasked(pipeline, alloc,
2333*c8dee2aaSAndroid Build Coastguard Worker                                               OffsetFromBase(dst),
2334*c8dee2aaSAndroid Build Coastguard Worker                                               OffsetFromBase(src),
2335*c8dee2aaSAndroid Build Coastguard Worker                                               inst.fImmA);
2336*c8dee2aaSAndroid Build Coastguard Worker                 break;
2337*c8dee2aaSAndroid Build Coastguard Worker             }
2338*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_clone_from_stack: {
2339*c8dee2aaSAndroid Build Coastguard Worker                 // immA: number of slots
2340*c8dee2aaSAndroid Build Coastguard Worker                 // immB: other stack ID
2341*c8dee2aaSAndroid Build Coastguard Worker                 // immC: offset from stack top
2342*c8dee2aaSAndroid Build Coastguard Worker                 float* sourceStackPtr = tempStackMap[inst.fImmB];
2343*c8dee2aaSAndroid Build Coastguard Worker                 float* src = sourceStackPtr - (inst.fImmC * N);
2344*c8dee2aaSAndroid Build Coastguard Worker                 float* dst = tempStackPtr;
2345*c8dee2aaSAndroid Build Coastguard Worker                 this->appendCopySlotsUnmasked(pipeline, alloc,
2346*c8dee2aaSAndroid Build Coastguard Worker                                               OffsetFromBase(dst),
2347*c8dee2aaSAndroid Build Coastguard Worker                                               OffsetFromBase(src),
2348*c8dee2aaSAndroid Build Coastguard Worker                                               inst.fImmA);
2349*c8dee2aaSAndroid Build Coastguard Worker                 break;
2350*c8dee2aaSAndroid Build Coastguard Worker             }
2351*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::push_clone_indirect_from_stack: {
2352*c8dee2aaSAndroid Build Coastguard Worker                 // immA: number of slots
2353*c8dee2aaSAndroid Build Coastguard Worker                 // immB: other stack ID
2354*c8dee2aaSAndroid Build Coastguard Worker                 // immC: offset from stack top
2355*c8dee2aaSAndroid Build Coastguard Worker                 // immD: dynamic stack ID
2356*c8dee2aaSAndroid Build Coastguard Worker                 float* sourceStackPtr = tempStackMap[inst.fImmB];
2357*c8dee2aaSAndroid Build Coastguard Worker 
2358*c8dee2aaSAndroid Build Coastguard Worker                 auto* ctx = alloc->make<SkRasterPipeline_CopyIndirectCtx>();
2359*c8dee2aaSAndroid Build Coastguard Worker                 ctx->dst = reinterpret_cast<int32_t*>(tempStackPtr);
2360*c8dee2aaSAndroid Build Coastguard Worker                 ctx->src = reinterpret_cast<const int32_t*>(sourceStackPtr) - (inst.fImmC * N);
2361*c8dee2aaSAndroid Build Coastguard Worker                 ctx->indirectOffset =
2362*c8dee2aaSAndroid Build Coastguard Worker                         reinterpret_cast<const uint32_t*>(tempStackMap[inst.fImmD]) - (1 * N);
2363*c8dee2aaSAndroid Build Coastguard Worker                 ctx->indirectLimit = inst.fImmC - inst.fImmA;
2364*c8dee2aaSAndroid Build Coastguard Worker                 ctx->slots = inst.fImmA;
2365*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::copy_from_indirect_unmasked, ctx});
2366*c8dee2aaSAndroid Build Coastguard Worker                 break;
2367*c8dee2aaSAndroid Build Coastguard Worker             }
2368*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::swizzle_copy_stack_to_slots_indirect: {
2369*c8dee2aaSAndroid Build Coastguard Worker                 // SlotA: fixed-range start
2370*c8dee2aaSAndroid Build Coastguard Worker                 // SlotB: limit-range end
2371*c8dee2aaSAndroid Build Coastguard Worker                 // immA: number of swizzle components
2372*c8dee2aaSAndroid Build Coastguard Worker                 // immB: swizzle components
2373*c8dee2aaSAndroid Build Coastguard Worker                 // immC: offset from stack top
2374*c8dee2aaSAndroid Build Coastguard Worker                 // immD: dynamic stack ID
2375*c8dee2aaSAndroid Build Coastguard Worker                 auto* ctx = alloc->make<SkRasterPipeline_SwizzleCopyIndirectCtx>();
2376*c8dee2aaSAndroid Build Coastguard Worker                 ctx->src = reinterpret_cast<const int32_t*>(tempStackPtr) - (inst.fImmC * N);
2377*c8dee2aaSAndroid Build Coastguard Worker                 ctx->dst = reinterpret_cast<int32_t*>(SlotA());
2378*c8dee2aaSAndroid Build Coastguard Worker                 ctx->indirectOffset =
2379*c8dee2aaSAndroid Build Coastguard Worker                         reinterpret_cast<const uint32_t*>(tempStackMap[inst.fImmD]) - (1 * N);
2380*c8dee2aaSAndroid Build Coastguard Worker                 ctx->indirectLimit =
2381*c8dee2aaSAndroid Build Coastguard Worker                         inst.fSlotB - inst.fSlotA - (max_packed_nybble(inst.fImmB, inst.fImmA) + 1);
2382*c8dee2aaSAndroid Build Coastguard Worker                 ctx->slots = inst.fImmA;
2383*c8dee2aaSAndroid Build Coastguard Worker                 unpack_nybbles_to_offsets(inst.fImmB, SkSpan(ctx->offsets));
2384*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::swizzle_copy_to_indirect_masked, ctx});
2385*c8dee2aaSAndroid Build Coastguard Worker                 break;
2386*c8dee2aaSAndroid Build Coastguard Worker             }
2387*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::case_op: {
2388*c8dee2aaSAndroid Build Coastguard Worker                 SkRasterPipeline_CaseOpCtx ctx;
2389*c8dee2aaSAndroid Build Coastguard Worker                 ctx.expectedValue = inst.fImmA;
2390*c8dee2aaSAndroid Build Coastguard Worker                 ctx.offset = OffsetFromBase(tempStackPtr - (2 * N));
2391*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::case_op, SkRPCtxUtils::Pack(ctx, alloc)});
2392*c8dee2aaSAndroid Build Coastguard Worker                 break;
2393*c8dee2aaSAndroid Build Coastguard Worker             }
2394*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::continue_op:
2395*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::continue_op, tempStackMap[inst.fImmA] - (1 * N)});
2396*c8dee2aaSAndroid Build Coastguard Worker                 break;
2397*c8dee2aaSAndroid Build Coastguard Worker 
2398*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::pad_stack:
2399*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::discard_stack:
2400*c8dee2aaSAndroid Build Coastguard Worker                 break;
2401*c8dee2aaSAndroid Build Coastguard Worker 
2402*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::invoke_shader:
2403*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::invoke_color_filter:
2404*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::invoke_blender:
2405*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({(ProgramOp)inst.fOp, context_bit_pun(inst.fImmA)});
2406*c8dee2aaSAndroid Build Coastguard Worker                 mostRecentInvocationInstructionIdx = instructionIdx;
2407*c8dee2aaSAndroid Build Coastguard Worker                 break;
2408*c8dee2aaSAndroid Build Coastguard Worker 
2409*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::invoke_to_linear_srgb:
2410*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::invoke_from_linear_srgb:
2411*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({(ProgramOp)inst.fOp, tempStackMap[inst.fImmA] - (4 * N)});
2412*c8dee2aaSAndroid Build Coastguard Worker                 mostRecentInvocationInstructionIdx = instructionIdx;
2413*c8dee2aaSAndroid Build Coastguard Worker                 break;
2414*c8dee2aaSAndroid Build Coastguard Worker 
2415*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::trace_line: {
2416*c8dee2aaSAndroid Build Coastguard Worker                 auto* ctx = AllocTraceContext((SkRasterPipeline_TraceLineCtx*)nullptr);
2417*c8dee2aaSAndroid Build Coastguard Worker                 ctx->lineNumber = inst.fImmB;
2418*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::trace_line, ctx});
2419*c8dee2aaSAndroid Build Coastguard Worker                 break;
2420*c8dee2aaSAndroid Build Coastguard Worker             }
2421*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::trace_scope: {
2422*c8dee2aaSAndroid Build Coastguard Worker                 auto* ctx = AllocTraceContext((SkRasterPipeline_TraceScopeCtx*)nullptr);
2423*c8dee2aaSAndroid Build Coastguard Worker                 ctx->delta = inst.fImmB;
2424*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::trace_scope, ctx});
2425*c8dee2aaSAndroid Build Coastguard Worker                 break;
2426*c8dee2aaSAndroid Build Coastguard Worker             }
2427*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::trace_enter:
2428*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::trace_exit: {
2429*c8dee2aaSAndroid Build Coastguard Worker                 auto* ctx = AllocTraceContext((SkRasterPipeline_TraceFuncCtx*)nullptr);
2430*c8dee2aaSAndroid Build Coastguard Worker                 ctx->funcIdx = inst.fImmB;
2431*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({(ProgramOp)inst.fOp, ctx});
2432*c8dee2aaSAndroid Build Coastguard Worker                 break;
2433*c8dee2aaSAndroid Build Coastguard Worker             }
2434*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::trace_var:
2435*c8dee2aaSAndroid Build Coastguard Worker             case BuilderOp::trace_var_indirect: {
2436*c8dee2aaSAndroid Build Coastguard Worker                 // SlotA: fixed-range start
2437*c8dee2aaSAndroid Build Coastguard Worker                 // SlotB: limit-range end
2438*c8dee2aaSAndroid Build Coastguard Worker                 // immA: trace-mask stack ID
2439*c8dee2aaSAndroid Build Coastguard Worker                 // immB: number of slots
2440*c8dee2aaSAndroid Build Coastguard Worker                 // immC: dynamic stack ID
2441*c8dee2aaSAndroid Build Coastguard Worker                 auto* ctx = AllocTraceContext((SkRasterPipeline_TraceVarCtx*)nullptr);
2442*c8dee2aaSAndroid Build Coastguard Worker                 ctx->slotIdx = inst.fSlotA;
2443*c8dee2aaSAndroid Build Coastguard Worker                 ctx->numSlots = inst.fImmB;
2444*c8dee2aaSAndroid Build Coastguard Worker                 ctx->data = reinterpret_cast<int*>(SlotA());
2445*c8dee2aaSAndroid Build Coastguard Worker                 if (inst.fOp == BuilderOp::trace_var_indirect) {
2446*c8dee2aaSAndroid Build Coastguard Worker                     ctx->indirectOffset =
2447*c8dee2aaSAndroid Build Coastguard Worker                             reinterpret_cast<const uint32_t*>(tempStackMap[inst.fImmC]) - (1 * N);
2448*c8dee2aaSAndroid Build Coastguard Worker                     ctx->indirectLimit = inst.fSlotB - inst.fSlotA - inst.fImmB;
2449*c8dee2aaSAndroid Build Coastguard Worker                 } else {
2450*c8dee2aaSAndroid Build Coastguard Worker                     ctx->indirectOffset = nullptr;
2451*c8dee2aaSAndroid Build Coastguard Worker                     ctx->indirectLimit = 0;
2452*c8dee2aaSAndroid Build Coastguard Worker                 }
2453*c8dee2aaSAndroid Build Coastguard Worker                 pipeline->push_back({ProgramOp::trace_var, ctx});
2454*c8dee2aaSAndroid Build Coastguard Worker                 break;
2455*c8dee2aaSAndroid Build Coastguard Worker             }
2456*c8dee2aaSAndroid Build Coastguard Worker             default:
2457*c8dee2aaSAndroid Build Coastguard Worker                 SkDEBUGFAILF("Raster Pipeline: unsupported instruction %d", (int)inst.fOp);
2458*c8dee2aaSAndroid Build Coastguard Worker                 break;
2459*c8dee2aaSAndroid Build Coastguard Worker         }
2460*c8dee2aaSAndroid Build Coastguard Worker 
2461*c8dee2aaSAndroid Build Coastguard Worker         int stackUsage = stack_usage(inst);
2462*c8dee2aaSAndroid Build Coastguard Worker         if (stackUsage != 0) {
2463*c8dee2aaSAndroid Build Coastguard Worker             tempStackPtr += stackUsage * N;
2464*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(tempStackPtr >= slots.stack.begin());
2465*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(tempStackPtr <= slots.stack.end());
2466*c8dee2aaSAndroid Build Coastguard Worker         }
2467*c8dee2aaSAndroid Build Coastguard Worker 
2468*c8dee2aaSAndroid Build Coastguard Worker         // Periodically rewind the stack every 500 instructions. When SK_HAS_MUSTTAIL is set,
2469*c8dee2aaSAndroid Build Coastguard Worker         // rewinds are not actually used; the appendStackRewind call becomes a no-op. On platforms
2470*c8dee2aaSAndroid Build Coastguard Worker         // that don't support SK_HAS_MUSTTAIL, rewinding the stack periodically can prevent a
2471*c8dee2aaSAndroid Build Coastguard Worker         // potential stack overflow when running a long program.
2472*c8dee2aaSAndroid Build Coastguard Worker         int numPipelineStages = pipeline->size();
2473*c8dee2aaSAndroid Build Coastguard Worker         if (numPipelineStages - mostRecentRewind > 500) {
2474*c8dee2aaSAndroid Build Coastguard Worker             this->appendStackRewindForNonTailcallers(pipeline);
2475*c8dee2aaSAndroid Build Coastguard Worker             mostRecentRewind = numPipelineStages;
2476*c8dee2aaSAndroid Build Coastguard Worker         }
2477*c8dee2aaSAndroid Build Coastguard Worker     }
2478*c8dee2aaSAndroid Build Coastguard Worker }
2479*c8dee2aaSAndroid Build Coastguard Worker 
2480*c8dee2aaSAndroid Build Coastguard Worker class Program::Dumper {
2481*c8dee2aaSAndroid Build Coastguard Worker public:
Dumper(const Program & p)2482*c8dee2aaSAndroid Build Coastguard Worker     Dumper(const Program& p) : fProgram(p) {}
2483*c8dee2aaSAndroid Build Coastguard Worker 
2484*c8dee2aaSAndroid Build Coastguard Worker     void dump(SkWStream* out, bool writeInstructionCount);
2485*c8dee2aaSAndroid Build Coastguard Worker 
2486*c8dee2aaSAndroid Build Coastguard Worker     // Finds the labels in the program, and keeps track of their offsets.
buildLabelToStageMap()2487*c8dee2aaSAndroid Build Coastguard Worker     void buildLabelToStageMap() {
2488*c8dee2aaSAndroid Build Coastguard Worker         for (int index = 0; index < fStages.size(); ++index) {
2489*c8dee2aaSAndroid Build Coastguard Worker             if (fStages[index].op == ProgramOp::label) {
2490*c8dee2aaSAndroid Build Coastguard Worker                 int labelID = sk_bit_cast<intptr_t>(fStages[index].ctx);
2491*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(!fLabelToStageMap.find(labelID));
2492*c8dee2aaSAndroid Build Coastguard Worker                 fLabelToStageMap[labelID] = index;
2493*c8dee2aaSAndroid Build Coastguard Worker             }
2494*c8dee2aaSAndroid Build Coastguard Worker         }
2495*c8dee2aaSAndroid Build Coastguard Worker     }
2496*c8dee2aaSAndroid Build Coastguard Worker 
2497*c8dee2aaSAndroid Build Coastguard Worker     // Assign unique names to each variable slot; our trace might have multiple variables with the
2498*c8dee2aaSAndroid Build Coastguard Worker     // same name, which can make a dump hard to read. We disambiguate them with subscripts.
buildUniqueSlotNameList()2499*c8dee2aaSAndroid Build Coastguard Worker     void buildUniqueSlotNameList() {
2500*c8dee2aaSAndroid Build Coastguard Worker         if (fProgram.fDebugTrace) {
2501*c8dee2aaSAndroid Build Coastguard Worker             fSlotNameList.reserve_exact(fProgram.fDebugTrace->fSlotInfo.size());
2502*c8dee2aaSAndroid Build Coastguard Worker 
2503*c8dee2aaSAndroid Build Coastguard Worker             // The map consists of <variable name, <source position, unique name>>.
2504*c8dee2aaSAndroid Build Coastguard Worker             THashMap<std::string_view, THashMap<int, std::string>> uniqueNameMap;
2505*c8dee2aaSAndroid Build Coastguard Worker 
2506*c8dee2aaSAndroid Build Coastguard Worker             for (const SlotDebugInfo& slotInfo : fProgram.fDebugTrace->fSlotInfo) {
2507*c8dee2aaSAndroid Build Coastguard Worker                 // Look up this variable by its name and source position.
2508*c8dee2aaSAndroid Build Coastguard Worker                 int pos = slotInfo.pos.valid() ? slotInfo.pos.startOffset() : 0;
2509*c8dee2aaSAndroid Build Coastguard Worker                 THashMap<int, std::string>& positionMap = uniqueNameMap[slotInfo.name];
2510*c8dee2aaSAndroid Build Coastguard Worker                 std::string& uniqueName = positionMap[pos];
2511*c8dee2aaSAndroid Build Coastguard Worker 
2512*c8dee2aaSAndroid Build Coastguard Worker                 // Have we seen this variable name/position combination before?
2513*c8dee2aaSAndroid Build Coastguard Worker                 if (uniqueName.empty()) {
2514*c8dee2aaSAndroid Build Coastguard Worker                     // This is a unique name/position pair.
2515*c8dee2aaSAndroid Build Coastguard Worker                     uniqueName = slotInfo.name;
2516*c8dee2aaSAndroid Build Coastguard Worker 
2517*c8dee2aaSAndroid Build Coastguard Worker                     // But if it's not a unique _name_, it deserves a subscript to disambiguate it.
2518*c8dee2aaSAndroid Build Coastguard Worker                     int subscript = positionMap.count() - 1;
2519*c8dee2aaSAndroid Build Coastguard Worker                     if (subscript > 0) {
2520*c8dee2aaSAndroid Build Coastguard Worker                         for (char digit : std::to_string(subscript)) {
2521*c8dee2aaSAndroid Build Coastguard Worker                             // U+2080 through U+2089 (₀₁₂₃₄₅₆₇₈₉) in UTF8:
2522*c8dee2aaSAndroid Build Coastguard Worker                             uniqueName.push_back((char)0xE2);
2523*c8dee2aaSAndroid Build Coastguard Worker                             uniqueName.push_back((char)0x82);
2524*c8dee2aaSAndroid Build Coastguard Worker                             uniqueName.push_back((char)(0x80 + digit - '0'));
2525*c8dee2aaSAndroid Build Coastguard Worker                         }
2526*c8dee2aaSAndroid Build Coastguard Worker                     }
2527*c8dee2aaSAndroid Build Coastguard Worker                 }
2528*c8dee2aaSAndroid Build Coastguard Worker 
2529*c8dee2aaSAndroid Build Coastguard Worker                 fSlotNameList.push_back(uniqueName);
2530*c8dee2aaSAndroid Build Coastguard Worker             }
2531*c8dee2aaSAndroid Build Coastguard Worker         }
2532*c8dee2aaSAndroid Build Coastguard Worker     }
2533*c8dee2aaSAndroid Build Coastguard Worker 
2534*c8dee2aaSAndroid Build Coastguard Worker     // Interprets the context value as a branch offset.
branchOffset(const SkRasterPipeline_BranchCtx * ctx,int index) const2535*c8dee2aaSAndroid Build Coastguard Worker     std::string branchOffset(const SkRasterPipeline_BranchCtx* ctx, int index) const {
2536*c8dee2aaSAndroid Build Coastguard Worker         // The context's offset field contains a label ID
2537*c8dee2aaSAndroid Build Coastguard Worker         int labelID = ctx->offset;
2538*c8dee2aaSAndroid Build Coastguard Worker         const int* targetIndex = fLabelToStageMap.find(labelID);
2539*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(targetIndex);
2540*c8dee2aaSAndroid Build Coastguard Worker         return SkSL::String::printf("%+d (label %d at #%d)", *targetIndex - index, labelID,
2541*c8dee2aaSAndroid Build Coastguard Worker                                                              *targetIndex + 1);
2542*c8dee2aaSAndroid Build Coastguard Worker     }
2543*c8dee2aaSAndroid Build Coastguard Worker 
2544*c8dee2aaSAndroid Build Coastguard Worker     // Prints a 32-bit immediate value of unknown type (int/float).
imm(float immFloat,bool showAsFloat=true) const2545*c8dee2aaSAndroid Build Coastguard Worker     std::string imm(float immFloat, bool showAsFloat = true) const {
2546*c8dee2aaSAndroid Build Coastguard Worker         // Special case exact zero as "0" for readability (vs `0x00000000 (0.0)`).
2547*c8dee2aaSAndroid Build Coastguard Worker         if (sk_bit_cast<int32_t>(immFloat) == 0) {
2548*c8dee2aaSAndroid Build Coastguard Worker             return "0";
2549*c8dee2aaSAndroid Build Coastguard Worker         }
2550*c8dee2aaSAndroid Build Coastguard Worker         // Start with `0x3F800000` as a baseline.
2551*c8dee2aaSAndroid Build Coastguard Worker         uint32_t immUnsigned;
2552*c8dee2aaSAndroid Build Coastguard Worker         memcpy(&immUnsigned, &immFloat, sizeof(uint32_t));
2553*c8dee2aaSAndroid Build Coastguard Worker         auto text = SkSL::String::printf("0x%08X", immUnsigned);
2554*c8dee2aaSAndroid Build Coastguard Worker 
2555*c8dee2aaSAndroid Build Coastguard Worker         // Extend it to `0x3F800000 (1.0)` for finite floating point values.
2556*c8dee2aaSAndroid Build Coastguard Worker         if (showAsFloat && std::isfinite(immFloat)) {
2557*c8dee2aaSAndroid Build Coastguard Worker             text += " (";
2558*c8dee2aaSAndroid Build Coastguard Worker             text += skstd::to_string(immFloat);
2559*c8dee2aaSAndroid Build Coastguard Worker             text += ')';
2560*c8dee2aaSAndroid Build Coastguard Worker         }
2561*c8dee2aaSAndroid Build Coastguard Worker         return text;
2562*c8dee2aaSAndroid Build Coastguard Worker     }
2563*c8dee2aaSAndroid Build Coastguard Worker 
2564*c8dee2aaSAndroid Build Coastguard Worker     // Interprets the context pointer as a 32-bit immediate value of unknown type (int/float).
immCtx(const void * ctx,bool showAsFloat=true) const2565*c8dee2aaSAndroid Build Coastguard Worker     std::string immCtx(const void* ctx, bool showAsFloat = true) const {
2566*c8dee2aaSAndroid Build Coastguard Worker         float f;
2567*c8dee2aaSAndroid Build Coastguard Worker         memcpy(&f, &ctx, sizeof(float));
2568*c8dee2aaSAndroid Build Coastguard Worker         return this->imm(f, showAsFloat);
2569*c8dee2aaSAndroid Build Coastguard Worker     }
2570*c8dee2aaSAndroid Build Coastguard Worker 
2571*c8dee2aaSAndroid Build Coastguard Worker     // Prints `1` for single slots and `1..3` for ranges of slots.
asRange(int first,int count) const2572*c8dee2aaSAndroid Build Coastguard Worker     std::string asRange(int first, int count) const {
2573*c8dee2aaSAndroid Build Coastguard Worker         std::string text = std::to_string(first);
2574*c8dee2aaSAndroid Build Coastguard Worker         if (count > 1) {
2575*c8dee2aaSAndroid Build Coastguard Worker             text += ".." + std::to_string(first + count - 1);
2576*c8dee2aaSAndroid Build Coastguard Worker         }
2577*c8dee2aaSAndroid Build Coastguard Worker         return text;
2578*c8dee2aaSAndroid Build Coastguard Worker     }
2579*c8dee2aaSAndroid Build Coastguard Worker 
2580*c8dee2aaSAndroid Build Coastguard Worker     // Generates a reasonable name for a range of slots or uniforms, e.g.:
2581*c8dee2aaSAndroid Build Coastguard Worker     // `val`: slot range points at one variable, named val
2582*c8dee2aaSAndroid Build Coastguard Worker     // `val(0..1)`: slot range points at the first and second slot of val (which has 3+ slots)
2583*c8dee2aaSAndroid Build Coastguard Worker     // `foo, bar`: slot range fully covers two variables, named foo and bar
2584*c8dee2aaSAndroid Build Coastguard Worker     // `foo(3), bar(0)`: slot range covers the fourth slot of foo and the first slot of bar
slotOrUniformName(SkSpan<const SlotDebugInfo> debugInfo,SkSpan<const std::string> names,SlotRange range) const2585*c8dee2aaSAndroid Build Coastguard Worker     std::string slotOrUniformName(SkSpan<const SlotDebugInfo> debugInfo,
2586*c8dee2aaSAndroid Build Coastguard Worker                                   SkSpan<const std::string> names,
2587*c8dee2aaSAndroid Build Coastguard Worker                                   SlotRange range) const {
2588*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(range.index >= 0 && (range.index + range.count) <= (int)debugInfo.size());
2589*c8dee2aaSAndroid Build Coastguard Worker 
2590*c8dee2aaSAndroid Build Coastguard Worker         std::string text;
2591*c8dee2aaSAndroid Build Coastguard Worker         auto separator = SkSL::String::Separator();
2592*c8dee2aaSAndroid Build Coastguard Worker         while (range.count > 0) {
2593*c8dee2aaSAndroid Build Coastguard Worker             const SlotDebugInfo& slotInfo = debugInfo[range.index];
2594*c8dee2aaSAndroid Build Coastguard Worker             text += separator();
2595*c8dee2aaSAndroid Build Coastguard Worker             text += names.empty() ? slotInfo.name : names[range.index];
2596*c8dee2aaSAndroid Build Coastguard Worker 
2597*c8dee2aaSAndroid Build Coastguard Worker             // Figure out how many slots we can chomp in this iteration.
2598*c8dee2aaSAndroid Build Coastguard Worker             int entireVariable = slotInfo.columns * slotInfo.rows;
2599*c8dee2aaSAndroid Build Coastguard Worker             int slotsToChomp = std::min(range.count, entireVariable - slotInfo.componentIndex);
2600*c8dee2aaSAndroid Build Coastguard Worker             // If we aren't consuming an entire variable, from first slot to last...
2601*c8dee2aaSAndroid Build Coastguard Worker             if (slotsToChomp != entireVariable) {
2602*c8dee2aaSAndroid Build Coastguard Worker                 // ... decorate it with a range suffix.
2603*c8dee2aaSAndroid Build Coastguard Worker                 text += '(' + this->asRange(slotInfo.componentIndex, slotsToChomp) + ')';
2604*c8dee2aaSAndroid Build Coastguard Worker             }
2605*c8dee2aaSAndroid Build Coastguard Worker             range.index += slotsToChomp;
2606*c8dee2aaSAndroid Build Coastguard Worker             range.count -= slotsToChomp;
2607*c8dee2aaSAndroid Build Coastguard Worker         }
2608*c8dee2aaSAndroid Build Coastguard Worker 
2609*c8dee2aaSAndroid Build Coastguard Worker         return text;
2610*c8dee2aaSAndroid Build Coastguard Worker     }
2611*c8dee2aaSAndroid Build Coastguard Worker 
2612*c8dee2aaSAndroid Build Coastguard Worker     // Generates a reasonable name for a range of slots.
slotName(SlotRange range) const2613*c8dee2aaSAndroid Build Coastguard Worker     std::string slotName(SlotRange range) const {
2614*c8dee2aaSAndroid Build Coastguard Worker         return this->slotOrUniformName(fProgram.fDebugTrace->fSlotInfo, fSlotNameList, range);
2615*c8dee2aaSAndroid Build Coastguard Worker     }
2616*c8dee2aaSAndroid Build Coastguard Worker 
2617*c8dee2aaSAndroid Build Coastguard Worker     // Generates a reasonable name for a range of uniforms.
uniformName(SlotRange range) const2618*c8dee2aaSAndroid Build Coastguard Worker     std::string uniformName(SlotRange range) const {
2619*c8dee2aaSAndroid Build Coastguard Worker         return this->slotOrUniformName(fProgram.fDebugTrace->fUniformInfo, /*names=*/{}, range);
2620*c8dee2aaSAndroid Build Coastguard Worker     }
2621*c8dee2aaSAndroid Build Coastguard Worker 
2622*c8dee2aaSAndroid Build Coastguard Worker     // Attempts to interpret the passed-in pointer as a uniform range.
uniformPtrCtx(const float * ptr,int numSlots) const2623*c8dee2aaSAndroid Build Coastguard Worker     std::string uniformPtrCtx(const float* ptr, int numSlots) const {
2624*c8dee2aaSAndroid Build Coastguard Worker         const float* end = ptr + numSlots;
2625*c8dee2aaSAndroid Build Coastguard Worker         if (ptr >= fUniforms.begin() && end <= fUniforms.end()) {
2626*c8dee2aaSAndroid Build Coastguard Worker             int uniformIdx = ptr - fUniforms.begin();
2627*c8dee2aaSAndroid Build Coastguard Worker             if (fProgram.fDebugTrace) {
2628*c8dee2aaSAndroid Build Coastguard Worker                 // Handle pointers to named uniform slots.
2629*c8dee2aaSAndroid Build Coastguard Worker                 std::string name = this->uniformName({uniformIdx, numSlots});
2630*c8dee2aaSAndroid Build Coastguard Worker                 if (!name.empty()) {
2631*c8dee2aaSAndroid Build Coastguard Worker                     return name;
2632*c8dee2aaSAndroid Build Coastguard Worker                 }
2633*c8dee2aaSAndroid Build Coastguard Worker             }
2634*c8dee2aaSAndroid Build Coastguard Worker             // Handle pointers to uniforms (when no debug info exists).
2635*c8dee2aaSAndroid Build Coastguard Worker             return 'u' + this->asRange(uniformIdx, numSlots);
2636*c8dee2aaSAndroid Build Coastguard Worker         }
2637*c8dee2aaSAndroid Build Coastguard Worker         return {};
2638*c8dee2aaSAndroid Build Coastguard Worker     }
2639*c8dee2aaSAndroid Build Coastguard Worker 
2640*c8dee2aaSAndroid Build Coastguard Worker     // Attempts to interpret the passed-in pointer as a value slot range.
valuePtrCtx(const float * ptr,int numSlots) const2641*c8dee2aaSAndroid Build Coastguard Worker     std::string valuePtrCtx(const float* ptr, int numSlots) const {
2642*c8dee2aaSAndroid Build Coastguard Worker         const float* end = ptr + (N * numSlots);
2643*c8dee2aaSAndroid Build Coastguard Worker         if (ptr >= fSlots.values.begin() && end <= fSlots.values.end()) {
2644*c8dee2aaSAndroid Build Coastguard Worker             int valueIdx = ptr - fSlots.values.begin();
2645*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT((valueIdx % N) == 0);
2646*c8dee2aaSAndroid Build Coastguard Worker             valueIdx /= N;
2647*c8dee2aaSAndroid Build Coastguard Worker             if (fProgram.fDebugTrace) {
2648*c8dee2aaSAndroid Build Coastguard Worker                 // Handle pointers to named value slots.
2649*c8dee2aaSAndroid Build Coastguard Worker                 std::string name = this->slotName({valueIdx, numSlots});
2650*c8dee2aaSAndroid Build Coastguard Worker                 if (!name.empty()) {
2651*c8dee2aaSAndroid Build Coastguard Worker                     return name;
2652*c8dee2aaSAndroid Build Coastguard Worker                 }
2653*c8dee2aaSAndroid Build Coastguard Worker             }
2654*c8dee2aaSAndroid Build Coastguard Worker             // Handle pointers to value slots (when no debug info exists).
2655*c8dee2aaSAndroid Build Coastguard Worker             return 'v' + this->asRange(valueIdx, numSlots);
2656*c8dee2aaSAndroid Build Coastguard Worker         }
2657*c8dee2aaSAndroid Build Coastguard Worker         return {};
2658*c8dee2aaSAndroid Build Coastguard Worker     }
2659*c8dee2aaSAndroid Build Coastguard Worker 
2660*c8dee2aaSAndroid Build Coastguard Worker     // Attempts to interpret the passed-in pointer as a immutable slot range.
immutablePtrCtx(const float * ptr,int numSlots) const2661*c8dee2aaSAndroid Build Coastguard Worker     std::string immutablePtrCtx(const float* ptr, int numSlots) const {
2662*c8dee2aaSAndroid Build Coastguard Worker         const float* end = ptr + numSlots;
2663*c8dee2aaSAndroid Build Coastguard Worker         if (ptr >= fSlots.immutable.begin() && end <= fSlots.immutable.end()) {
2664*c8dee2aaSAndroid Build Coastguard Worker             int index = ptr - fSlots.immutable.begin();
2665*c8dee2aaSAndroid Build Coastguard Worker             return 'i' + this->asRange(index, numSlots) + ' ' +
2666*c8dee2aaSAndroid Build Coastguard Worker                    this->multiImmCtx(ptr, numSlots);
2667*c8dee2aaSAndroid Build Coastguard Worker         }
2668*c8dee2aaSAndroid Build Coastguard Worker         return {};
2669*c8dee2aaSAndroid Build Coastguard Worker     }
2670*c8dee2aaSAndroid Build Coastguard Worker 
2671*c8dee2aaSAndroid Build Coastguard Worker     // Interprets the context value as a pointer to `count` immediate values.
multiImmCtx(const float * ptr,int count) const2672*c8dee2aaSAndroid Build Coastguard Worker     std::string multiImmCtx(const float* ptr, int count) const {
2673*c8dee2aaSAndroid Build Coastguard Worker         // If this is a uniform, print it by name.
2674*c8dee2aaSAndroid Build Coastguard Worker         if (std::string text = this->uniformPtrCtx(ptr, count); !text.empty()) {
2675*c8dee2aaSAndroid Build Coastguard Worker             return text;
2676*c8dee2aaSAndroid Build Coastguard Worker         }
2677*c8dee2aaSAndroid Build Coastguard Worker         // Emit a single bracketed immediate.
2678*c8dee2aaSAndroid Build Coastguard Worker         if (count == 1) {
2679*c8dee2aaSAndroid Build Coastguard Worker             return '[' + this->imm(*ptr) + ']';
2680*c8dee2aaSAndroid Build Coastguard Worker         }
2681*c8dee2aaSAndroid Build Coastguard Worker         // Emit a list like `[0x00000000 (0.0), 0x3F80000 (1.0)]`.
2682*c8dee2aaSAndroid Build Coastguard Worker         std::string text = "[";
2683*c8dee2aaSAndroid Build Coastguard Worker         auto separator = SkSL::String::Separator();
2684*c8dee2aaSAndroid Build Coastguard Worker         while (count--) {
2685*c8dee2aaSAndroid Build Coastguard Worker             text += separator();
2686*c8dee2aaSAndroid Build Coastguard Worker             text += this->imm(*ptr++);
2687*c8dee2aaSAndroid Build Coastguard Worker         }
2688*c8dee2aaSAndroid Build Coastguard Worker         return text + ']';
2689*c8dee2aaSAndroid Build Coastguard Worker     }
2690*c8dee2aaSAndroid Build Coastguard Worker 
2691*c8dee2aaSAndroid Build Coastguard Worker     // Interprets the context value as a generic pointer.
ptrCtx(const void * ctx,int numSlots) const2692*c8dee2aaSAndroid Build Coastguard Worker     std::string ptrCtx(const void* ctx, int numSlots) const {
2693*c8dee2aaSAndroid Build Coastguard Worker         const float *ctxAsSlot = static_cast<const float*>(ctx);
2694*c8dee2aaSAndroid Build Coastguard Worker         // Check for uniform, value, and immutable pointers.
2695*c8dee2aaSAndroid Build Coastguard Worker         if (std::string uniform = this->uniformPtrCtx(ctxAsSlot, numSlots); !uniform.empty()) {
2696*c8dee2aaSAndroid Build Coastguard Worker             return uniform;
2697*c8dee2aaSAndroid Build Coastguard Worker         }
2698*c8dee2aaSAndroid Build Coastguard Worker         if (std::string value = this->valuePtrCtx(ctxAsSlot, numSlots); !value.empty()) {
2699*c8dee2aaSAndroid Build Coastguard Worker             return value;
2700*c8dee2aaSAndroid Build Coastguard Worker         }
2701*c8dee2aaSAndroid Build Coastguard Worker         if (std::string value = this->immutablePtrCtx(ctxAsSlot, numSlots); !value.empty()) {
2702*c8dee2aaSAndroid Build Coastguard Worker             return value;
2703*c8dee2aaSAndroid Build Coastguard Worker         }
2704*c8dee2aaSAndroid Build Coastguard Worker         // Handle pointers to temporary stack slots.
2705*c8dee2aaSAndroid Build Coastguard Worker         if (ctxAsSlot >= fSlots.stack.begin() && ctxAsSlot < fSlots.stack.end()) {
2706*c8dee2aaSAndroid Build Coastguard Worker             int stackIdx = ctxAsSlot - fSlots.stack.begin();
2707*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT((stackIdx % N) == 0);
2708*c8dee2aaSAndroid Build Coastguard Worker             return '$' + this->asRange(stackIdx / N, numSlots);
2709*c8dee2aaSAndroid Build Coastguard Worker         }
2710*c8dee2aaSAndroid Build Coastguard Worker         // This pointer is out of our expected bounds; this generally isn't expected to happen.
2711*c8dee2aaSAndroid Build Coastguard Worker         return "ExternalPtr(" + this->asRange(0, numSlots) + ")";
2712*c8dee2aaSAndroid Build Coastguard Worker     }
2713*c8dee2aaSAndroid Build Coastguard Worker 
2714*c8dee2aaSAndroid Build Coastguard Worker     // Converts an SkRPOffset to a pointer into the value-slot range.
offsetToPtr(SkRPOffset offset) const2715*c8dee2aaSAndroid Build Coastguard Worker     std::byte* offsetToPtr(SkRPOffset offset) const {
2716*c8dee2aaSAndroid Build Coastguard Worker         return (std::byte*)fSlots.values.data() + offset;
2717*c8dee2aaSAndroid Build Coastguard Worker     }
2718*c8dee2aaSAndroid Build Coastguard Worker 
2719*c8dee2aaSAndroid Build Coastguard Worker     // Interprets a slab offset as a slot range.
offsetCtx(SkRPOffset offset,int numSlots) const2720*c8dee2aaSAndroid Build Coastguard Worker     std::string offsetCtx(SkRPOffset offset, int numSlots) const {
2721*c8dee2aaSAndroid Build Coastguard Worker         return this->ptrCtx(this->offsetToPtr(offset), numSlots);
2722*c8dee2aaSAndroid Build Coastguard Worker     }
2723*c8dee2aaSAndroid Build Coastguard Worker 
2724*c8dee2aaSAndroid Build Coastguard Worker     // Interprets the context value as a packed ConstantCtx structure.
constantCtx(const void * v,int slots,bool showAsFloat=true) const2725*c8dee2aaSAndroid Build Coastguard Worker     std::tuple<std::string, std::string> constantCtx(const void* v,
2726*c8dee2aaSAndroid Build Coastguard Worker                                                      int slots,
2727*c8dee2aaSAndroid Build Coastguard Worker                                                      bool showAsFloat = true) const {
2728*c8dee2aaSAndroid Build Coastguard Worker         auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipeline_ConstantCtx*)v);
2729*c8dee2aaSAndroid Build Coastguard Worker         return {this->offsetCtx(ctx.dst, slots),
2730*c8dee2aaSAndroid Build Coastguard Worker                 this->imm(sk_bit_cast<float>(ctx.value), showAsFloat)};
2731*c8dee2aaSAndroid Build Coastguard Worker     }
2732*c8dee2aaSAndroid Build Coastguard Worker 
2733*c8dee2aaSAndroid Build Coastguard Worker     // Interprets the context value as a BinaryOp structure for copy_n_slots (numSlots is dictated
2734*c8dee2aaSAndroid Build Coastguard Worker     // by the op itself).
binaryOpCtx(const void * v,int numSlots) const2735*c8dee2aaSAndroid Build Coastguard Worker     std::tuple<std::string, std::string> binaryOpCtx(const void* v, int numSlots) const {
2736*c8dee2aaSAndroid Build Coastguard Worker         auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipeline_BinaryOpCtx*)v);
2737*c8dee2aaSAndroid Build Coastguard Worker         return {this->offsetCtx(ctx.dst, numSlots),
2738*c8dee2aaSAndroid Build Coastguard Worker                 this->offsetCtx(ctx.src, numSlots)};
2739*c8dee2aaSAndroid Build Coastguard Worker     }
2740*c8dee2aaSAndroid Build Coastguard Worker 
2741*c8dee2aaSAndroid Build Coastguard Worker     // Interprets the context value as a BinaryOp structure for copy_n_uniforms (numSlots is
2742*c8dee2aaSAndroid Build Coastguard Worker     // dictated by the op itself).
copyUniformCtx(const void * v,int numSlots) const2743*c8dee2aaSAndroid Build Coastguard Worker     std::tuple<std::string, std::string> copyUniformCtx(const void* v, int numSlots) const {
2744*c8dee2aaSAndroid Build Coastguard Worker         const auto *ctx = static_cast<const SkRasterPipeline_UniformCtx*>(v);
2745*c8dee2aaSAndroid Build Coastguard Worker         return {this->ptrCtx(ctx->dst, numSlots),
2746*c8dee2aaSAndroid Build Coastguard Worker                 this->multiImmCtx(reinterpret_cast<const float*>(ctx->src), numSlots)};
2747*c8dee2aaSAndroid Build Coastguard Worker     }
2748*c8dee2aaSAndroid Build Coastguard Worker 
2749*c8dee2aaSAndroid Build Coastguard Worker     // Interprets the context value as a pointer to two adjacent values.
adjacentPtrCtx(const void * ctx,int numSlots) const2750*c8dee2aaSAndroid Build Coastguard Worker     std::tuple<std::string, std::string> adjacentPtrCtx(const void* ctx, int numSlots) const {
2751*c8dee2aaSAndroid Build Coastguard Worker         const float *ctxAsSlot = static_cast<const float*>(ctx);
2752*c8dee2aaSAndroid Build Coastguard Worker         return std::make_tuple(this->ptrCtx(ctxAsSlot, numSlots),
2753*c8dee2aaSAndroid Build Coastguard Worker                                this->ptrCtx(ctxAsSlot + (N * numSlots), numSlots));
2754*c8dee2aaSAndroid Build Coastguard Worker     }
2755*c8dee2aaSAndroid Build Coastguard Worker 
2756*c8dee2aaSAndroid Build Coastguard Worker     // Interprets a slab offset as two adjacent slot ranges.
adjacentOffsetCtx(SkRPOffset offset,int numSlots) const2757*c8dee2aaSAndroid Build Coastguard Worker     std::tuple<std::string, std::string> adjacentOffsetCtx(SkRPOffset offset, int numSlots) const {
2758*c8dee2aaSAndroid Build Coastguard Worker         return this->adjacentPtrCtx((std::byte*)fSlots.values.data() + offset, numSlots);
2759*c8dee2aaSAndroid Build Coastguard Worker     }
2760*c8dee2aaSAndroid Build Coastguard Worker 
2761*c8dee2aaSAndroid Build Coastguard Worker     // Interprets the context value as a BinaryOp structure (numSlots is inferred from the distance
2762*c8dee2aaSAndroid Build Coastguard Worker     // between pointers).
adjacentBinaryOpCtx(const void * v) const2763*c8dee2aaSAndroid Build Coastguard Worker     std::tuple<std::string, std::string> adjacentBinaryOpCtx(const void* v) const {
2764*c8dee2aaSAndroid Build Coastguard Worker         auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipeline_BinaryOpCtx*)v);
2765*c8dee2aaSAndroid Build Coastguard Worker         int numSlots = (ctx.src - ctx.dst) / (N * sizeof(float));
2766*c8dee2aaSAndroid Build Coastguard Worker         return this->adjacentOffsetCtx(ctx.dst, numSlots);
2767*c8dee2aaSAndroid Build Coastguard Worker     }
2768*c8dee2aaSAndroid Build Coastguard Worker 
2769*c8dee2aaSAndroid Build Coastguard Worker     // Interprets the context value as a pointer to three adjacent values.
adjacent3PtrCtx(const void * ctx,int numSlots) const2770*c8dee2aaSAndroid Build Coastguard Worker     std::tuple<std::string, std::string, std::string> adjacent3PtrCtx(const void* ctx,
2771*c8dee2aaSAndroid Build Coastguard Worker                                                                       int numSlots) const {
2772*c8dee2aaSAndroid Build Coastguard Worker         const float *ctxAsSlot = static_cast<const float*>(ctx);
2773*c8dee2aaSAndroid Build Coastguard Worker         return {this->ptrCtx(ctxAsSlot, numSlots),
2774*c8dee2aaSAndroid Build Coastguard Worker                 this->ptrCtx(ctxAsSlot + (N * numSlots), numSlots),
2775*c8dee2aaSAndroid Build Coastguard Worker                 this->ptrCtx(ctxAsSlot + (2 * N * numSlots), numSlots)};
2776*c8dee2aaSAndroid Build Coastguard Worker     }
2777*c8dee2aaSAndroid Build Coastguard Worker 
2778*c8dee2aaSAndroid Build Coastguard Worker     // Interprets a slab offset as three adjacent slot ranges.
adjacent3OffsetCtx(SkRPOffset offset,int numSlots) const2779*c8dee2aaSAndroid Build Coastguard Worker     std::tuple<std::string, std::string, std::string> adjacent3OffsetCtx(SkRPOffset offset,
2780*c8dee2aaSAndroid Build Coastguard Worker                                                                          int numSlots) const {
2781*c8dee2aaSAndroid Build Coastguard Worker         return this->adjacent3PtrCtx((std::byte*)fSlots.values.data() + offset, numSlots);
2782*c8dee2aaSAndroid Build Coastguard Worker     }
2783*c8dee2aaSAndroid Build Coastguard Worker 
2784*c8dee2aaSAndroid Build Coastguard Worker     // Interprets the context value as a TernaryOp structure (numSlots is inferred from `delta`).
adjacentTernaryOpCtx(const void * v) const2785*c8dee2aaSAndroid Build Coastguard Worker     std::tuple<std::string, std::string, std::string> adjacentTernaryOpCtx(const void* v) const {
2786*c8dee2aaSAndroid Build Coastguard Worker         auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipeline_TernaryOpCtx*)v);
2787*c8dee2aaSAndroid Build Coastguard Worker         int numSlots = ctx.delta / (sizeof(float) * N);
2788*c8dee2aaSAndroid Build Coastguard Worker         return this->adjacent3OffsetCtx(ctx.dst, numSlots);
2789*c8dee2aaSAndroid Build Coastguard Worker     }
2790*c8dee2aaSAndroid Build Coastguard Worker 
2791*c8dee2aaSAndroid Build Coastguard Worker     // Stringizes a span of swizzle offsets to the textual equivalent (`xyzw`).
2792*c8dee2aaSAndroid Build Coastguard Worker     template <typename T>
swizzleOffsetSpan(SkSpan<T> offsets) const2793*c8dee2aaSAndroid Build Coastguard Worker     std::string swizzleOffsetSpan(SkSpan<T> offsets) const {
2794*c8dee2aaSAndroid Build Coastguard Worker         std::string src;
2795*c8dee2aaSAndroid Build Coastguard Worker         for (uint16_t offset : offsets) {
2796*c8dee2aaSAndroid Build Coastguard Worker             if (offset == (0 * N * sizeof(float))) {
2797*c8dee2aaSAndroid Build Coastguard Worker                 src.push_back('x');
2798*c8dee2aaSAndroid Build Coastguard Worker             } else if (offset == (1 * N * sizeof(float))) {
2799*c8dee2aaSAndroid Build Coastguard Worker                 src.push_back('y');
2800*c8dee2aaSAndroid Build Coastguard Worker             } else if (offset == (2 * N * sizeof(float))) {
2801*c8dee2aaSAndroid Build Coastguard Worker                 src.push_back('z');
2802*c8dee2aaSAndroid Build Coastguard Worker             } else if (offset == (3 * N * sizeof(float))) {
2803*c8dee2aaSAndroid Build Coastguard Worker                 src.push_back('w');
2804*c8dee2aaSAndroid Build Coastguard Worker             } else {
2805*c8dee2aaSAndroid Build Coastguard Worker                 src.push_back('?');
2806*c8dee2aaSAndroid Build Coastguard Worker             }
2807*c8dee2aaSAndroid Build Coastguard Worker         }
2808*c8dee2aaSAndroid Build Coastguard Worker         return src;
2809*c8dee2aaSAndroid Build Coastguard Worker     }
2810*c8dee2aaSAndroid Build Coastguard Worker 
2811*c8dee2aaSAndroid Build Coastguard Worker     // Determines the effective width of a swizzle op. When we decode a swizzle, we don't know the
2812*c8dee2aaSAndroid Build Coastguard Worker     // slot width of the original value; that's not preserved in the instruction encoding. (e.g.,
2813*c8dee2aaSAndroid Build Coastguard Worker     // myFloat4.y would be indistinguishable from myFloat2.y.) We do our best to make a readable
2814*c8dee2aaSAndroid Build Coastguard Worker     // dump using the data we have.
2815*c8dee2aaSAndroid Build Coastguard Worker     template <typename T>
swizzleWidth(SkSpan<T> offsets) const2816*c8dee2aaSAndroid Build Coastguard Worker     size_t swizzleWidth(SkSpan<T> offsets) const {
2817*c8dee2aaSAndroid Build Coastguard Worker         size_t highestComponent = *std::max_element(offsets.begin(), offsets.end()) /
2818*c8dee2aaSAndroid Build Coastguard Worker                                   (N * sizeof(float));
2819*c8dee2aaSAndroid Build Coastguard Worker         size_t swizzleWidth = offsets.size();
2820*c8dee2aaSAndroid Build Coastguard Worker         return std::max(swizzleWidth, highestComponent + 1);
2821*c8dee2aaSAndroid Build Coastguard Worker     }
2822*c8dee2aaSAndroid Build Coastguard Worker 
2823*c8dee2aaSAndroid Build Coastguard Worker     // Stringizes a swizzled pointer.
2824*c8dee2aaSAndroid Build Coastguard Worker     template <typename T>
swizzlePtr(const void * ptr,SkSpan<T> offsets) const2825*c8dee2aaSAndroid Build Coastguard Worker     std::string swizzlePtr(const void* ptr, SkSpan<T> offsets) const {
2826*c8dee2aaSAndroid Build Coastguard Worker         return "(" + this->ptrCtx(ptr, this->swizzleWidth(SkSpan(offsets))) + ")." +
2827*c8dee2aaSAndroid Build Coastguard Worker                this->swizzleOffsetSpan(SkSpan(offsets));
2828*c8dee2aaSAndroid Build Coastguard Worker     }
2829*c8dee2aaSAndroid Build Coastguard Worker 
2830*c8dee2aaSAndroid Build Coastguard Worker     // Interprets the context value as a SwizzleCtx structure.
swizzleCtx(ProgramOp op,const void * v) const2831*c8dee2aaSAndroid Build Coastguard Worker     std::tuple<std::string, std::string> swizzleCtx(ProgramOp op, const void* v) const {
2832*c8dee2aaSAndroid Build Coastguard Worker         auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipeline_SwizzleCtx*)v);
2833*c8dee2aaSAndroid Build Coastguard Worker         int destSlots = (int)op - (int)BuilderOp::swizzle_1 + 1;
2834*c8dee2aaSAndroid Build Coastguard Worker         return {this->offsetCtx(ctx.dst, destSlots),
2835*c8dee2aaSAndroid Build Coastguard Worker                 this->swizzlePtr(this->offsetToPtr(ctx.dst), SkSpan(ctx.offsets, destSlots))};
2836*c8dee2aaSAndroid Build Coastguard Worker     }
2837*c8dee2aaSAndroid Build Coastguard Worker 
2838*c8dee2aaSAndroid Build Coastguard Worker     // Interprets the context value as a SwizzleCopyCtx structure.
swizzleCopyCtx(ProgramOp op,const void * v) const2839*c8dee2aaSAndroid Build Coastguard Worker     std::tuple<std::string, std::string> swizzleCopyCtx(ProgramOp op, const void* v) const {
2840*c8dee2aaSAndroid Build Coastguard Worker         const auto* ctx = static_cast<const SkRasterPipeline_SwizzleCopyCtx*>(v);
2841*c8dee2aaSAndroid Build Coastguard Worker         int destSlots = (int)op - (int)BuilderOp::swizzle_copy_slot_masked + 1;
2842*c8dee2aaSAndroid Build Coastguard Worker 
2843*c8dee2aaSAndroid Build Coastguard Worker         return {this->swizzlePtr(ctx->dst, SkSpan(ctx->offsets, destSlots)),
2844*c8dee2aaSAndroid Build Coastguard Worker                 this->ptrCtx(ctx->src, destSlots)};
2845*c8dee2aaSAndroid Build Coastguard Worker     }
2846*c8dee2aaSAndroid Build Coastguard Worker 
2847*c8dee2aaSAndroid Build Coastguard Worker     // Interprets the context value as a ShuffleCtx structure.
shuffleCtx(const void * v) const2848*c8dee2aaSAndroid Build Coastguard Worker     std::tuple<std::string, std::string> shuffleCtx(const void* v) const {
2849*c8dee2aaSAndroid Build Coastguard Worker         const auto* ctx = static_cast<const SkRasterPipeline_ShuffleCtx*>(v);
2850*c8dee2aaSAndroid Build Coastguard Worker 
2851*c8dee2aaSAndroid Build Coastguard Worker         std::string dst = this->ptrCtx(ctx->ptr, ctx->count);
2852*c8dee2aaSAndroid Build Coastguard Worker         std::string src = "(" + dst + ")[";
2853*c8dee2aaSAndroid Build Coastguard Worker         for (int index = 0; index < ctx->count; ++index) {
2854*c8dee2aaSAndroid Build Coastguard Worker             if (ctx->offsets[index] % (N * sizeof(float))) {
2855*c8dee2aaSAndroid Build Coastguard Worker                 src.push_back('?');
2856*c8dee2aaSAndroid Build Coastguard Worker             } else {
2857*c8dee2aaSAndroid Build Coastguard Worker                 src += std::to_string(ctx->offsets[index] / (N * sizeof(float)));
2858*c8dee2aaSAndroid Build Coastguard Worker             }
2859*c8dee2aaSAndroid Build Coastguard Worker             src.push_back(' ');
2860*c8dee2aaSAndroid Build Coastguard Worker         }
2861*c8dee2aaSAndroid Build Coastguard Worker         src.back() = ']';
2862*c8dee2aaSAndroid Build Coastguard Worker         return std::make_tuple(dst, src);
2863*c8dee2aaSAndroid Build Coastguard Worker     }
2864*c8dee2aaSAndroid Build Coastguard Worker 
2865*c8dee2aaSAndroid Build Coastguard Worker     // Interprets the context value as a packed MatrixMultiplyCtx structure.
matrixMultiply(const void * v) const2866*c8dee2aaSAndroid Build Coastguard Worker     std::tuple<std::string, std::string, std::string> matrixMultiply(const void* v) const {
2867*c8dee2aaSAndroid Build Coastguard Worker         auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipeline_MatrixMultiplyCtx*)v);
2868*c8dee2aaSAndroid Build Coastguard Worker         int leftMatrix = ctx.leftColumns * ctx.leftRows;
2869*c8dee2aaSAndroid Build Coastguard Worker         int rightMatrix = ctx.rightColumns * ctx.rightRows;
2870*c8dee2aaSAndroid Build Coastguard Worker         int resultMatrix = ctx.rightColumns * ctx.leftRows;
2871*c8dee2aaSAndroid Build Coastguard Worker         SkRPOffset leftOffset = ctx.dst + (ctx.rightColumns * ctx.leftRows * sizeof(float) * N);
2872*c8dee2aaSAndroid Build Coastguard Worker         SkRPOffset rightOffset = leftOffset + (ctx.leftColumns * ctx.leftRows * sizeof(float) * N);
2873*c8dee2aaSAndroid Build Coastguard Worker         return {SkSL::String::printf("mat%dx%d(%s)",
2874*c8dee2aaSAndroid Build Coastguard Worker                                      ctx.rightColumns,
2875*c8dee2aaSAndroid Build Coastguard Worker                                      ctx.leftRows,
2876*c8dee2aaSAndroid Build Coastguard Worker                                      this->offsetCtx(ctx.dst, resultMatrix).c_str()),
2877*c8dee2aaSAndroid Build Coastguard Worker                 SkSL::String::printf("mat%dx%d(%s)",
2878*c8dee2aaSAndroid Build Coastguard Worker                                      ctx.leftColumns,
2879*c8dee2aaSAndroid Build Coastguard Worker                                      ctx.leftRows,
2880*c8dee2aaSAndroid Build Coastguard Worker                                      this->offsetCtx(leftOffset, leftMatrix).c_str()),
2881*c8dee2aaSAndroid Build Coastguard Worker                 SkSL::String::printf("mat%dx%d(%s)",
2882*c8dee2aaSAndroid Build Coastguard Worker                                      ctx.rightColumns,
2883*c8dee2aaSAndroid Build Coastguard Worker                                      ctx.rightRows,
2884*c8dee2aaSAndroid Build Coastguard Worker                                      this->offsetCtx(rightOffset, rightMatrix).c_str())};
2885*c8dee2aaSAndroid Build Coastguard Worker     }
2886*c8dee2aaSAndroid Build Coastguard Worker 
2887*c8dee2aaSAndroid Build Coastguard Worker private:
2888*c8dee2aaSAndroid Build Coastguard Worker     const int N = SkOpts::raster_pipeline_highp_stride;
2889*c8dee2aaSAndroid Build Coastguard Worker     const Program& fProgram;
2890*c8dee2aaSAndroid Build Coastguard Worker     TArray<Stage> fStages;
2891*c8dee2aaSAndroid Build Coastguard Worker     TArray<std::string> fSlotNameList;
2892*c8dee2aaSAndroid Build Coastguard Worker     THashMap<int, int> fLabelToStageMap;  // <label ID, stage index>
2893*c8dee2aaSAndroid Build Coastguard Worker     SlotData fSlots;
2894*c8dee2aaSAndroid Build Coastguard Worker     SkSpan<float> fUniforms;
2895*c8dee2aaSAndroid Build Coastguard Worker };
2896*c8dee2aaSAndroid Build Coastguard Worker 
dump(SkWStream * out,bool writeInstructionCount)2897*c8dee2aaSAndroid Build Coastguard Worker void Program::Dumper::dump(SkWStream* out, bool writeInstructionCount) {
2898*c8dee2aaSAndroid Build Coastguard Worker     using POp = ProgramOp;
2899*c8dee2aaSAndroid Build Coastguard Worker 
2900*c8dee2aaSAndroid Build Coastguard Worker     // Allocate memory for the slot and uniform data, even though the program won't ever be
2901*c8dee2aaSAndroid Build Coastguard Worker     // executed. The program requires pointer ranges for managing its data, and ASAN will report
2902*c8dee2aaSAndroid Build Coastguard Worker     // errors if those pointers are pointing at unallocated memory.
2903*c8dee2aaSAndroid Build Coastguard Worker     SkArenaAlloc alloc(/*firstHeapAllocation=*/1000);
2904*c8dee2aaSAndroid Build Coastguard Worker     fSlots = fProgram.allocateSlotData(&alloc).value();
2905*c8dee2aaSAndroid Build Coastguard Worker     float* uniformPtr = alloc.makeArray<float>(fProgram.fNumUniformSlots);
2906*c8dee2aaSAndroid Build Coastguard Worker     fUniforms = SkSpan(uniformPtr, fProgram.fNumUniformSlots);
2907*c8dee2aaSAndroid Build Coastguard Worker 
2908*c8dee2aaSAndroid Build Coastguard Worker     // Turn this program into an array of Raster Pipeline stages.
2909*c8dee2aaSAndroid Build Coastguard Worker     fProgram.makeStages(&fStages, &alloc, fUniforms, fSlots);
2910*c8dee2aaSAndroid Build Coastguard Worker 
2911*c8dee2aaSAndroid Build Coastguard Worker     // Assemble lookup tables for program labels and slot names.
2912*c8dee2aaSAndroid Build Coastguard Worker     this->buildLabelToStageMap();
2913*c8dee2aaSAndroid Build Coastguard Worker     this->buildUniqueSlotNameList();
2914*c8dee2aaSAndroid Build Coastguard Worker 
2915*c8dee2aaSAndroid Build Coastguard Worker     // Emit the program's instruction count.
2916*c8dee2aaSAndroid Build Coastguard Worker     if (writeInstructionCount) {
2917*c8dee2aaSAndroid Build Coastguard Worker         int invocationCount = 0, instructionCount = 0;
2918*c8dee2aaSAndroid Build Coastguard Worker         for (const Stage& stage : fStages) {
2919*c8dee2aaSAndroid Build Coastguard Worker             switch (stage.op) {
2920*c8dee2aaSAndroid Build Coastguard Worker                 case POp::label:
2921*c8dee2aaSAndroid Build Coastguard Worker                     // consumes zero instructions
2922*c8dee2aaSAndroid Build Coastguard Worker                     break;
2923*c8dee2aaSAndroid Build Coastguard Worker 
2924*c8dee2aaSAndroid Build Coastguard Worker                 case POp::invoke_shader:
2925*c8dee2aaSAndroid Build Coastguard Worker                 case POp::invoke_color_filter:
2926*c8dee2aaSAndroid Build Coastguard Worker                 case POp::invoke_blender:
2927*c8dee2aaSAndroid Build Coastguard Worker                 case POp::invoke_to_linear_srgb:
2928*c8dee2aaSAndroid Build Coastguard Worker                 case POp::invoke_from_linear_srgb:
2929*c8dee2aaSAndroid Build Coastguard Worker                     ++invocationCount;
2930*c8dee2aaSAndroid Build Coastguard Worker                     break;
2931*c8dee2aaSAndroid Build Coastguard Worker 
2932*c8dee2aaSAndroid Build Coastguard Worker                 default:
2933*c8dee2aaSAndroid Build Coastguard Worker                     ++instructionCount;
2934*c8dee2aaSAndroid Build Coastguard Worker                     break;
2935*c8dee2aaSAndroid Build Coastguard Worker             }
2936*c8dee2aaSAndroid Build Coastguard Worker         }
2937*c8dee2aaSAndroid Build Coastguard Worker 
2938*c8dee2aaSAndroid Build Coastguard Worker         out->writeText(std::to_string(instructionCount).c_str());
2939*c8dee2aaSAndroid Build Coastguard Worker         out->writeText(" instructions");
2940*c8dee2aaSAndroid Build Coastguard Worker         if (invocationCount > 0) {
2941*c8dee2aaSAndroid Build Coastguard Worker             out->writeText(", ");
2942*c8dee2aaSAndroid Build Coastguard Worker             out->writeText(std::to_string(invocationCount).c_str());
2943*c8dee2aaSAndroid Build Coastguard Worker             out->writeText(" invocations");
2944*c8dee2aaSAndroid Build Coastguard Worker         }
2945*c8dee2aaSAndroid Build Coastguard Worker         out->writeText("\n\n");
2946*c8dee2aaSAndroid Build Coastguard Worker     }
2947*c8dee2aaSAndroid Build Coastguard Worker 
2948*c8dee2aaSAndroid Build Coastguard Worker     // Emit all of the program's immutable data.
2949*c8dee2aaSAndroid Build Coastguard Worker     const char* header = "[immutable slots]\n";
2950*c8dee2aaSAndroid Build Coastguard Worker     const char* footer = "";
2951*c8dee2aaSAndroid Build Coastguard Worker     for (const Instruction& inst : fProgram.fInstructions) {
2952*c8dee2aaSAndroid Build Coastguard Worker         if (inst.fOp == BuilderOp::store_immutable_value) {
2953*c8dee2aaSAndroid Build Coastguard Worker             out->writeText(header);
2954*c8dee2aaSAndroid Build Coastguard Worker             out->writeText("i");
2955*c8dee2aaSAndroid Build Coastguard Worker             out->writeText(std::to_string(inst.fSlotA).c_str());
2956*c8dee2aaSAndroid Build Coastguard Worker             out->writeText(" = ");
2957*c8dee2aaSAndroid Build Coastguard Worker             out->writeText(this->imm(sk_bit_cast<float>(inst.fImmA)).c_str());
2958*c8dee2aaSAndroid Build Coastguard Worker             out->writeText("\n");
2959*c8dee2aaSAndroid Build Coastguard Worker 
2960*c8dee2aaSAndroid Build Coastguard Worker             header = "";
2961*c8dee2aaSAndroid Build Coastguard Worker             footer = "\n";
2962*c8dee2aaSAndroid Build Coastguard Worker         }
2963*c8dee2aaSAndroid Build Coastguard Worker     }
2964*c8dee2aaSAndroid Build Coastguard Worker     out->writeText(footer);
2965*c8dee2aaSAndroid Build Coastguard Worker 
2966*c8dee2aaSAndroid Build Coastguard Worker     // Emit the program's instruction list.
2967*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < fStages.size(); ++index) {
2968*c8dee2aaSAndroid Build Coastguard Worker         const Stage& stage = fStages[index];
2969*c8dee2aaSAndroid Build Coastguard Worker 
2970*c8dee2aaSAndroid Build Coastguard Worker         std::string opArg1, opArg2, opArg3, opSwizzle;
2971*c8dee2aaSAndroid Build Coastguard Worker         switch (stage.op) {
2972*c8dee2aaSAndroid Build Coastguard Worker             case POp::label:
2973*c8dee2aaSAndroid Build Coastguard Worker             case POp::invoke_shader:
2974*c8dee2aaSAndroid Build Coastguard Worker             case POp::invoke_color_filter:
2975*c8dee2aaSAndroid Build Coastguard Worker             case POp::invoke_blender:
2976*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->immCtx(stage.ctx, /*showAsFloat=*/false);
2977*c8dee2aaSAndroid Build Coastguard Worker                 break;
2978*c8dee2aaSAndroid Build Coastguard Worker 
2979*c8dee2aaSAndroid Build Coastguard Worker             case POp::case_op: {
2980*c8dee2aaSAndroid Build Coastguard Worker                 auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipeline_CaseOpCtx*)stage.ctx);
2981*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->offsetCtx(ctx.offset, 1);
2982*c8dee2aaSAndroid Build Coastguard Worker                 opArg2 = this->offsetCtx(ctx.offset + sizeof(int32_t) * N, 1);
2983*c8dee2aaSAndroid Build Coastguard Worker                 opArg3 = this->imm(sk_bit_cast<float>(ctx.expectedValue), /*showAsFloat=*/false);
2984*c8dee2aaSAndroid Build Coastguard Worker                 break;
2985*c8dee2aaSAndroid Build Coastguard Worker             }
2986*c8dee2aaSAndroid Build Coastguard Worker             case POp::swizzle_1:
2987*c8dee2aaSAndroid Build Coastguard Worker             case POp::swizzle_2:
2988*c8dee2aaSAndroid Build Coastguard Worker             case POp::swizzle_3:
2989*c8dee2aaSAndroid Build Coastguard Worker             case POp::swizzle_4:
2990*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->swizzleCtx(stage.op, stage.ctx);
2991*c8dee2aaSAndroid Build Coastguard Worker                 break;
2992*c8dee2aaSAndroid Build Coastguard Worker 
2993*c8dee2aaSAndroid Build Coastguard Worker             case POp::swizzle_copy_slot_masked:
2994*c8dee2aaSAndroid Build Coastguard Worker             case POp::swizzle_copy_2_slots_masked:
2995*c8dee2aaSAndroid Build Coastguard Worker             case POp::swizzle_copy_3_slots_masked:
2996*c8dee2aaSAndroid Build Coastguard Worker             case POp::swizzle_copy_4_slots_masked:
2997*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->swizzleCopyCtx(stage.op, stage.ctx);
2998*c8dee2aaSAndroid Build Coastguard Worker                 break;
2999*c8dee2aaSAndroid Build Coastguard Worker 
3000*c8dee2aaSAndroid Build Coastguard Worker             case POp::refract_4_floats:
3001*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->adjacentPtrCtx(stage.ctx, 4);
3002*c8dee2aaSAndroid Build Coastguard Worker                 opArg3 = this->ptrCtx((const float*)(stage.ctx) + (8 * N), 1);
3003*c8dee2aaSAndroid Build Coastguard Worker                 break;
3004*c8dee2aaSAndroid Build Coastguard Worker 
3005*c8dee2aaSAndroid Build Coastguard Worker             case POp::dot_2_floats:
3006*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->ptrCtx(stage.ctx, 1);
3007*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg2, opArg3) = this->adjacentPtrCtx(stage.ctx, 2);
3008*c8dee2aaSAndroid Build Coastguard Worker                 break;
3009*c8dee2aaSAndroid Build Coastguard Worker 
3010*c8dee2aaSAndroid Build Coastguard Worker             case POp::dot_3_floats:
3011*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->ptrCtx(stage.ctx, 1);
3012*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg2, opArg3) = this->adjacentPtrCtx(stage.ctx, 3);
3013*c8dee2aaSAndroid Build Coastguard Worker                 break;
3014*c8dee2aaSAndroid Build Coastguard Worker 
3015*c8dee2aaSAndroid Build Coastguard Worker             case POp::dot_4_floats:
3016*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->ptrCtx(stage.ctx, 1);
3017*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg2, opArg3) = this->adjacentPtrCtx(stage.ctx, 4);
3018*c8dee2aaSAndroid Build Coastguard Worker                 break;
3019*c8dee2aaSAndroid Build Coastguard Worker 
3020*c8dee2aaSAndroid Build Coastguard Worker             case POp::shuffle:
3021*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->shuffleCtx(stage.ctx);
3022*c8dee2aaSAndroid Build Coastguard Worker                 break;
3023*c8dee2aaSAndroid Build Coastguard Worker 
3024*c8dee2aaSAndroid Build Coastguard Worker             case POp::matrix_multiply_2:
3025*c8dee2aaSAndroid Build Coastguard Worker             case POp::matrix_multiply_3:
3026*c8dee2aaSAndroid Build Coastguard Worker             case POp::matrix_multiply_4:
3027*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2, opArg3) = this->matrixMultiply(stage.ctx);
3028*c8dee2aaSAndroid Build Coastguard Worker                 break;
3029*c8dee2aaSAndroid Build Coastguard Worker 
3030*c8dee2aaSAndroid Build Coastguard Worker             case POp::load_condition_mask:
3031*c8dee2aaSAndroid Build Coastguard Worker             case POp::store_condition_mask:
3032*c8dee2aaSAndroid Build Coastguard Worker             case POp::load_loop_mask:
3033*c8dee2aaSAndroid Build Coastguard Worker             case POp::store_loop_mask:
3034*c8dee2aaSAndroid Build Coastguard Worker             case POp::merge_loop_mask:
3035*c8dee2aaSAndroid Build Coastguard Worker             case POp::reenable_loop_mask:
3036*c8dee2aaSAndroid Build Coastguard Worker             case POp::load_return_mask:
3037*c8dee2aaSAndroid Build Coastguard Worker             case POp::store_return_mask:
3038*c8dee2aaSAndroid Build Coastguard Worker             case POp::continue_op:
3039*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_float_from_int: case POp::cast_to_float_from_uint:
3040*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_int_from_float: case POp::cast_to_uint_from_float:
3041*c8dee2aaSAndroid Build Coastguard Worker             case POp::abs_int:
3042*c8dee2aaSAndroid Build Coastguard Worker             case POp::acos_float:
3043*c8dee2aaSAndroid Build Coastguard Worker             case POp::asin_float:
3044*c8dee2aaSAndroid Build Coastguard Worker             case POp::atan_float:
3045*c8dee2aaSAndroid Build Coastguard Worker             case POp::ceil_float:
3046*c8dee2aaSAndroid Build Coastguard Worker             case POp::cos_float:
3047*c8dee2aaSAndroid Build Coastguard Worker             case POp::exp_float:
3048*c8dee2aaSAndroid Build Coastguard Worker             case POp::exp2_float:
3049*c8dee2aaSAndroid Build Coastguard Worker             case POp::log_float:
3050*c8dee2aaSAndroid Build Coastguard Worker             case POp::log2_float:
3051*c8dee2aaSAndroid Build Coastguard Worker             case POp::floor_float:
3052*c8dee2aaSAndroid Build Coastguard Worker             case POp::invsqrt_float:
3053*c8dee2aaSAndroid Build Coastguard Worker             case POp::sin_float:
3054*c8dee2aaSAndroid Build Coastguard Worker             case POp::sqrt_float:
3055*c8dee2aaSAndroid Build Coastguard Worker             case POp::tan_float:
3056*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->ptrCtx(stage.ctx, 1);
3057*c8dee2aaSAndroid Build Coastguard Worker                 break;
3058*c8dee2aaSAndroid Build Coastguard Worker 
3059*c8dee2aaSAndroid Build Coastguard Worker             case POp::store_src_rg:
3060*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_float_from_2_ints: case POp::cast_to_float_from_2_uints:
3061*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_int_from_2_floats: case POp::cast_to_uint_from_2_floats:
3062*c8dee2aaSAndroid Build Coastguard Worker             case POp::abs_2_ints:
3063*c8dee2aaSAndroid Build Coastguard Worker             case POp::ceil_2_floats:
3064*c8dee2aaSAndroid Build Coastguard Worker             case POp::floor_2_floats:
3065*c8dee2aaSAndroid Build Coastguard Worker             case POp::invsqrt_2_floats:
3066*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->ptrCtx(stage.ctx, 2);
3067*c8dee2aaSAndroid Build Coastguard Worker                 break;
3068*c8dee2aaSAndroid Build Coastguard Worker 
3069*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_float_from_3_ints: case POp::cast_to_float_from_3_uints:
3070*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_int_from_3_floats: case POp::cast_to_uint_from_3_floats:
3071*c8dee2aaSAndroid Build Coastguard Worker             case POp::abs_3_ints:
3072*c8dee2aaSAndroid Build Coastguard Worker             case POp::ceil_3_floats:
3073*c8dee2aaSAndroid Build Coastguard Worker             case POp::floor_3_floats:
3074*c8dee2aaSAndroid Build Coastguard Worker             case POp::invsqrt_3_floats:
3075*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->ptrCtx(stage.ctx, 3);
3076*c8dee2aaSAndroid Build Coastguard Worker                 break;
3077*c8dee2aaSAndroid Build Coastguard Worker 
3078*c8dee2aaSAndroid Build Coastguard Worker             case POp::load_src:
3079*c8dee2aaSAndroid Build Coastguard Worker             case POp::load_dst:
3080*c8dee2aaSAndroid Build Coastguard Worker             case POp::exchange_src:
3081*c8dee2aaSAndroid Build Coastguard Worker             case POp::store_src:
3082*c8dee2aaSAndroid Build Coastguard Worker             case POp::store_dst:
3083*c8dee2aaSAndroid Build Coastguard Worker             case POp::store_device_xy01:
3084*c8dee2aaSAndroid Build Coastguard Worker             case POp::invoke_to_linear_srgb:
3085*c8dee2aaSAndroid Build Coastguard Worker             case POp::invoke_from_linear_srgb:
3086*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_float_from_4_ints: case POp::cast_to_float_from_4_uints:
3087*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_int_from_4_floats: case POp::cast_to_uint_from_4_floats:
3088*c8dee2aaSAndroid Build Coastguard Worker             case POp::abs_4_ints:
3089*c8dee2aaSAndroid Build Coastguard Worker             case POp::ceil_4_floats:
3090*c8dee2aaSAndroid Build Coastguard Worker             case POp::floor_4_floats:
3091*c8dee2aaSAndroid Build Coastguard Worker             case POp::invsqrt_4_floats:
3092*c8dee2aaSAndroid Build Coastguard Worker             case POp::inverse_mat2:
3093*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->ptrCtx(stage.ctx, 4);
3094*c8dee2aaSAndroid Build Coastguard Worker                 break;
3095*c8dee2aaSAndroid Build Coastguard Worker 
3096*c8dee2aaSAndroid Build Coastguard Worker             case POp::inverse_mat3:
3097*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->ptrCtx(stage.ctx, 9);
3098*c8dee2aaSAndroid Build Coastguard Worker                 break;
3099*c8dee2aaSAndroid Build Coastguard Worker 
3100*c8dee2aaSAndroid Build Coastguard Worker             case POp::inverse_mat4:
3101*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->ptrCtx(stage.ctx, 16);
3102*c8dee2aaSAndroid Build Coastguard Worker                 break;
3103*c8dee2aaSAndroid Build Coastguard Worker 
3104*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_constant:
3105*c8dee2aaSAndroid Build Coastguard Worker             case POp::add_imm_float:
3106*c8dee2aaSAndroid Build Coastguard Worker             case POp::mul_imm_float:
3107*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmple_imm_float:
3108*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmplt_imm_float:
3109*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpeq_imm_float:
3110*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpne_imm_float:
3111*c8dee2aaSAndroid Build Coastguard Worker             case POp::min_imm_float:
3112*c8dee2aaSAndroid Build Coastguard Worker             case POp::max_imm_float:
3113*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->constantCtx(stage.ctx, 1);
3114*c8dee2aaSAndroid Build Coastguard Worker                 break;
3115*c8dee2aaSAndroid Build Coastguard Worker 
3116*c8dee2aaSAndroid Build Coastguard Worker             case POp::add_imm_int:
3117*c8dee2aaSAndroid Build Coastguard Worker             case POp::mul_imm_int:
3118*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_and_imm_int:
3119*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_xor_imm_int:
3120*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmple_imm_int:
3121*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmple_imm_uint:
3122*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmplt_imm_int:
3123*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmplt_imm_uint:
3124*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpeq_imm_int:
3125*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpne_imm_int:
3126*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->constantCtx(stage.ctx, 1, /*showAsFloat=*/false);
3127*c8dee2aaSAndroid Build Coastguard Worker                 break;
3128*c8dee2aaSAndroid Build Coastguard Worker 
3129*c8dee2aaSAndroid Build Coastguard Worker             case POp::splat_2_constants:
3130*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_and_imm_2_ints:
3131*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->constantCtx(stage.ctx, 2);
3132*c8dee2aaSAndroid Build Coastguard Worker                 break;
3133*c8dee2aaSAndroid Build Coastguard Worker 
3134*c8dee2aaSAndroid Build Coastguard Worker             case POp::splat_3_constants:
3135*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_and_imm_3_ints:
3136*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->constantCtx(stage.ctx, 3);
3137*c8dee2aaSAndroid Build Coastguard Worker                 break;
3138*c8dee2aaSAndroid Build Coastguard Worker 
3139*c8dee2aaSAndroid Build Coastguard Worker             case POp::splat_4_constants:
3140*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_and_imm_4_ints:
3141*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->constantCtx(stage.ctx, 4);
3142*c8dee2aaSAndroid Build Coastguard Worker                 break;
3143*c8dee2aaSAndroid Build Coastguard Worker 
3144*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_uniform:
3145*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->copyUniformCtx(stage.ctx, 1);
3146*c8dee2aaSAndroid Build Coastguard Worker                 break;
3147*c8dee2aaSAndroid Build Coastguard Worker 
3148*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_2_uniforms:
3149*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->copyUniformCtx(stage.ctx, 2);
3150*c8dee2aaSAndroid Build Coastguard Worker                 break;
3151*c8dee2aaSAndroid Build Coastguard Worker 
3152*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_3_uniforms:
3153*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->copyUniformCtx(stage.ctx, 3);
3154*c8dee2aaSAndroid Build Coastguard Worker                 break;
3155*c8dee2aaSAndroid Build Coastguard Worker 
3156*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_4_uniforms:
3157*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->copyUniformCtx(stage.ctx, 4);
3158*c8dee2aaSAndroid Build Coastguard Worker                 break;
3159*c8dee2aaSAndroid Build Coastguard Worker 
3160*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_slot_masked:
3161*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_slot_unmasked:
3162*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_immutable_unmasked:
3163*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->binaryOpCtx(stage.ctx, 1);
3164*c8dee2aaSAndroid Build Coastguard Worker                 break;
3165*c8dee2aaSAndroid Build Coastguard Worker 
3166*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_2_slots_masked:
3167*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_2_slots_unmasked:
3168*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_2_immutables_unmasked:
3169*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->binaryOpCtx(stage.ctx, 2);
3170*c8dee2aaSAndroid Build Coastguard Worker                 break;
3171*c8dee2aaSAndroid Build Coastguard Worker 
3172*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_3_slots_masked:
3173*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_3_slots_unmasked:
3174*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_3_immutables_unmasked:
3175*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->binaryOpCtx(stage.ctx, 3);
3176*c8dee2aaSAndroid Build Coastguard Worker                 break;
3177*c8dee2aaSAndroid Build Coastguard Worker 
3178*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_4_slots_masked:
3179*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_4_slots_unmasked:
3180*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_4_immutables_unmasked:
3181*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->binaryOpCtx(stage.ctx, 4);
3182*c8dee2aaSAndroid Build Coastguard Worker                 break;
3183*c8dee2aaSAndroid Build Coastguard Worker 
3184*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_from_indirect_uniform_unmasked:
3185*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_from_indirect_unmasked:
3186*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_to_indirect_masked: {
3187*c8dee2aaSAndroid Build Coastguard Worker                 const auto* ctx = static_cast<SkRasterPipeline_CopyIndirectCtx*>(stage.ctx);
3188*c8dee2aaSAndroid Build Coastguard Worker                 // We don't incorporate the indirect-limit in the output
3189*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->ptrCtx(ctx->dst, ctx->slots);
3190*c8dee2aaSAndroid Build Coastguard Worker                 opArg2 = this->ptrCtx(ctx->src, ctx->slots);
3191*c8dee2aaSAndroid Build Coastguard Worker                 opArg3 = this->ptrCtx(ctx->indirectOffset, 1);
3192*c8dee2aaSAndroid Build Coastguard Worker                 break;
3193*c8dee2aaSAndroid Build Coastguard Worker             }
3194*c8dee2aaSAndroid Build Coastguard Worker             case POp::swizzle_copy_to_indirect_masked: {
3195*c8dee2aaSAndroid Build Coastguard Worker                 const auto* ctx = static_cast<SkRasterPipeline_SwizzleCopyIndirectCtx*>(stage.ctx);
3196*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->ptrCtx(ctx->dst, this->swizzleWidth(SkSpan(ctx->offsets,
3197*c8dee2aaSAndroid Build Coastguard Worker                                                                           ctx->slots)));
3198*c8dee2aaSAndroid Build Coastguard Worker                 opArg2 = this->ptrCtx(ctx->src, ctx->slots);
3199*c8dee2aaSAndroid Build Coastguard Worker                 opArg3 = this->ptrCtx(ctx->indirectOffset, 1);
3200*c8dee2aaSAndroid Build Coastguard Worker                 opSwizzle = this->swizzleOffsetSpan(SkSpan(ctx->offsets, ctx->slots));
3201*c8dee2aaSAndroid Build Coastguard Worker                 break;
3202*c8dee2aaSAndroid Build Coastguard Worker             }
3203*c8dee2aaSAndroid Build Coastguard Worker             case POp::merge_condition_mask:
3204*c8dee2aaSAndroid Build Coastguard Worker             case POp::merge_inv_condition_mask:
3205*c8dee2aaSAndroid Build Coastguard Worker             case POp::add_float:   case POp::add_int:
3206*c8dee2aaSAndroid Build Coastguard Worker             case POp::sub_float:   case POp::sub_int:
3207*c8dee2aaSAndroid Build Coastguard Worker             case POp::mul_float:   case POp::mul_int:
3208*c8dee2aaSAndroid Build Coastguard Worker             case POp::div_float:   case POp::div_int:   case POp::div_uint:
3209*c8dee2aaSAndroid Build Coastguard Worker                                    case POp::bitwise_and_int:
3210*c8dee2aaSAndroid Build Coastguard Worker                                    case POp::bitwise_or_int:
3211*c8dee2aaSAndroid Build Coastguard Worker                                    case POp::bitwise_xor_int:
3212*c8dee2aaSAndroid Build Coastguard Worker             case POp::mod_float:
3213*c8dee2aaSAndroid Build Coastguard Worker             case POp::min_float:   case POp::min_int:   case POp::min_uint:
3214*c8dee2aaSAndroid Build Coastguard Worker             case POp::max_float:   case POp::max_int:   case POp::max_uint:
3215*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmplt_float: case POp::cmplt_int: case POp::cmplt_uint:
3216*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmple_float: case POp::cmple_int: case POp::cmple_uint:
3217*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpeq_float: case POp::cmpeq_int:
3218*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpne_float: case POp::cmpne_int:
3219*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->adjacentPtrCtx(stage.ctx, 1);
3220*c8dee2aaSAndroid Build Coastguard Worker                 break;
3221*c8dee2aaSAndroid Build Coastguard Worker 
3222*c8dee2aaSAndroid Build Coastguard Worker             case POp::mix_float:   case POp::mix_int:
3223*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2, opArg3) = this->adjacent3PtrCtx(stage.ctx, 1);
3224*c8dee2aaSAndroid Build Coastguard Worker                 break;
3225*c8dee2aaSAndroid Build Coastguard Worker 
3226*c8dee2aaSAndroid Build Coastguard Worker             case POp::add_2_floats:   case POp::add_2_ints:
3227*c8dee2aaSAndroid Build Coastguard Worker             case POp::sub_2_floats:   case POp::sub_2_ints:
3228*c8dee2aaSAndroid Build Coastguard Worker             case POp::mul_2_floats:   case POp::mul_2_ints:
3229*c8dee2aaSAndroid Build Coastguard Worker             case POp::div_2_floats:   case POp::div_2_ints:   case POp::div_2_uints:
3230*c8dee2aaSAndroid Build Coastguard Worker                                       case POp::bitwise_and_2_ints:
3231*c8dee2aaSAndroid Build Coastguard Worker                                       case POp::bitwise_or_2_ints:
3232*c8dee2aaSAndroid Build Coastguard Worker                                       case POp::bitwise_xor_2_ints:
3233*c8dee2aaSAndroid Build Coastguard Worker             case POp::mod_2_floats:
3234*c8dee2aaSAndroid Build Coastguard Worker             case POp::min_2_floats:   case POp::min_2_ints:   case POp::min_2_uints:
3235*c8dee2aaSAndroid Build Coastguard Worker             case POp::max_2_floats:   case POp::max_2_ints:   case POp::max_2_uints:
3236*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmplt_2_floats: case POp::cmplt_2_ints: case POp::cmplt_2_uints:
3237*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmple_2_floats: case POp::cmple_2_ints: case POp::cmple_2_uints:
3238*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpeq_2_floats: case POp::cmpeq_2_ints:
3239*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpne_2_floats: case POp::cmpne_2_ints:
3240*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->adjacentPtrCtx(stage.ctx, 2);
3241*c8dee2aaSAndroid Build Coastguard Worker                 break;
3242*c8dee2aaSAndroid Build Coastguard Worker 
3243*c8dee2aaSAndroid Build Coastguard Worker             case POp::mix_2_floats:   case POp::mix_2_ints:
3244*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2, opArg3) = this->adjacent3PtrCtx(stage.ctx, 2);
3245*c8dee2aaSAndroid Build Coastguard Worker                 break;
3246*c8dee2aaSAndroid Build Coastguard Worker 
3247*c8dee2aaSAndroid Build Coastguard Worker             case POp::add_3_floats:   case POp::add_3_ints:
3248*c8dee2aaSAndroid Build Coastguard Worker             case POp::sub_3_floats:   case POp::sub_3_ints:
3249*c8dee2aaSAndroid Build Coastguard Worker             case POp::mul_3_floats:   case POp::mul_3_ints:
3250*c8dee2aaSAndroid Build Coastguard Worker             case POp::div_3_floats:   case POp::div_3_ints:   case POp::div_3_uints:
3251*c8dee2aaSAndroid Build Coastguard Worker                                       case POp::bitwise_and_3_ints:
3252*c8dee2aaSAndroid Build Coastguard Worker                                       case POp::bitwise_or_3_ints:
3253*c8dee2aaSAndroid Build Coastguard Worker                                       case POp::bitwise_xor_3_ints:
3254*c8dee2aaSAndroid Build Coastguard Worker             case POp::mod_3_floats:
3255*c8dee2aaSAndroid Build Coastguard Worker             case POp::min_3_floats:   case POp::min_3_ints:   case POp::min_3_uints:
3256*c8dee2aaSAndroid Build Coastguard Worker             case POp::max_3_floats:   case POp::max_3_ints:   case POp::max_3_uints:
3257*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmplt_3_floats: case POp::cmplt_3_ints: case POp::cmplt_3_uints:
3258*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmple_3_floats: case POp::cmple_3_ints: case POp::cmple_3_uints:
3259*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpeq_3_floats: case POp::cmpeq_3_ints:
3260*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpne_3_floats: case POp::cmpne_3_ints:
3261*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->adjacentPtrCtx(stage.ctx, 3);
3262*c8dee2aaSAndroid Build Coastguard Worker                 break;
3263*c8dee2aaSAndroid Build Coastguard Worker 
3264*c8dee2aaSAndroid Build Coastguard Worker             case POp::mix_3_floats:   case POp::mix_3_ints:
3265*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2, opArg3) = this->adjacent3PtrCtx(stage.ctx, 3);
3266*c8dee2aaSAndroid Build Coastguard Worker                 break;
3267*c8dee2aaSAndroid Build Coastguard Worker 
3268*c8dee2aaSAndroid Build Coastguard Worker             case POp::add_4_floats:   case POp::add_4_ints:
3269*c8dee2aaSAndroid Build Coastguard Worker             case POp::sub_4_floats:   case POp::sub_4_ints:
3270*c8dee2aaSAndroid Build Coastguard Worker             case POp::mul_4_floats:   case POp::mul_4_ints:
3271*c8dee2aaSAndroid Build Coastguard Worker             case POp::div_4_floats:   case POp::div_4_ints:   case POp::div_4_uints:
3272*c8dee2aaSAndroid Build Coastguard Worker                                       case POp::bitwise_and_4_ints:
3273*c8dee2aaSAndroid Build Coastguard Worker                                       case POp::bitwise_or_4_ints:
3274*c8dee2aaSAndroid Build Coastguard Worker                                       case POp::bitwise_xor_4_ints:
3275*c8dee2aaSAndroid Build Coastguard Worker             case POp::mod_4_floats:
3276*c8dee2aaSAndroid Build Coastguard Worker             case POp::min_4_floats:   case POp::min_4_ints:   case POp::min_4_uints:
3277*c8dee2aaSAndroid Build Coastguard Worker             case POp::max_4_floats:   case POp::max_4_ints:   case POp::max_4_uints:
3278*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmplt_4_floats: case POp::cmplt_4_ints: case POp::cmplt_4_uints:
3279*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmple_4_floats: case POp::cmple_4_ints: case POp::cmple_4_uints:
3280*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpeq_4_floats: case POp::cmpeq_4_ints:
3281*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpne_4_floats: case POp::cmpne_4_ints:
3282*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->adjacentPtrCtx(stage.ctx, 4);
3283*c8dee2aaSAndroid Build Coastguard Worker                 break;
3284*c8dee2aaSAndroid Build Coastguard Worker 
3285*c8dee2aaSAndroid Build Coastguard Worker             case POp::mix_4_floats:   case POp::mix_4_ints:
3286*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2, opArg3) = this->adjacent3PtrCtx(stage.ctx, 4);
3287*c8dee2aaSAndroid Build Coastguard Worker                 break;
3288*c8dee2aaSAndroid Build Coastguard Worker 
3289*c8dee2aaSAndroid Build Coastguard Worker             case POp::add_n_floats:   case POp::add_n_ints:
3290*c8dee2aaSAndroid Build Coastguard Worker             case POp::sub_n_floats:   case POp::sub_n_ints:
3291*c8dee2aaSAndroid Build Coastguard Worker             case POp::mul_n_floats:   case POp::mul_n_ints:
3292*c8dee2aaSAndroid Build Coastguard Worker             case POp::div_n_floats:   case POp::div_n_ints:   case POp::div_n_uints:
3293*c8dee2aaSAndroid Build Coastguard Worker                                       case POp::bitwise_and_n_ints:
3294*c8dee2aaSAndroid Build Coastguard Worker                                       case POp::bitwise_or_n_ints:
3295*c8dee2aaSAndroid Build Coastguard Worker                                       case POp::bitwise_xor_n_ints:
3296*c8dee2aaSAndroid Build Coastguard Worker             case POp::mod_n_floats:
3297*c8dee2aaSAndroid Build Coastguard Worker             case POp::min_n_floats:   case POp::min_n_ints:   case POp::min_n_uints:
3298*c8dee2aaSAndroid Build Coastguard Worker             case POp::max_n_floats:   case POp::max_n_ints:   case POp::max_n_uints:
3299*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmplt_n_floats: case POp::cmplt_n_ints: case POp::cmplt_n_uints:
3300*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmple_n_floats: case POp::cmple_n_ints: case POp::cmple_n_uints:
3301*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpeq_n_floats: case POp::cmpeq_n_ints:
3302*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpne_n_floats: case POp::cmpne_n_ints:
3303*c8dee2aaSAndroid Build Coastguard Worker             case POp::atan2_n_floats:
3304*c8dee2aaSAndroid Build Coastguard Worker             case POp::pow_n_floats:
3305*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2) = this->adjacentBinaryOpCtx(stage.ctx);
3306*c8dee2aaSAndroid Build Coastguard Worker                 break;
3307*c8dee2aaSAndroid Build Coastguard Worker 
3308*c8dee2aaSAndroid Build Coastguard Worker             case POp::mix_n_floats:        case POp::mix_n_ints:
3309*c8dee2aaSAndroid Build Coastguard Worker             case POp::smoothstep_n_floats:
3310*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(opArg1, opArg2, opArg3) = this->adjacentTernaryOpCtx(stage.ctx);
3311*c8dee2aaSAndroid Build Coastguard Worker                 break;
3312*c8dee2aaSAndroid Build Coastguard Worker 
3313*c8dee2aaSAndroid Build Coastguard Worker             case POp::jump:
3314*c8dee2aaSAndroid Build Coastguard Worker             case POp::branch_if_all_lanes_active:
3315*c8dee2aaSAndroid Build Coastguard Worker             case POp::branch_if_any_lanes_active:
3316*c8dee2aaSAndroid Build Coastguard Worker             case POp::branch_if_no_lanes_active:
3317*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->branchOffset(static_cast<SkRasterPipeline_BranchCtx*>(stage.ctx),
3318*c8dee2aaSAndroid Build Coastguard Worker                                             index);
3319*c8dee2aaSAndroid Build Coastguard Worker                 break;
3320*c8dee2aaSAndroid Build Coastguard Worker 
3321*c8dee2aaSAndroid Build Coastguard Worker             case POp::branch_if_no_active_lanes_eq: {
3322*c8dee2aaSAndroid Build Coastguard Worker                 const auto* ctx = static_cast<SkRasterPipeline_BranchIfEqualCtx*>(stage.ctx);
3323*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->branchOffset(ctx, index);
3324*c8dee2aaSAndroid Build Coastguard Worker                 opArg2 = this->ptrCtx(ctx->ptr, 1);
3325*c8dee2aaSAndroid Build Coastguard Worker                 opArg3 = this->imm(sk_bit_cast<float>(ctx->value));
3326*c8dee2aaSAndroid Build Coastguard Worker                 break;
3327*c8dee2aaSAndroid Build Coastguard Worker             }
3328*c8dee2aaSAndroid Build Coastguard Worker             case POp::trace_var: {
3329*c8dee2aaSAndroid Build Coastguard Worker                 const auto* ctx = static_cast<SkRasterPipeline_TraceVarCtx*>(stage.ctx);
3330*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->ptrCtx(ctx->traceMask, 1);
3331*c8dee2aaSAndroid Build Coastguard Worker                 opArg2 = this->ptrCtx(ctx->data, ctx->numSlots);
3332*c8dee2aaSAndroid Build Coastguard Worker                 if (ctx->indirectOffset != nullptr) {
3333*c8dee2aaSAndroid Build Coastguard Worker                     opArg3 = " + " + this->ptrCtx(ctx->indirectOffset, 1);
3334*c8dee2aaSAndroid Build Coastguard Worker                 }
3335*c8dee2aaSAndroid Build Coastguard Worker                 break;
3336*c8dee2aaSAndroid Build Coastguard Worker             }
3337*c8dee2aaSAndroid Build Coastguard Worker             case POp::trace_line: {
3338*c8dee2aaSAndroid Build Coastguard Worker                 const auto* ctx = static_cast<SkRasterPipeline_TraceLineCtx*>(stage.ctx);
3339*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->ptrCtx(ctx->traceMask, 1);
3340*c8dee2aaSAndroid Build Coastguard Worker                 opArg2 = std::to_string(ctx->lineNumber);
3341*c8dee2aaSAndroid Build Coastguard Worker                 break;
3342*c8dee2aaSAndroid Build Coastguard Worker             }
3343*c8dee2aaSAndroid Build Coastguard Worker             case POp::trace_enter:
3344*c8dee2aaSAndroid Build Coastguard Worker             case POp::trace_exit: {
3345*c8dee2aaSAndroid Build Coastguard Worker                 const auto* ctx = static_cast<SkRasterPipeline_TraceFuncCtx*>(stage.ctx);
3346*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->ptrCtx(ctx->traceMask, 1);
3347*c8dee2aaSAndroid Build Coastguard Worker                 opArg2 = (fProgram.fDebugTrace &&
3348*c8dee2aaSAndroid Build Coastguard Worker                           ctx->funcIdx >= 0 &&
3349*c8dee2aaSAndroid Build Coastguard Worker                           ctx->funcIdx < (int)fProgram.fDebugTrace->fFuncInfo.size())
3350*c8dee2aaSAndroid Build Coastguard Worker                                  ? fProgram.fDebugTrace->fFuncInfo[ctx->funcIdx].name
3351*c8dee2aaSAndroid Build Coastguard Worker                                  : "???";
3352*c8dee2aaSAndroid Build Coastguard Worker                 break;
3353*c8dee2aaSAndroid Build Coastguard Worker             }
3354*c8dee2aaSAndroid Build Coastguard Worker             case POp::trace_scope: {
3355*c8dee2aaSAndroid Build Coastguard Worker                 const auto* ctx = static_cast<SkRasterPipeline_TraceScopeCtx*>(stage.ctx);
3356*c8dee2aaSAndroid Build Coastguard Worker                 opArg1 = this->ptrCtx(ctx->traceMask, 1);
3357*c8dee2aaSAndroid Build Coastguard Worker                 opArg2 = SkSL::String::printf("%+d", ctx->delta);
3358*c8dee2aaSAndroid Build Coastguard Worker                 break;
3359*c8dee2aaSAndroid Build Coastguard Worker             }
3360*c8dee2aaSAndroid Build Coastguard Worker             default:
3361*c8dee2aaSAndroid Build Coastguard Worker                 break;
3362*c8dee2aaSAndroid Build Coastguard Worker         }
3363*c8dee2aaSAndroid Build Coastguard Worker 
3364*c8dee2aaSAndroid Build Coastguard Worker         std::string_view opName;
3365*c8dee2aaSAndroid Build Coastguard Worker         switch (stage.op) {
3366*c8dee2aaSAndroid Build Coastguard Worker         #define M(x) case POp::x: opName = #x; break;
3367*c8dee2aaSAndroid Build Coastguard Worker             SK_RASTER_PIPELINE_OPS_ALL(M)
3368*c8dee2aaSAndroid Build Coastguard Worker             SKRP_EXTENDED_OPS(M)
3369*c8dee2aaSAndroid Build Coastguard Worker         #undef M
3370*c8dee2aaSAndroid Build Coastguard Worker         }
3371*c8dee2aaSAndroid Build Coastguard Worker 
3372*c8dee2aaSAndroid Build Coastguard Worker         std::string opText;
3373*c8dee2aaSAndroid Build Coastguard Worker         switch (stage.op) {
3374*c8dee2aaSAndroid Build Coastguard Worker             case POp::trace_var:
3375*c8dee2aaSAndroid Build Coastguard Worker                 opText = "TraceVar(" + opArg2 + opArg3 + ") when " + opArg1 + " is true";
3376*c8dee2aaSAndroid Build Coastguard Worker                 break;
3377*c8dee2aaSAndroid Build Coastguard Worker 
3378*c8dee2aaSAndroid Build Coastguard Worker             case POp::trace_line:
3379*c8dee2aaSAndroid Build Coastguard Worker                 opText = "TraceLine(" + opArg2 + ") when " + opArg1 + " is true";
3380*c8dee2aaSAndroid Build Coastguard Worker                 break;
3381*c8dee2aaSAndroid Build Coastguard Worker 
3382*c8dee2aaSAndroid Build Coastguard Worker             case POp::trace_enter:
3383*c8dee2aaSAndroid Build Coastguard Worker                 opText = "TraceEnter(" + opArg2 + ") when " + opArg1 + " is true";
3384*c8dee2aaSAndroid Build Coastguard Worker                 break;
3385*c8dee2aaSAndroid Build Coastguard Worker 
3386*c8dee2aaSAndroid Build Coastguard Worker             case POp::trace_exit:
3387*c8dee2aaSAndroid Build Coastguard Worker                 opText = "TraceExit(" + opArg2 + ") when " + opArg1 + " is true";
3388*c8dee2aaSAndroid Build Coastguard Worker                 break;
3389*c8dee2aaSAndroid Build Coastguard Worker 
3390*c8dee2aaSAndroid Build Coastguard Worker             case POp::trace_scope:
3391*c8dee2aaSAndroid Build Coastguard Worker                 opText = "TraceScope(" + opArg2 + ") when " + opArg1 + " is true";
3392*c8dee2aaSAndroid Build Coastguard Worker                 break;
3393*c8dee2aaSAndroid Build Coastguard Worker 
3394*c8dee2aaSAndroid Build Coastguard Worker             case POp::init_lane_masks:
3395*c8dee2aaSAndroid Build Coastguard Worker                 opText = "CondMask = LoopMask = RetMask = true";
3396*c8dee2aaSAndroid Build Coastguard Worker                 break;
3397*c8dee2aaSAndroid Build Coastguard Worker 
3398*c8dee2aaSAndroid Build Coastguard Worker             case POp::load_condition_mask:
3399*c8dee2aaSAndroid Build Coastguard Worker                 opText = "CondMask = " + opArg1;
3400*c8dee2aaSAndroid Build Coastguard Worker                 break;
3401*c8dee2aaSAndroid Build Coastguard Worker 
3402*c8dee2aaSAndroid Build Coastguard Worker             case POp::store_condition_mask:
3403*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = CondMask";
3404*c8dee2aaSAndroid Build Coastguard Worker                 break;
3405*c8dee2aaSAndroid Build Coastguard Worker 
3406*c8dee2aaSAndroid Build Coastguard Worker             case POp::merge_condition_mask:
3407*c8dee2aaSAndroid Build Coastguard Worker                 opText = "CondMask = " + opArg1 + " & " + opArg2;
3408*c8dee2aaSAndroid Build Coastguard Worker                 break;
3409*c8dee2aaSAndroid Build Coastguard Worker 
3410*c8dee2aaSAndroid Build Coastguard Worker             case POp::merge_inv_condition_mask:
3411*c8dee2aaSAndroid Build Coastguard Worker                 opText = "CondMask = " + opArg1 + " & ~" + opArg2;
3412*c8dee2aaSAndroid Build Coastguard Worker                 break;
3413*c8dee2aaSAndroid Build Coastguard Worker 
3414*c8dee2aaSAndroid Build Coastguard Worker             case POp::load_loop_mask:
3415*c8dee2aaSAndroid Build Coastguard Worker                 opText = "LoopMask = " + opArg1;
3416*c8dee2aaSAndroid Build Coastguard Worker                 break;
3417*c8dee2aaSAndroid Build Coastguard Worker 
3418*c8dee2aaSAndroid Build Coastguard Worker             case POp::store_loop_mask:
3419*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = LoopMask";
3420*c8dee2aaSAndroid Build Coastguard Worker                 break;
3421*c8dee2aaSAndroid Build Coastguard Worker 
3422*c8dee2aaSAndroid Build Coastguard Worker             case POp::mask_off_loop_mask:
3423*c8dee2aaSAndroid Build Coastguard Worker                 opText = "LoopMask &= ~(CondMask & LoopMask & RetMask)";
3424*c8dee2aaSAndroid Build Coastguard Worker                 break;
3425*c8dee2aaSAndroid Build Coastguard Worker 
3426*c8dee2aaSAndroid Build Coastguard Worker             case POp::reenable_loop_mask:
3427*c8dee2aaSAndroid Build Coastguard Worker                 opText = "LoopMask |= " + opArg1;
3428*c8dee2aaSAndroid Build Coastguard Worker                 break;
3429*c8dee2aaSAndroid Build Coastguard Worker 
3430*c8dee2aaSAndroid Build Coastguard Worker             case POp::merge_loop_mask:
3431*c8dee2aaSAndroid Build Coastguard Worker                 opText = "LoopMask &= " + opArg1;
3432*c8dee2aaSAndroid Build Coastguard Worker                 break;
3433*c8dee2aaSAndroid Build Coastguard Worker 
3434*c8dee2aaSAndroid Build Coastguard Worker             case POp::load_return_mask:
3435*c8dee2aaSAndroid Build Coastguard Worker                 opText = "RetMask = " + opArg1;
3436*c8dee2aaSAndroid Build Coastguard Worker                 break;
3437*c8dee2aaSAndroid Build Coastguard Worker 
3438*c8dee2aaSAndroid Build Coastguard Worker             case POp::store_return_mask:
3439*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = RetMask";
3440*c8dee2aaSAndroid Build Coastguard Worker                 break;
3441*c8dee2aaSAndroid Build Coastguard Worker 
3442*c8dee2aaSAndroid Build Coastguard Worker             case POp::mask_off_return_mask:
3443*c8dee2aaSAndroid Build Coastguard Worker                 opText = "RetMask &= ~(CondMask & LoopMask & RetMask)";
3444*c8dee2aaSAndroid Build Coastguard Worker                 break;
3445*c8dee2aaSAndroid Build Coastguard Worker 
3446*c8dee2aaSAndroid Build Coastguard Worker             case POp::store_src_rg:
3447*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = src.rg";
3448*c8dee2aaSAndroid Build Coastguard Worker                 break;
3449*c8dee2aaSAndroid Build Coastguard Worker 
3450*c8dee2aaSAndroid Build Coastguard Worker             case POp::exchange_src:
3451*c8dee2aaSAndroid Build Coastguard Worker                 opText = "swap(src.rgba, " + opArg1 + ")";
3452*c8dee2aaSAndroid Build Coastguard Worker                 break;
3453*c8dee2aaSAndroid Build Coastguard Worker 
3454*c8dee2aaSAndroid Build Coastguard Worker             case POp::store_src:
3455*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = src.rgba";
3456*c8dee2aaSAndroid Build Coastguard Worker                 break;
3457*c8dee2aaSAndroid Build Coastguard Worker 
3458*c8dee2aaSAndroid Build Coastguard Worker             case POp::store_dst:
3459*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = dst.rgba";
3460*c8dee2aaSAndroid Build Coastguard Worker                 break;
3461*c8dee2aaSAndroid Build Coastguard Worker 
3462*c8dee2aaSAndroid Build Coastguard Worker             case POp::store_device_xy01:
3463*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = DeviceCoords.xy01";
3464*c8dee2aaSAndroid Build Coastguard Worker                 break;
3465*c8dee2aaSAndroid Build Coastguard Worker 
3466*c8dee2aaSAndroid Build Coastguard Worker             case POp::load_src:
3467*c8dee2aaSAndroid Build Coastguard Worker                 opText = "src.rgba = " + opArg1;
3468*c8dee2aaSAndroid Build Coastguard Worker                 break;
3469*c8dee2aaSAndroid Build Coastguard Worker 
3470*c8dee2aaSAndroid Build Coastguard Worker             case POp::load_dst:
3471*c8dee2aaSAndroid Build Coastguard Worker                 opText = "dst.rgba = " + opArg1;
3472*c8dee2aaSAndroid Build Coastguard Worker                 break;
3473*c8dee2aaSAndroid Build Coastguard Worker 
3474*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_and_int:
3475*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_and_2_ints:
3476*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_and_3_ints:
3477*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_and_4_ints:
3478*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_and_n_ints:
3479*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_and_imm_int:
3480*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_and_imm_2_ints:
3481*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_and_imm_3_ints:
3482*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_and_imm_4_ints:
3483*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " &= " + opArg2;
3484*c8dee2aaSAndroid Build Coastguard Worker                 break;
3485*c8dee2aaSAndroid Build Coastguard Worker 
3486*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_or_int:
3487*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_or_2_ints:
3488*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_or_3_ints:
3489*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_or_4_ints:
3490*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_or_n_ints:
3491*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " |= " + opArg2;
3492*c8dee2aaSAndroid Build Coastguard Worker                 break;
3493*c8dee2aaSAndroid Build Coastguard Worker 
3494*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_xor_int:
3495*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_xor_2_ints:
3496*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_xor_3_ints:
3497*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_xor_4_ints:
3498*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_xor_n_ints:
3499*c8dee2aaSAndroid Build Coastguard Worker             case POp::bitwise_xor_imm_int:
3500*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " ^= " + opArg2;
3501*c8dee2aaSAndroid Build Coastguard Worker                 break;
3502*c8dee2aaSAndroid Build Coastguard Worker 
3503*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_float_from_int:
3504*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_float_from_2_ints:
3505*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_float_from_3_ints:
3506*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_float_from_4_ints:
3507*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = IntToFloat(" + opArg1 + ")";
3508*c8dee2aaSAndroid Build Coastguard Worker                 break;
3509*c8dee2aaSAndroid Build Coastguard Worker 
3510*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_float_from_uint:
3511*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_float_from_2_uints:
3512*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_float_from_3_uints:
3513*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_float_from_4_uints:
3514*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = UintToFloat(" + opArg1 + ")";
3515*c8dee2aaSAndroid Build Coastguard Worker                 break;
3516*c8dee2aaSAndroid Build Coastguard Worker 
3517*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_int_from_float:
3518*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_int_from_2_floats:
3519*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_int_from_3_floats:
3520*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_int_from_4_floats:
3521*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = FloatToInt(" + opArg1 + ")";
3522*c8dee2aaSAndroid Build Coastguard Worker                 break;
3523*c8dee2aaSAndroid Build Coastguard Worker 
3524*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_uint_from_float:
3525*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_uint_from_2_floats:
3526*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_uint_from_3_floats:
3527*c8dee2aaSAndroid Build Coastguard Worker             case POp::cast_to_uint_from_4_floats:
3528*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = FloatToUint(" + opArg1 + ")";
3529*c8dee2aaSAndroid Build Coastguard Worker                 break;
3530*c8dee2aaSAndroid Build Coastguard Worker 
3531*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_slot_masked:            case POp::copy_2_slots_masked:
3532*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_3_slots_masked:         case POp::copy_4_slots_masked:
3533*c8dee2aaSAndroid Build Coastguard Worker             case POp::swizzle_copy_slot_masked:    case POp::swizzle_copy_2_slots_masked:
3534*c8dee2aaSAndroid Build Coastguard Worker             case POp::swizzle_copy_3_slots_masked: case POp::swizzle_copy_4_slots_masked:
3535*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = Mask(" + opArg2 + ")";
3536*c8dee2aaSAndroid Build Coastguard Worker                 break;
3537*c8dee2aaSAndroid Build Coastguard Worker 
3538*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_uniform:                case POp::copy_2_uniforms:
3539*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_3_uniforms:             case POp::copy_4_uniforms:
3540*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_slot_unmasked:          case POp::copy_2_slots_unmasked:
3541*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_3_slots_unmasked:       case POp::copy_4_slots_unmasked:
3542*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_immutable_unmasked:     case POp::copy_2_immutables_unmasked:
3543*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_3_immutables_unmasked:  case POp::copy_4_immutables_unmasked:
3544*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_constant:               case POp::splat_2_constants:
3545*c8dee2aaSAndroid Build Coastguard Worker             case POp::splat_3_constants:           case POp::splat_4_constants:
3546*c8dee2aaSAndroid Build Coastguard Worker             case POp::swizzle_1:                   case POp::swizzle_2:
3547*c8dee2aaSAndroid Build Coastguard Worker             case POp::swizzle_3:                   case POp::swizzle_4:
3548*c8dee2aaSAndroid Build Coastguard Worker             case POp::shuffle:
3549*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = " + opArg2;
3550*c8dee2aaSAndroid Build Coastguard Worker                 break;
3551*c8dee2aaSAndroid Build Coastguard Worker 
3552*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_from_indirect_unmasked:
3553*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_from_indirect_uniform_unmasked:
3554*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = Indirect(" + opArg2 + " + " + opArg3 + ")";
3555*c8dee2aaSAndroid Build Coastguard Worker                 break;
3556*c8dee2aaSAndroid Build Coastguard Worker 
3557*c8dee2aaSAndroid Build Coastguard Worker             case POp::copy_to_indirect_masked:
3558*c8dee2aaSAndroid Build Coastguard Worker                 opText = "Indirect(" + opArg1 + " + " + opArg3 + ") = Mask(" + opArg2 + ")";
3559*c8dee2aaSAndroid Build Coastguard Worker                 break;
3560*c8dee2aaSAndroid Build Coastguard Worker 
3561*c8dee2aaSAndroid Build Coastguard Worker             case POp::swizzle_copy_to_indirect_masked:
3562*c8dee2aaSAndroid Build Coastguard Worker                 opText = "Indirect(" + opArg1 + " + " + opArg3 + ")." + opSwizzle + " = Mask(" +
3563*c8dee2aaSAndroid Build Coastguard Worker                          opArg2 + ")";
3564*c8dee2aaSAndroid Build Coastguard Worker                 break;
3565*c8dee2aaSAndroid Build Coastguard Worker 
3566*c8dee2aaSAndroid Build Coastguard Worker             case POp::abs_int:
3567*c8dee2aaSAndroid Build Coastguard Worker             case POp::abs_2_ints:
3568*c8dee2aaSAndroid Build Coastguard Worker             case POp::abs_3_ints:
3569*c8dee2aaSAndroid Build Coastguard Worker             case POp::abs_4_ints:
3570*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = abs(" + opArg1 + ")";
3571*c8dee2aaSAndroid Build Coastguard Worker                 break;
3572*c8dee2aaSAndroid Build Coastguard Worker 
3573*c8dee2aaSAndroid Build Coastguard Worker             case POp::acos_float:
3574*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = acos(" + opArg1 + ")";
3575*c8dee2aaSAndroid Build Coastguard Worker                 break;
3576*c8dee2aaSAndroid Build Coastguard Worker 
3577*c8dee2aaSAndroid Build Coastguard Worker             case POp::asin_float:
3578*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = asin(" + opArg1 + ")";
3579*c8dee2aaSAndroid Build Coastguard Worker                 break;
3580*c8dee2aaSAndroid Build Coastguard Worker 
3581*c8dee2aaSAndroid Build Coastguard Worker             case POp::atan_float:
3582*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = atan(" + opArg1 + ")";
3583*c8dee2aaSAndroid Build Coastguard Worker                 break;
3584*c8dee2aaSAndroid Build Coastguard Worker 
3585*c8dee2aaSAndroid Build Coastguard Worker             case POp::atan2_n_floats:
3586*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = atan2(" + opArg1 + ", " + opArg2 + ")";
3587*c8dee2aaSAndroid Build Coastguard Worker                 break;
3588*c8dee2aaSAndroid Build Coastguard Worker 
3589*c8dee2aaSAndroid Build Coastguard Worker             case POp::ceil_float:
3590*c8dee2aaSAndroid Build Coastguard Worker             case POp::ceil_2_floats:
3591*c8dee2aaSAndroid Build Coastguard Worker             case POp::ceil_3_floats:
3592*c8dee2aaSAndroid Build Coastguard Worker             case POp::ceil_4_floats:
3593*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = ceil(" + opArg1 + ")";
3594*c8dee2aaSAndroid Build Coastguard Worker                 break;
3595*c8dee2aaSAndroid Build Coastguard Worker 
3596*c8dee2aaSAndroid Build Coastguard Worker             case POp::cos_float:
3597*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = cos(" + opArg1 + ")";
3598*c8dee2aaSAndroid Build Coastguard Worker                 break;
3599*c8dee2aaSAndroid Build Coastguard Worker 
3600*c8dee2aaSAndroid Build Coastguard Worker             case POp::refract_4_floats:
3601*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = refract(" + opArg1 + ", " + opArg2 + ", " + opArg3 + ")";
3602*c8dee2aaSAndroid Build Coastguard Worker                 break;
3603*c8dee2aaSAndroid Build Coastguard Worker 
3604*c8dee2aaSAndroid Build Coastguard Worker             case POp::dot_2_floats:
3605*c8dee2aaSAndroid Build Coastguard Worker             case POp::dot_3_floats:
3606*c8dee2aaSAndroid Build Coastguard Worker             case POp::dot_4_floats:
3607*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = dot(" + opArg2 + ", " + opArg3 + ")";
3608*c8dee2aaSAndroid Build Coastguard Worker                 break;
3609*c8dee2aaSAndroid Build Coastguard Worker 
3610*c8dee2aaSAndroid Build Coastguard Worker             case POp::exp_float:
3611*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = exp(" + opArg1 + ")";
3612*c8dee2aaSAndroid Build Coastguard Worker                 break;
3613*c8dee2aaSAndroid Build Coastguard Worker 
3614*c8dee2aaSAndroid Build Coastguard Worker             case POp::exp2_float:
3615*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = exp2(" + opArg1 + ")";
3616*c8dee2aaSAndroid Build Coastguard Worker                 break;
3617*c8dee2aaSAndroid Build Coastguard Worker 
3618*c8dee2aaSAndroid Build Coastguard Worker             case POp::log_float:
3619*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = log(" + opArg1 + ")";
3620*c8dee2aaSAndroid Build Coastguard Worker                 break;
3621*c8dee2aaSAndroid Build Coastguard Worker 
3622*c8dee2aaSAndroid Build Coastguard Worker             case POp::log2_float:
3623*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = log2(" + opArg1 + ")";
3624*c8dee2aaSAndroid Build Coastguard Worker                 break;
3625*c8dee2aaSAndroid Build Coastguard Worker 
3626*c8dee2aaSAndroid Build Coastguard Worker             case POp::pow_n_floats:
3627*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = pow(" + opArg1 + ", " + opArg2 + ")";
3628*c8dee2aaSAndroid Build Coastguard Worker                 break;
3629*c8dee2aaSAndroid Build Coastguard Worker 
3630*c8dee2aaSAndroid Build Coastguard Worker             case POp::sin_float:
3631*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = sin(" + opArg1 + ")";
3632*c8dee2aaSAndroid Build Coastguard Worker                 break;
3633*c8dee2aaSAndroid Build Coastguard Worker 
3634*c8dee2aaSAndroid Build Coastguard Worker             case POp::sqrt_float:
3635*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = sqrt(" + opArg1 + ")";
3636*c8dee2aaSAndroid Build Coastguard Worker                 break;
3637*c8dee2aaSAndroid Build Coastguard Worker 
3638*c8dee2aaSAndroid Build Coastguard Worker             case POp::tan_float:
3639*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = tan(" + opArg1 + ")";
3640*c8dee2aaSAndroid Build Coastguard Worker                 break;
3641*c8dee2aaSAndroid Build Coastguard Worker 
3642*c8dee2aaSAndroid Build Coastguard Worker             case POp::floor_float:
3643*c8dee2aaSAndroid Build Coastguard Worker             case POp::floor_2_floats:
3644*c8dee2aaSAndroid Build Coastguard Worker             case POp::floor_3_floats:
3645*c8dee2aaSAndroid Build Coastguard Worker             case POp::floor_4_floats:
3646*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = floor(" + opArg1 + ")";
3647*c8dee2aaSAndroid Build Coastguard Worker                 break;
3648*c8dee2aaSAndroid Build Coastguard Worker 
3649*c8dee2aaSAndroid Build Coastguard Worker             case POp::invsqrt_float:
3650*c8dee2aaSAndroid Build Coastguard Worker             case POp::invsqrt_2_floats:
3651*c8dee2aaSAndroid Build Coastguard Worker             case POp::invsqrt_3_floats:
3652*c8dee2aaSAndroid Build Coastguard Worker             case POp::invsqrt_4_floats:
3653*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = inversesqrt(" + opArg1 + ")";
3654*c8dee2aaSAndroid Build Coastguard Worker                 break;
3655*c8dee2aaSAndroid Build Coastguard Worker 
3656*c8dee2aaSAndroid Build Coastguard Worker             case POp::inverse_mat2:
3657*c8dee2aaSAndroid Build Coastguard Worker             case POp::inverse_mat3:
3658*c8dee2aaSAndroid Build Coastguard Worker             case POp::inverse_mat4:
3659*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = inverse(" + opArg1 + ")";
3660*c8dee2aaSAndroid Build Coastguard Worker                 break;
3661*c8dee2aaSAndroid Build Coastguard Worker 
3662*c8dee2aaSAndroid Build Coastguard Worker             case POp::add_float:     case POp::add_int:
3663*c8dee2aaSAndroid Build Coastguard Worker             case POp::add_2_floats:  case POp::add_2_ints:
3664*c8dee2aaSAndroid Build Coastguard Worker             case POp::add_3_floats:  case POp::add_3_ints:
3665*c8dee2aaSAndroid Build Coastguard Worker             case POp::add_4_floats:  case POp::add_4_ints:
3666*c8dee2aaSAndroid Build Coastguard Worker             case POp::add_n_floats:  case POp::add_n_ints:
3667*c8dee2aaSAndroid Build Coastguard Worker             case POp::add_imm_float: case POp::add_imm_int:
3668*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " += " + opArg2;
3669*c8dee2aaSAndroid Build Coastguard Worker                 break;
3670*c8dee2aaSAndroid Build Coastguard Worker 
3671*c8dee2aaSAndroid Build Coastguard Worker             case POp::sub_float:    case POp::sub_int:
3672*c8dee2aaSAndroid Build Coastguard Worker             case POp::sub_2_floats: case POp::sub_2_ints:
3673*c8dee2aaSAndroid Build Coastguard Worker             case POp::sub_3_floats: case POp::sub_3_ints:
3674*c8dee2aaSAndroid Build Coastguard Worker             case POp::sub_4_floats: case POp::sub_4_ints:
3675*c8dee2aaSAndroid Build Coastguard Worker             case POp::sub_n_floats: case POp::sub_n_ints:
3676*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " -= " + opArg2;
3677*c8dee2aaSAndroid Build Coastguard Worker                 break;
3678*c8dee2aaSAndroid Build Coastguard Worker 
3679*c8dee2aaSAndroid Build Coastguard Worker             case POp::mul_float:     case POp::mul_int:
3680*c8dee2aaSAndroid Build Coastguard Worker             case POp::mul_2_floats:  case POp::mul_2_ints:
3681*c8dee2aaSAndroid Build Coastguard Worker             case POp::mul_3_floats:  case POp::mul_3_ints:
3682*c8dee2aaSAndroid Build Coastguard Worker             case POp::mul_4_floats:  case POp::mul_4_ints:
3683*c8dee2aaSAndroid Build Coastguard Worker             case POp::mul_n_floats:  case POp::mul_n_ints:
3684*c8dee2aaSAndroid Build Coastguard Worker             case POp::mul_imm_float: case POp::mul_imm_int:
3685*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " *= " + opArg2;
3686*c8dee2aaSAndroid Build Coastguard Worker                 break;
3687*c8dee2aaSAndroid Build Coastguard Worker 
3688*c8dee2aaSAndroid Build Coastguard Worker             case POp::div_float:    case POp::div_int:    case POp::div_uint:
3689*c8dee2aaSAndroid Build Coastguard Worker             case POp::div_2_floats: case POp::div_2_ints: case POp::div_2_uints:
3690*c8dee2aaSAndroid Build Coastguard Worker             case POp::div_3_floats: case POp::div_3_ints: case POp::div_3_uints:
3691*c8dee2aaSAndroid Build Coastguard Worker             case POp::div_4_floats: case POp::div_4_ints: case POp::div_4_uints:
3692*c8dee2aaSAndroid Build Coastguard Worker             case POp::div_n_floats: case POp::div_n_ints: case POp::div_n_uints:
3693*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " /= " + opArg2;
3694*c8dee2aaSAndroid Build Coastguard Worker                 break;
3695*c8dee2aaSAndroid Build Coastguard Worker 
3696*c8dee2aaSAndroid Build Coastguard Worker             case POp::matrix_multiply_2:
3697*c8dee2aaSAndroid Build Coastguard Worker             case POp::matrix_multiply_3:
3698*c8dee2aaSAndroid Build Coastguard Worker             case POp::matrix_multiply_4:
3699*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = " + opArg2 + " * " + opArg3;
3700*c8dee2aaSAndroid Build Coastguard Worker                 break;
3701*c8dee2aaSAndroid Build Coastguard Worker 
3702*c8dee2aaSAndroid Build Coastguard Worker             case POp::mod_float:
3703*c8dee2aaSAndroid Build Coastguard Worker             case POp::mod_2_floats:
3704*c8dee2aaSAndroid Build Coastguard Worker             case POp::mod_3_floats:
3705*c8dee2aaSAndroid Build Coastguard Worker             case POp::mod_4_floats:
3706*c8dee2aaSAndroid Build Coastguard Worker             case POp::mod_n_floats:
3707*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = mod(" + opArg1 + ", " + opArg2 + ")";
3708*c8dee2aaSAndroid Build Coastguard Worker                 break;
3709*c8dee2aaSAndroid Build Coastguard Worker 
3710*c8dee2aaSAndroid Build Coastguard Worker             case POp::min_float:        case POp::min_int:          case POp::min_uint:
3711*c8dee2aaSAndroid Build Coastguard Worker             case POp::min_2_floats:     case POp::min_2_ints:       case POp::min_2_uints:
3712*c8dee2aaSAndroid Build Coastguard Worker             case POp::min_3_floats:     case POp::min_3_ints:       case POp::min_3_uints:
3713*c8dee2aaSAndroid Build Coastguard Worker             case POp::min_4_floats:     case POp::min_4_ints:       case POp::min_4_uints:
3714*c8dee2aaSAndroid Build Coastguard Worker             case POp::min_n_floats:     case POp::min_n_ints:       case POp::min_n_uints:
3715*c8dee2aaSAndroid Build Coastguard Worker             case POp::min_imm_float:
3716*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = min(" + opArg1 + ", " + opArg2 + ")";
3717*c8dee2aaSAndroid Build Coastguard Worker                 break;
3718*c8dee2aaSAndroid Build Coastguard Worker 
3719*c8dee2aaSAndroid Build Coastguard Worker             case POp::max_float:        case POp::max_int:          case POp::max_uint:
3720*c8dee2aaSAndroid Build Coastguard Worker             case POp::max_2_floats:     case POp::max_2_ints:       case POp::max_2_uints:
3721*c8dee2aaSAndroid Build Coastguard Worker             case POp::max_3_floats:     case POp::max_3_ints:       case POp::max_3_uints:
3722*c8dee2aaSAndroid Build Coastguard Worker             case POp::max_4_floats:     case POp::max_4_ints:       case POp::max_4_uints:
3723*c8dee2aaSAndroid Build Coastguard Worker             case POp::max_n_floats:     case POp::max_n_ints:       case POp::max_n_uints:
3724*c8dee2aaSAndroid Build Coastguard Worker             case POp::max_imm_float:
3725*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = max(" + opArg1 + ", " + opArg2 + ")";
3726*c8dee2aaSAndroid Build Coastguard Worker                 break;
3727*c8dee2aaSAndroid Build Coastguard Worker 
3728*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmplt_float:     case POp::cmplt_int:     case POp::cmplt_uint:
3729*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmplt_2_floats:  case POp::cmplt_2_ints:  case POp::cmplt_2_uints:
3730*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmplt_3_floats:  case POp::cmplt_3_ints:  case POp::cmplt_3_uints:
3731*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmplt_4_floats:  case POp::cmplt_4_ints:  case POp::cmplt_4_uints:
3732*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmplt_n_floats:  case POp::cmplt_n_ints:  case POp::cmplt_n_uints:
3733*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmplt_imm_float: case POp::cmplt_imm_int: case POp::cmplt_imm_uint:
3734*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = lessThan(" + opArg1 + ", " + opArg2 + ")";
3735*c8dee2aaSAndroid Build Coastguard Worker                 break;
3736*c8dee2aaSAndroid Build Coastguard Worker 
3737*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmple_float:     case POp::cmple_int:     case POp::cmple_uint:
3738*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmple_2_floats:  case POp::cmple_2_ints:  case POp::cmple_2_uints:
3739*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmple_3_floats:  case POp::cmple_3_ints:  case POp::cmple_3_uints:
3740*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmple_4_floats:  case POp::cmple_4_ints:  case POp::cmple_4_uints:
3741*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmple_n_floats:  case POp::cmple_n_ints:  case POp::cmple_n_uints:
3742*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmple_imm_float: case POp::cmple_imm_int: case POp::cmple_imm_uint:
3743*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = lessThanEqual(" + opArg1 + ", " + opArg2 + ")";
3744*c8dee2aaSAndroid Build Coastguard Worker                 break;
3745*c8dee2aaSAndroid Build Coastguard Worker 
3746*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpeq_float:     case POp::cmpeq_int:
3747*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpeq_2_floats:  case POp::cmpeq_2_ints:
3748*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpeq_3_floats:  case POp::cmpeq_3_ints:
3749*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpeq_4_floats:  case POp::cmpeq_4_ints:
3750*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpeq_n_floats:  case POp::cmpeq_n_ints:
3751*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpeq_imm_float: case POp::cmpeq_imm_int:
3752*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = equal(" + opArg1 + ", " + opArg2 + ")";
3753*c8dee2aaSAndroid Build Coastguard Worker                 break;
3754*c8dee2aaSAndroid Build Coastguard Worker 
3755*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpne_float:     case POp::cmpne_int:
3756*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpne_2_floats:  case POp::cmpne_2_ints:
3757*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpne_3_floats:  case POp::cmpne_3_ints:
3758*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpne_4_floats:  case POp::cmpne_4_ints:
3759*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpne_n_floats:  case POp::cmpne_n_ints:
3760*c8dee2aaSAndroid Build Coastguard Worker             case POp::cmpne_imm_float: case POp::cmpne_imm_int:
3761*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = notEqual(" + opArg1 + ", " + opArg2 + ")";
3762*c8dee2aaSAndroid Build Coastguard Worker                 break;
3763*c8dee2aaSAndroid Build Coastguard Worker 
3764*c8dee2aaSAndroid Build Coastguard Worker             case POp::mix_float:      case POp::mix_int:
3765*c8dee2aaSAndroid Build Coastguard Worker             case POp::mix_2_floats:   case POp::mix_2_ints:
3766*c8dee2aaSAndroid Build Coastguard Worker             case POp::mix_3_floats:   case POp::mix_3_ints:
3767*c8dee2aaSAndroid Build Coastguard Worker             case POp::mix_4_floats:   case POp::mix_4_ints:
3768*c8dee2aaSAndroid Build Coastguard Worker             case POp::mix_n_floats:   case POp::mix_n_ints:
3769*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = mix(" + opArg2 + ", " + opArg3 + ", " + opArg1 + ")";
3770*c8dee2aaSAndroid Build Coastguard Worker                 break;
3771*c8dee2aaSAndroid Build Coastguard Worker 
3772*c8dee2aaSAndroid Build Coastguard Worker             case POp::smoothstep_n_floats:
3773*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = smoothstep(" + opArg1 + ", " + opArg2 + ", " + opArg3 + ")";
3774*c8dee2aaSAndroid Build Coastguard Worker                 break;
3775*c8dee2aaSAndroid Build Coastguard Worker 
3776*c8dee2aaSAndroid Build Coastguard Worker             case POp::jump:
3777*c8dee2aaSAndroid Build Coastguard Worker             case POp::branch_if_all_lanes_active:
3778*c8dee2aaSAndroid Build Coastguard Worker             case POp::branch_if_any_lanes_active:
3779*c8dee2aaSAndroid Build Coastguard Worker             case POp::branch_if_no_lanes_active:
3780*c8dee2aaSAndroid Build Coastguard Worker             case POp::invoke_shader:
3781*c8dee2aaSAndroid Build Coastguard Worker             case POp::invoke_color_filter:
3782*c8dee2aaSAndroid Build Coastguard Worker             case POp::invoke_blender:
3783*c8dee2aaSAndroid Build Coastguard Worker                 opText = std::string(opName) + " " + opArg1;
3784*c8dee2aaSAndroid Build Coastguard Worker                 break;
3785*c8dee2aaSAndroid Build Coastguard Worker 
3786*c8dee2aaSAndroid Build Coastguard Worker             case POp::invoke_to_linear_srgb:
3787*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = toLinearSrgb(" + opArg1 + ")";
3788*c8dee2aaSAndroid Build Coastguard Worker                 break;
3789*c8dee2aaSAndroid Build Coastguard Worker 
3790*c8dee2aaSAndroid Build Coastguard Worker             case POp::invoke_from_linear_srgb:
3791*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 + " = fromLinearSrgb(" + opArg1 + ")";
3792*c8dee2aaSAndroid Build Coastguard Worker                 break;
3793*c8dee2aaSAndroid Build Coastguard Worker 
3794*c8dee2aaSAndroid Build Coastguard Worker             case POp::branch_if_no_active_lanes_eq:
3795*c8dee2aaSAndroid Build Coastguard Worker                 opText = "branch " + opArg1 + " if no lanes of " + opArg2 + " == " + opArg3;
3796*c8dee2aaSAndroid Build Coastguard Worker                 break;
3797*c8dee2aaSAndroid Build Coastguard Worker 
3798*c8dee2aaSAndroid Build Coastguard Worker             case POp::label:
3799*c8dee2aaSAndroid Build Coastguard Worker                 opText = "label " + opArg1;
3800*c8dee2aaSAndroid Build Coastguard Worker                 break;
3801*c8dee2aaSAndroid Build Coastguard Worker 
3802*c8dee2aaSAndroid Build Coastguard Worker             case POp::case_op:
3803*c8dee2aaSAndroid Build Coastguard Worker                 opText = "if (" + opArg1 + " == " + opArg3 +
3804*c8dee2aaSAndroid Build Coastguard Worker                          ") { LoopMask = true; " + opArg2 + " = false; }";
3805*c8dee2aaSAndroid Build Coastguard Worker                 break;
3806*c8dee2aaSAndroid Build Coastguard Worker 
3807*c8dee2aaSAndroid Build Coastguard Worker             case POp::continue_op:
3808*c8dee2aaSAndroid Build Coastguard Worker                 opText = opArg1 +
3809*c8dee2aaSAndroid Build Coastguard Worker                          " |= Mask(0xFFFFFFFF); LoopMask &= ~(CondMask & LoopMask & RetMask)";
3810*c8dee2aaSAndroid Build Coastguard Worker                 break;
3811*c8dee2aaSAndroid Build Coastguard Worker 
3812*c8dee2aaSAndroid Build Coastguard Worker             default:
3813*c8dee2aaSAndroid Build Coastguard Worker                 break;
3814*c8dee2aaSAndroid Build Coastguard Worker         }
3815*c8dee2aaSAndroid Build Coastguard Worker 
3816*c8dee2aaSAndroid Build Coastguard Worker         opName = opName.substr(0, 30);
3817*c8dee2aaSAndroid Build Coastguard Worker         if (!opText.empty()) {
3818*c8dee2aaSAndroid Build Coastguard Worker             out->writeText(SkSL::String::printf("%-30.*s %s\n",
3819*c8dee2aaSAndroid Build Coastguard Worker                                                 (int)opName.size(), opName.data(),
3820*c8dee2aaSAndroid Build Coastguard Worker                                                 opText.c_str()).c_str());
3821*c8dee2aaSAndroid Build Coastguard Worker         } else {
3822*c8dee2aaSAndroid Build Coastguard Worker             out->writeText(SkSL::String::printf("%.*s\n",
3823*c8dee2aaSAndroid Build Coastguard Worker                                                 (int)opName.size(), opName.data()).c_str());
3824*c8dee2aaSAndroid Build Coastguard Worker         }
3825*c8dee2aaSAndroid Build Coastguard Worker     }
3826*c8dee2aaSAndroid Build Coastguard Worker }
3827*c8dee2aaSAndroid Build Coastguard Worker 
dump(SkWStream * out,bool writeInstructionCount) const3828*c8dee2aaSAndroid Build Coastguard Worker void Program::dump(SkWStream* out, bool writeInstructionCount) const {
3829*c8dee2aaSAndroid Build Coastguard Worker     Dumper(*this).dump(out, writeInstructionCount);
3830*c8dee2aaSAndroid Build Coastguard Worker }
3831*c8dee2aaSAndroid Build Coastguard Worker 
3832*c8dee2aaSAndroid Build Coastguard Worker }  // namespace SkSL::RP
3833